Add usage totals

This commit is contained in:
Webifi 2023-05-28 11:57:07 -05:00
parent b1acf6806a
commit 5d7b51ba1d
5 changed files with 80 additions and 37 deletions

View File

@ -17,7 +17,10 @@
updateChatSettings, updateChatSettings,
resetChatSettings, resetChatSettings,
setChatSettingValue, setChatSettingValue,
addChatFromJSON addChatFromJSON,
updateRunningTotal
} from './Storage.svelte' } from './Storage.svelte'
import { getChatSettingObjectByKey, getChatSettingList, getRequestSettingList } from './Settings.svelte' import { getChatSettingObjectByKey, getChatSettingList, getRequestSettingList } from './Settings.svelte'
import { import {
@ -59,6 +62,7 @@
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { exportChatAsJSON, exportProfileAsJSON } from './Export.svelte' import { exportChatAsJSON, exportProfileAsJSON } from './Export.svelte'
import { clickOutside } from 'svelte-use-click-outside' import { clickOutside } from 'svelte-use-click-outside'
import { getPrice } from './Stats.svelte';
// This makes it possible to override the OpenAI API base URL in the .env file // This makes it possible to override the OpenAI API base URL in the .env file
const apiBase = import.meta.env.VITE_API_BASE || 'https://api.openai.com' const apiBase = import.meta.env.VITE_API_BASE || 'https://api.openai.com'
@ -86,9 +90,10 @@
$: chatSettings = chat.settings $: chatSettings = chat.settings
$: globalStore = $globalStorage $: globalStore = $globalStorage
// Make sure chat object is ready to go
updateChatSettings(chatId)
onMount(async () => { onMount(async () => {
// Make sure chat object is ready to go
updateChatSettings(chatId)
// Focus the input on mount // Focus the input on mount
focusInput() focusInput()
@ -147,7 +152,6 @@
const sendRequest = async (messages: Message[], doingSummary?:boolean, withSummary?:boolean): Promise<Response> => { const sendRequest = async (messages: Message[], doingSummary?:boolean, withSummary?:boolean): Promise<Response> => {
// Show updating bar // Show updating bar
updating = true updating = true
updatingMessage = ''
let response: Response let response: Response
@ -207,6 +211,7 @@
content: summaryPrompt content: summaryPrompt
} as Message) } as Message)
// Wait for the summary completion // Wait for the summary completion
updatingMessage = 'Building Summary...'
const summary = await sendRequest(summarizeReq, true) const summary = await sendRequest(summarizeReq, true)
if (summary.error) { if (summary.error) {
// Failed to some API issue. let the original caller handle it. // Failed to some API issue. let the original caller handle it.
@ -241,6 +246,7 @@
saveChatStore() saveChatStore()
// Re-run request with summarized prompts // Re-run request with summarized prompts
// return { error: { message: "End for now" } } as Response // return { error: { message: "End for now" } } as Response
updatingMessage = 'Continuing...'
return await sendRequest(chat.messages, false, true) return await sendRequest(chat.messages, false, true)
} }
} else if (!summaryPrompt) { } else if (!summaryPrompt) {
@ -313,10 +319,7 @@
updatingMessage = '' updatingMessage = ''
if (!response.error) { if (!response.error) {
// tc.completions++ updateRunningTotal(chatId, response.usage, response.model)
// tc.completionsTokens += response.usage.completion_tokens
// chat.totals.push(tc)
// console.log('got response:', response)
} }
return response return response
@ -777,7 +780,15 @@
<button title="Send" class="button is-info" type="submit"><Fa icon={faPaperPlane} /></button> <button title="Send" class="button is-info" type="submit"><Fa icon={faPaperPlane} /></button>
</p> </p>
</form> </form>
<div class="chat-focus-point" style="height.4em"></div> <!-- a target to scroll to -->
<div class="chat-focus-point running-total-container">
{#each Object.entries(chat.usage||{}) as [model, usage]}
<p class="is-size-7 running-totals">
<em>{model}</em> total <span class="has-text-weight-bold">{usage.total_tokens}</span>
tokens ~= <span class="has-text-weight-bold">${getPrice(usage, model).toFixed(6)}</span>
</p>
{/each}
</div>
<svelte:window <svelte:window
on:keydown={(event) => { on:keydown={(event) => {
@ -959,3 +970,15 @@
</div> </div>
</form> </form>
<!-- end --> <!-- end -->
<style>
.running-total-container {
min-height:2em;
padding-bottom:.6em;
padding-left: 1.9em;
margin-bottom:-2.6em
}
.running-totals {
opacity: 0.5;
}
</style>

View File

@ -82,6 +82,7 @@
console.error("Can't find element with message ID", uuid) console.error("Can't find element with message ID", uuid)
} }
} }
</script> </script>
{#key message.uuid} {#key message.uuid}
@ -137,19 +138,22 @@
<div id={'edit-' + message.uuid} class="message-editor" bind:innerText={message.content} contenteditable on:input={update} on:blur={exit} /> <div id={'edit-' + message.uuid} class="message-editor" bind:innerText={message.content} contenteditable on:input={update} on:blur={exit} />
</form> </form>
{:else} {:else}
<a href={'#'} class="message-display" on:click|preventDefault={() => {}} on:dblclick|preventDefault={() => edit('edit-' + message.uuid)}> <div
class="message-display"
on:dblclick|preventDefault={() => edit('edit-' + message.uuid)}
>
<SvelteMarkdown <SvelteMarkdown
source={message.content} source={message.content}
options={markedownOptions} options={markedownOptions}
renderers={{ code: Code, html: Code }} renderers={{ code: Code, html: Code }}
/> />
</a> </div>
{/if} {/if}
{#if message.role === 'system'} {#if message.role === 'system'}
<p class="is-size-7">System Prompt</p> <p class="is-size-7 message-note">System Prompt</p>
{:else if message.usage} {:else if message.usage}
<p class="is-size-7"> <p class="is-size-7 message-note">
This message was generated on <em>{message.model || defaultModel}</em> using <span class="has-text-weight-bold">{message.usage.total_tokens}</span> <em>{message.model || defaultModel}</em> using <span class="has-text-weight-bold">{message.usage.total_tokens}</span>
tokens ~= <span class="has-text-weight-bold">${getPrice(message.usage, message.model || defaultModel).toFixed(6)}</span> tokens ~= <span class="has-text-weight-bold">${getPrice(message.usage, message.model || defaultModel).toFixed(6)}</span>
</p> </p>
{/if} {/if}
@ -158,6 +162,11 @@
{/key} {/key}
<style> <style>
.message-note {
padding-top: .6em;
margin-bottom: -0.6em;
opacity: 0.5;
}
.message-edit { .message-edit {
display: block; display: block;
} }

View File

@ -29,21 +29,4 @@
return ((tokens.prompt_tokens * t[0]) + (tokens.completion_tokens * t[1])) return ((tokens.prompt_tokens * t[0]) + (tokens.completion_tokens * t[1]))
} }
export const totalUse = (totals: Usage[]): Usage => {
const r = {
completion_tokens: 0,
prompt_tokens: 0,
total_tokens: 0,
total: 0
} as Usage
(totals || ([] as Usage[])).forEach((t) => {
r.total += getPrice(t, t.model as any)
r.completion_tokens += t.completion_tokens
r.prompt_tokens += t.prompt_tokens
r.total_tokens += t.prompt_tokens
})
return r
}
</script> </script>

View File

@ -1,7 +1,7 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
import { persisted } from 'svelte-local-storage-store' import { persisted } from 'svelte-local-storage-store'
import { get } from 'svelte/store' import { get } from 'svelte/store'
import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting } from './Types.svelte' import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting, Usage, Model } from './Types.svelte'
import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte' import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { applyProfile, getProfile, isStaticProfile } from './Profiles.svelte' import { applyProfile, getProfile, isStaticProfile } from './Profiles.svelte'
@ -29,7 +29,8 @@
id: chatId, id: chatId,
name: `Chat ${chatId}`, name: `Chat ${chatId}`,
settings: {} as ChatSettings, settings: {} as ChatSettings,
messages: [] messages: [],
usage:{} as Record<Model,Usage>,
}) })
chatsStorage.set(chats) chatsStorage.set(chats)
// Apply defaults and prepare it to start // Apply defaults and prepare it to start
@ -60,10 +61,12 @@
// Add a new chat // Add a new chat
chats.push(chat) chats.push(chat)
chatsStorage.set(chats) chatsStorage.set(chats)
// make sure it's up-to-date
updateChatSettings(chatId)
return chatId return chatId
} }
// Make sure all chat settings are set with current values or defaults // Make sure a chat's settings are set with current values or defaults
export const updateChatSettings = (chatId) => { export const updateChatSettings = (chatId) => {
const chats = get(chatsStorage) const chats = get(chatsStorage)
const chat = chats.find((chat) => chat.id === chatId) as Chat const chat = chats.find((chat) => chat.id === chatId) as Chat
@ -78,6 +81,15 @@
chat.messages.forEach((m) => { chat.messages.forEach((m) => {
m.uuid = m.uuid || uuidv4() m.uuid = m.uuid || uuidv4()
}) })
// Make sure the usage totals object is set
// (some earlier versions of this had different structures)
const hasUsage = chat.usage && !Array.isArray(chat.usage)
&& typeof chat.usage === 'object'
&& Object.values(chat.usage).find(v=>'prompt_tokens' in v)
if (!hasUsage) {
const usageMap:Record<Model,Usage> = {}
chat.usage = usageMap
}
chatsStorage.set(chats) chatsStorage.set(chats)
} }
@ -118,6 +130,24 @@
return (chats.find((chat) => chat.id === chatId) as Chat).settings return (chats.find((chat) => chat.id === chatId) as Chat).settings
} }
export const updateRunningTotal = (chatId: number, usage: Usage, model:Model) => {
const chats = get(chatsStorage)
const chat = chats.find((chat) => chat.id === chatId) as Chat
let total:Usage = chat.usage[model]
if (!total) {
total = {
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0,
}
chat.usage[model] = total
}
total.completion_tokens += usage.completion_tokens
total.prompt_tokens += usage.prompt_tokens
total.total_tokens += usage.total_tokens
chatsStorage.set(chats)
}
export const addMessage = (chatId: number, message: Message) => { export const addMessage = (chatId: number, message: Message) => {
const chats = get(chatsStorage) const chats = get(chatsStorage)
const chat = chats.find((chat) => chat.id === chatId) as Chat const chat = chats.find((chat) => chat.id === chatId) as Chat

View File

@ -15,8 +15,6 @@
completion_tokens: number; completion_tokens: number;
prompt_tokens: number; prompt_tokens: number;
total_tokens: number; total_tokens: number;
total: number;
model?: Model;
}; };
export type Message = { export type Message = {
@ -69,7 +67,7 @@
id: number; id: number;
name: string; name: string;
messages: Message[]; messages: Message[];
usage?: Usage[]; usage: Record<Model, Usage>;
settings: ChatSettings; settings: ChatSettings;
}; };