Fix issues with token counts
This commit is contained in:
		
							parent
							
								
									0cc30a28dc
								
							
						
					
					
						commit
						567845be06
					
				| 
						 | 
					@ -22,8 +22,9 @@ html {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  --chatContentPaddingTop: 20px;
 | 
					  --chatContentPaddingTop: 20px;
 | 
				
			||||||
  --chatContentPaddingRight: 40px;
 | 
					  --chatContentPaddingRight: 40px;
 | 
				
			||||||
  --chatContentPaddingBottom: 130px;
 | 
					  --chatContentPaddingBottom: 110px;
 | 
				
			||||||
  --chatContentPaddingLeft: 40px;
 | 
					  --chatContentPaddingLeft: 40px;
 | 
				
			||||||
 | 
					  --runningTotalLineHeight: 28px;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  --chatInputPaddingTop: 0px;
 | 
					  --chatInputPaddingTop: 0px;
 | 
				
			||||||
  --chatInputPaddingRight: 60px;
 | 
					  --chatInputPaddingRight: 60px;
 | 
				
			||||||
| 
						 | 
					@ -31,6 +32,7 @@ html {
 | 
				
			||||||
  --chatInputPaddingLeft: 60px;
 | 
					  --chatInputPaddingLeft: 60px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  --BgColor: var(-BgColorLight);
 | 
					  --BgColor: var(-BgColorLight);
 | 
				
			||||||
 | 
					  --running-totals: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.navbar {
 | 
					.navbar {
 | 
				
			||||||
| 
						 | 
					@ -371,7 +373,7 @@ aside.menu.main-menu .menu-expanse {
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
  position: fixed;
 | 
					  position: fixed;
 | 
				
			||||||
  bottom: 0px;
 | 
					  bottom: 0px;
 | 
				
			||||||
  height: var(--chatContentPaddingBottom) ;
 | 
					  height: calc(var(--chatContentPaddingBottom) + var(--runningTotalLineHeight) * var(--running-totals));
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  background-image: linear-gradient(180deg,hsla(0,0%,100%,0) 13.94%, var(--BgColorLight) 54.73%);
 | 
					  background-image: linear-gradient(180deg,hsla(0,0%,100%,0) 13.94%, var(--BgColorLight) 54.73%);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -419,7 +421,7 @@ aside.menu.main-menu .menu-expanse {
 | 
				
			||||||
  padding: 
 | 
					  padding: 
 | 
				
			||||||
    var(--chatContentPaddingTop) 
 | 
					    var(--chatContentPaddingTop) 
 | 
				
			||||||
    var(--chatContentPaddingRight)
 | 
					    var(--chatContentPaddingRight)
 | 
				
			||||||
    var(--chatContentPaddingBottom)
 | 
					    calc(var(--chatContentPaddingBottom) + var(--runningTotalLineHeight) * var(--running-totals))
 | 
				
			||||||
    var(--chatContentPaddingLeft) ;
 | 
					    var(--chatContentPaddingLeft) ;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -370,7 +370,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      request.stream = opts.streaming
 | 
					      request.stream = opts.streaming
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      chatResponse.setPromptTokenCount(promptTokenCount)
 | 
					      chatResponse.setPromptTokenCount(promptTokenCount) // streaming needs this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const signal = controller.signal
 | 
					      const signal = controller.signal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -589,7 +589,7 @@
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
{#if chat}
 | 
					{#if chat}
 | 
				
			||||||
<ChatSettingsModal chatId={chatId} bind:show={showSettingsModal} />
 | 
					<ChatSettingsModal chatId={chatId} bind:show={showSettingsModal} />
 | 
				
			||||||
 | 
					<div class="chat-page" style="--running-totals: {Object.entries(chat.usage || {}).length}">
 | 
				
			||||||
<div class="chat-content">
 | 
					<div class="chat-content">
 | 
				
			||||||
<nav class="level chat-header">
 | 
					<nav class="level chat-header">
 | 
				
			||||||
  <div class="level-left">
 | 
					  <div class="level-left">
 | 
				
			||||||
| 
						 | 
					@ -676,4 +676,5 @@
 | 
				
			||||||
    {/each}
 | 
					    {/each}
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</Footer>
 | 
					</Footer>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
<script context="module" lang="ts">
 | 
					<script context="module" lang="ts">
 | 
				
			||||||
// TODO: Integrate API calls
 | 
					// TODO: Integrate API calls
 | 
				
			||||||
import { addMessage, saveChatStore, subtractRunningTotal, updateRunningTotal } from './Storage.svelte'
 | 
					import { addMessage, getLatestKnownModel, saveChatStore, setLatestKnownModel, subtractRunningTotal, updateRunningTotal } from './Storage.svelte'
 | 
				
			||||||
import type { Chat, ChatCompletionOpts, Message, Response, Usage } from './Types.svelte'
 | 
					import type { Chat, ChatCompletionOpts, Message, Model, Response, Usage } from './Types.svelte'
 | 
				
			||||||
import { encode } from 'gpt-tokenizer'
 | 
					import { encode } from 'gpt-tokenizer'
 | 
				
			||||||
import { v4 as uuidv4 } from 'uuid'
 | 
					import { v4 as uuidv4 } from 'uuid'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ export class ChatCompletionResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private offsetTotals: Usage
 | 
					  private offsetTotals: Usage
 | 
				
			||||||
  private isFill: boolean = false
 | 
					  private isFill: boolean = false
 | 
				
			||||||
 | 
					  private didFill: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private opts: ChatCompletionOpts
 | 
					  private opts: ChatCompletionOpts
 | 
				
			||||||
  private chat: Chat
 | 
					  private chat: Chat
 | 
				
			||||||
| 
						 | 
					@ -28,6 +29,16 @@ export class ChatCompletionResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private error: string
 | 
					  private error: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private model: Model
 | 
				
			||||||
 | 
					  private lastModel: Model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private setModel = (model: Model) => {
 | 
				
			||||||
 | 
					    if (!model) return
 | 
				
			||||||
 | 
					    !this.model && setLatestKnownModel(this.chat.settings.model as Model, model)
 | 
				
			||||||
 | 
					    this.lastModel = this.model || model
 | 
				
			||||||
 | 
					    this.model = model
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private finishResolver: (value: Message[]) => void
 | 
					  private finishResolver: (value: Message[]) => void
 | 
				
			||||||
  private errorResolver: (error: string) => void
 | 
					  private errorResolver: (error: string) => void
 | 
				
			||||||
  private finishPromise = new Promise<Message[]>((resolve, reject) => {
 | 
					  private finishPromise = new Promise<Message[]>((resolve, reject) => {
 | 
				
			||||||
| 
						 | 
					@ -49,11 +60,11 @@ export class ChatCompletionResponse {
 | 
				
			||||||
      const exitingMessage = this.messages[i]
 | 
					      const exitingMessage = this.messages[i]
 | 
				
			||||||
      const message = exitingMessage || choice.message
 | 
					      const message = exitingMessage || choice.message
 | 
				
			||||||
      if (exitingMessage) {
 | 
					      if (exitingMessage) {
 | 
				
			||||||
        if (this.isFill && choice.message.content.match(/^'(t|ll|ve|m|d|re)[^a-z]/i)) {
 | 
					        if (!this.didFill && this.isFill && choice.message.content.match(/^'(t|ll|ve|m|d|re)[^a-z]/i)) {
 | 
				
			||||||
          // deal with merging contractions since we've added an extra space to your fill message
 | 
					          // deal with merging contractions since we've added an extra space to your fill message
 | 
				
			||||||
          message.content.replace(/ $/, '')
 | 
					          message.content.replace(/ $/, '')
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.isFill = false
 | 
					        this.didFill = true
 | 
				
			||||||
        message.content += choice.message.content
 | 
					        message.content += choice.message.content
 | 
				
			||||||
        message.usage = message.usage || {
 | 
					        message.usage = message.usage || {
 | 
				
			||||||
          prompt_tokens: 0,
 | 
					          prompt_tokens: 0,
 | 
				
			||||||
| 
						 | 
					@ -61,8 +72,8 @@ export class ChatCompletionResponse {
 | 
				
			||||||
          total_tokens: 0
 | 
					          total_tokens: 0
 | 
				
			||||||
        } as Usage
 | 
					        } as Usage
 | 
				
			||||||
        message.usage.completion_tokens += response.usage.completion_tokens
 | 
					        message.usage.completion_tokens += response.usage.completion_tokens
 | 
				
			||||||
        message.usage.prompt_tokens += response.usage.prompt_tokens
 | 
					        message.usage.prompt_tokens = response.usage.prompt_tokens + (this.offsetTotals?.prompt_tokens || 0)
 | 
				
			||||||
        message.usage.total_tokens += response.usage.total_tokens
 | 
					        message.usage.total_tokens += response.usage.total_tokens + (this.offsetTotals?.total_tokens || 0)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        message.content = choice.message.content
 | 
					        message.content = choice.message.content
 | 
				
			||||||
        message.usage = response.usage
 | 
					        message.usage = response.usage
 | 
				
			||||||
| 
						 | 
					@ -70,6 +81,7 @@ export class ChatCompletionResponse {
 | 
				
			||||||
      message.finish_reason = choice.finish_reason
 | 
					      message.finish_reason = choice.finish_reason
 | 
				
			||||||
      message.role = choice.message.role
 | 
					      message.role = choice.message.role
 | 
				
			||||||
      message.model = response.model
 | 
					      message.model = response.model
 | 
				
			||||||
 | 
					      this.setModel(response.model)
 | 
				
			||||||
      this.messages[i] = message
 | 
					      this.messages[i] = message
 | 
				
			||||||
      if (this.opts.autoAddMessages) addMessage(this.chat.id, message)
 | 
					      if (this.opts.autoAddMessages) addMessage(this.chat.id, message)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
| 
						 | 
					@ -87,30 +99,30 @@ export class ChatCompletionResponse {
 | 
				
			||||||
      } as Message
 | 
					      } as Message
 | 
				
			||||||
      choice.delta?.role && (message.role = choice.delta.role)
 | 
					      choice.delta?.role && (message.role = choice.delta.role)
 | 
				
			||||||
      if (choice.delta?.content) {
 | 
					      if (choice.delta?.content) {
 | 
				
			||||||
        if (this.isFill && choice.delta.content.match(/^'(t|ll|ve|m|d|re)[^a-z]/i)) {
 | 
					        if (!this.didFill && this.isFill && choice.delta.content.match(/^'(t|ll|ve|m|d|re)[^a-z]/i)) {
 | 
				
			||||||
          // deal with merging contractions since we've added an extra space to your fill message
 | 
					          // deal with merging contractions since we've added an extra space to your fill message
 | 
				
			||||||
          message.content.replace(/([a-z]) $/i, '$1')
 | 
					          message.content.replace(/([a-z]) $/i, '$1')
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.isFill = false
 | 
					        this.didFill = true
 | 
				
			||||||
        message.content += choice.delta.content
 | 
					        message.content += choice.delta.content
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      completionTokenCount += encode(message.content).length
 | 
					      completionTokenCount += encode(message.content).length
 | 
				
			||||||
      message.usage = response.usage || {
 | 
					 | 
				
			||||||
        prompt_tokens: this.promptTokenCount
 | 
					 | 
				
			||||||
      } as Usage
 | 
					 | 
				
			||||||
      message.model = response.model
 | 
					      message.model = response.model
 | 
				
			||||||
 | 
					      this.setModel(response.model)
 | 
				
			||||||
      message.finish_reason = choice.finish_reason
 | 
					      message.finish_reason = choice.finish_reason
 | 
				
			||||||
      message.streaming = choice.finish_reason === null && !this.finished
 | 
					      message.streaming = choice.finish_reason === null && !this.finished
 | 
				
			||||||
      this.messages[i] = message
 | 
					      this.messages[i] = message
 | 
				
			||||||
      if (this.opts.autoAddMessages) addMessage(this.chat.id, message)
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    // total up the tokens
 | 
					    // total up the tokens
 | 
				
			||||||
    const totalTokens = this.promptTokenCount + completionTokenCount
 | 
					    const promptTokens = this.promptTokenCount + (this.offsetTotals?.prompt_tokens || 0)
 | 
				
			||||||
 | 
					    const totalTokens = promptTokens + completionTokenCount
 | 
				
			||||||
    this.messages.forEach(m => {
 | 
					    this.messages.forEach(m => {
 | 
				
			||||||
      if (m.usage) {
 | 
					      m.usage = {
 | 
				
			||||||
        m.usage.completion_tokens = completionTokenCount
 | 
					        completion_tokens: completionTokenCount,
 | 
				
			||||||
        m.usage.total_tokens = totalTokens
 | 
					        total_tokens: totalTokens,
 | 
				
			||||||
      }
 | 
					        prompt_tokens: promptTokens
 | 
				
			||||||
 | 
					      } as Usage
 | 
				
			||||||
 | 
					      if (this.opts.autoAddMessages) addMessage(this.chat.id, m)
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    const finished = !this.messages.find(m => m.streaming)
 | 
					    const finished = !this.messages.find(m => m.streaming)
 | 
				
			||||||
    this.notifyMessageChange()
 | 
					    this.notifyMessageChange()
 | 
				
			||||||
| 
						 | 
					@ -167,12 +179,13 @@ export class ChatCompletionResponse {
 | 
				
			||||||
    this.messages.forEach(m => { m.streaming = false }) // make sure all are marked stopped
 | 
					    this.messages.forEach(m => { m.streaming = false }) // make sure all are marked stopped
 | 
				
			||||||
    saveChatStore()
 | 
					    saveChatStore()
 | 
				
			||||||
    const message = this.messages[0]
 | 
					    const message = this.messages[0]
 | 
				
			||||||
 | 
					    const model = this.model || getLatestKnownModel(this.chat.settings.model as Model)
 | 
				
			||||||
    if (message) {
 | 
					    if (message) {
 | 
				
			||||||
      if (this.offsetTotals) {
 | 
					      if (this.isFill && this.lastModel === this.model && this.offsetTotals && model && message.usage) {
 | 
				
			||||||
        // Need to subtract some previous message totals before we add new combined message totals
 | 
					        // Need to subtract some previous message totals before we add new combined message totals
 | 
				
			||||||
        subtractRunningTotal(this.chat.id, this.offsetTotals, this.chat.settings.model as any)
 | 
					        subtractRunningTotal(this.chat.id, this.offsetTotals, model)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      updateRunningTotal(this.chat.id, message.usage as any, message.model as any)
 | 
					      updateRunningTotal(this.chat.id, message.usage as Usage, model)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // If no messages it's probably because of an error or user initiated abort.
 | 
					      // If no messages it's probably because of an error or user initiated abort.
 | 
				
			||||||
      // We could miss counting the cost of the prompts sent.
 | 
					      // We could miss counting the cost of the prompts sent.
 | 
				
			||||||
| 
						 | 
					@ -186,7 +199,7 @@ export class ChatCompletionResponse {
 | 
				
			||||||
        completion_tokens: 0, // We have no idea if there are any to count
 | 
					        completion_tokens: 0, // We have no idea if there are any to count
 | 
				
			||||||
        total_tokens: this.promptTokenCount
 | 
					        total_tokens: this.promptTokenCount
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      updateRunningTotal(this.chat.id, usage as any, this.chat.settings.model as any)
 | 
					      updateRunningTotal(this.chat.id, usage as Usage, model)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.notifyFinish()
 | 
					    this.notifyFinish()
 | 
				
			||||||
    if (this.error) {
 | 
					    if (this.error) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@
 | 
				
			||||||
  import { errorNotice } from './Util.svelte'
 | 
					  import { errorNotice } from './Util.svelte'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export const chatsStorage = persisted('chats', [] as Chat[])
 | 
					  export const chatsStorage = persisted('chats', [] as Chat[])
 | 
				
			||||||
 | 
					  export const latestModelMap = persisted('latestModelMap', {} as Record<Model, Model>) // What was returned when a model was requested
 | 
				
			||||||
  export const globalStorage = persisted('global', {} as GlobalSettings)
 | 
					  export const globalStorage = persisted('global', {} as GlobalSettings)
 | 
				
			||||||
  export const apiKeyStorage = persisted('apiKey', '' as string)
 | 
					  export const apiKeyStorage = persisted('apiKey', '' as string)
 | 
				
			||||||
  export let checkStateChange = writable(0) // Trigger for Chat
 | 
					  export let checkStateChange = writable(0) // Trigger for Chat
 | 
				
			||||||
| 
						 | 
					@ -462,5 +463,17 @@
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return cname
 | 
					    return cname
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  export const getLatestKnownModel = (model:Model) => {
 | 
				
			||||||
 | 
					    const modelMapStore = get(latestModelMap)
 | 
				
			||||||
 | 
					    return modelMapStore[model] || model
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  export const setLatestKnownModel = (requestedModel:Model, responseModel:Model) => {
 | 
				
			||||||
 | 
					    const modelMapStore = get(latestModelMap)
 | 
				
			||||||
 | 
					    modelMapStore[requestedModel] = responseModel
 | 
				
			||||||
 | 
					    latestModelMap.set(modelMapStore)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue