diff --git a/src/renderer/src/config/constant.ts b/src/renderer/src/config/constant.ts index a1d31d054..f666ee061 100644 --- a/src/renderer/src/config/constant.ts +++ b/src/renderer/src/config/constant.ts @@ -1,3 +1,2 @@ export const DEFAULT_TEMPERATURE = 0.7 -export const DEFAULT_MAXTOKENS = 800 export const DEFAULT_CONEXTCOUNT = 5 diff --git a/src/renderer/src/i18n/index.ts b/src/renderer/src/i18n/index.ts index e7cfe431a..acf89b1e4 100644 --- a/src/renderer/src/i18n/index.ts +++ b/src/renderer/src/i18n/index.ts @@ -66,11 +66,10 @@ const resources = { 'input.send': 'Send', 'input.pause': 'Pause', 'input.settings': 'Settings', + 'input.estimated_tokens': 'Estimated tokens: ', 'settings.temperature': 'Temperature', 'settings.temperature.tip': 'Lower values make the model more creative and unpredictable, while higher values make it more deterministic and precise.', - 'settings.max_tokens': 'Max Tokens', - 'settings.max_tokens.tip': 'The maximum number of tokens to generate in the completion.', 'settings.conext_count': 'Context', 'settings.conext_count.tip': 'The number of previous messages to keep in the context.', 'settings.reset': 'Reset', @@ -200,12 +199,10 @@ const resources = { 'input.send': '发送', 'input.pause': '暂停', 'input.settings': '设置', + 'input.estimated_tokens': '预估消耗', 'settings.temperature': '模型温度', 'settings.temperature.tip': '模型生成文本的随机程度。值越大,回复内容越赋有多样性、创造性、随机性;设为 0 根据事实回答。日常聊天建议设置为 0.7', - 'settings.max_tokens': '最大回复', - 'settings.max_tokens.tip': - '最大回复内容多少,数值越大,生成的文本越长。普通聊天建议 500-800;短文生成建议 800-2000;代码生成建议 2000-3600;长文生成建议切换模型到 4000 左右', 'settings.conext_count': '上下文数', 'settings.conext_count.tip': '要保留在上下文中的消息数量,数值越大,上下文越长,消耗的 token 越多。普通聊天建议 5-10,代码生成建议 5-10', @@ -233,7 +230,7 @@ const resources = { }, settings: { title: '设置', - general: '常规', + general: '常规设置', provider: '模型提供商', model: '模型设置', assistant: '默认助手', diff --git a/src/renderer/src/pages/home/components/AssistantSettings.tsx b/src/renderer/src/pages/home/components/AssistantSettings.tsx index 55012eb93..21fe6eafa 100644 --- a/src/renderer/src/pages/home/components/AssistantSettings.tsx +++ b/src/renderer/src/pages/home/components/AssistantSettings.tsx @@ -1,9 +1,10 @@ import { QuestionCircleOutlined } from '@ant-design/icons' -import { DEFAULT_CONEXTCOUNT, DEFAULT_MAXTOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { useAssistants } from '@renderer/hooks/useAssistant' import { Assistant } from '@renderer/types' import { Button, Col, InputNumber, Popover, Row, Slider, Tooltip } from 'antd' -import { FC, PropsWithChildren, useState } from 'react' +import { debounce } from 'lodash' +import { FC, PropsWithChildren, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -14,29 +15,26 @@ interface Props { const PopoverContent: FC = ({ assistant }) => { const { updateAssistant } = useAssistants() const [temperature, setTemperature] = useState(assistant.settings?.temperature ?? DEFAULT_TEMPERATURE) - const [maxTokens, setMaxTokens] = useState(assistant.settings?.maxTokens ?? DEFAULT_MAXTOKENS) const [contextCount, setConextCount] = useState(assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) const { t } = useTranslation() - const onUpdateAssistantSettings = ({ - _temperature, - _maxTokens, - _contextCount - }: { - _temperature?: number - _maxTokens?: number - _contextCount?: number - }) => { - updateAssistant({ - ...assistant, - settings: { - ...assistant.settings, - temperature: _temperature ?? temperature, - maxTokens: _maxTokens ?? maxTokens, - contextCount: _contextCount ?? contextCount - } - }) - } + const onUpdateAssistantSettings = useCallback( + debounce( + ({ _temperature, _contextCount }: { _temperature?: number; _contextCount?: number }) => { + updateAssistant({ + ...assistant, + settings: { + ...assistant.settings, + temperature: _temperature ?? temperature, + contextCount: _contextCount ?? contextCount + } + }) + }, + 1000, + { leading: false, trailing: true } + ), + [] + ) const onTemperatureChange = (value) => { if (!isNaN(value as number)) { @@ -45,13 +43,6 @@ const PopoverContent: FC = ({ assistant }) => { } } - const onMaxTokensChange = (value) => { - if (!isNaN(value as number)) { - setMaxTokens(value) - onUpdateAssistantSettings({ _maxTokens: value }) - } - } - const onConextCountChange = (value) => { if (!isNaN(value as number)) { setConextCount(value) @@ -61,24 +52,27 @@ const PopoverContent: FC = ({ assistant }) => { const onReset = () => { setTemperature(DEFAULT_TEMPERATURE) - setMaxTokens(DEFAULT_MAXTOKENS) setConextCount(DEFAULT_CONEXTCOUNT) updateAssistant({ ...assistant, settings: { ...assistant.settings, temperature: DEFAULT_TEMPERATURE, - maxTokens: DEFAULT_MAXTOKENS, contextCount: DEFAULT_CONEXTCOUNT } }) } + useEffect(() => { + setTemperature(assistant.settings?.temperature ?? DEFAULT_TEMPERATURE) + setConextCount(assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) + }, [assistant]) + return ( - - + + @@ -95,11 +89,11 @@ const PopoverContent: FC = ({ assistant }) => { step={0.1} /> - + = ({ assistant }) => { - - + + @@ -126,11 +120,11 @@ const PopoverContent: FC = ({ assistant }) => { step={1} /> - + = ({ assistant }) => { /> - - - - - - - - - - - - - - - - - - + + ) @@ -199,7 +160,7 @@ const Container = styled.div` display: flex; flex-direction: column; margin-bottom: 8px; - width: 500px; + width: 420px; padding: 5px; ` diff --git a/src/renderer/src/pages/home/components/Assistants.tsx b/src/renderer/src/pages/home/components/Assistants.tsx index a515e54ee..846cc80cd 100644 --- a/src/renderer/src/pages/home/components/Assistants.tsx +++ b/src/renderer/src/pages/home/components/Assistants.tsx @@ -24,11 +24,9 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs const { t } = useTranslation() const onDelete = (assistant: Assistant) => { + const _assistant = last(assistants.filter((a) => a.id !== assistant.id)) + _assistant ? setActiveAssistant(_assistant) : onCreateAssistant() removeAssistant(assistant.id) - setTimeout(() => { - const _assistant = last(assistants.filter((a) => a.id !== assistant.id)) - _assistant ? setActiveAssistant(_assistant) : onCreateAssistant() - }, 0) } const items: MenuProps['items'] = [ diff --git a/src/renderer/src/pages/home/components/Chat.tsx b/src/renderer/src/pages/home/components/Chat.tsx index 6fbf70031..b2e8eb66e 100644 --- a/src/renderer/src/pages/home/components/Chat.tsx +++ b/src/renderer/src/pages/home/components/Chat.tsx @@ -1,5 +1,5 @@ -import { Assistant, Message } from '@renderer/types' -import { FC, useRef } from 'react' +import { Assistant } from '@renderer/types' +import { FC } from 'react' import styled from 'styled-components' import Inputbar from './Inputbar' import Messages from './Messages' @@ -15,17 +15,12 @@ interface Props { const Chat: FC = (props) => { const { assistant } = useAssistant(props.assistant.id) const { activeTopic, setActiveTopic } = useActiveTopic(assistant) - const messagesRef = useRef([]) - - if (!assistant) { - return null - } return ( - - + + diff --git a/src/renderer/src/pages/home/components/Inputbar.tsx b/src/renderer/src/pages/home/components/Inputbar.tsx index b111d4bbd..6f352875a 100644 --- a/src/renderer/src/pages/home/components/Inputbar.tsx +++ b/src/renderer/src/pages/home/components/Inputbar.tsx @@ -1,7 +1,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { Assistant, Message, Topic } from '@renderer/types' -import { estimateTokenCount, uuid } from '@renderer/utils' -import { FC, MutableRefObject, useCallback, useEffect, useRef, useState } from 'react' +import { estimateInputTokenCount, uuid } from '@renderer/utils' +import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react' import styled from 'styled-components' import { MoreOutlined } from '@ant-design/icons' import { Button, Popconfirm, Tooltip } from 'antd' @@ -9,15 +9,15 @@ import { useShowRightSidebar } from '@renderer/hooks/useStore' import { useAssistant } from '@renderer/hooks/useAssistant' import { ClearOutlined, + ControlOutlined, FullscreenExitOutlined, FullscreenOutlined, HistoryOutlined, PauseCircleOutlined, - PlusCircleOutlined, - SettingOutlined + PlusCircleOutlined } from '@ant-design/icons' import TextArea, { TextAreaRef } from 'antd/es/input/TextArea' -import { isEmpty } from 'lodash' +import { debounce, isEmpty } from 'lodash' import SendMessageSetting from './SendMessageSetting' import { useSettings } from '@renderer/hooks/useSettings' import dayjs from 'dayjs' @@ -30,15 +30,15 @@ import AssistantSettings from './AssistantSettings' interface Props { assistant: Assistant setActiveTopic: (topic: Topic) => void - messagesRef: MutableRefObject } -const Inputbar: FC = ({ assistant, setActiveTopic, messagesRef }) => { +const Inputbar: FC = ({ assistant, setActiveTopic }) => { const [text, setText] = useState('') const { setShowRightSidebar } = useShowRightSidebar() const { addTopic } = useAssistant(assistant.id) const { sendMessageShortcut } = useSettings() const [expended, setExpend] = useState(false) + const [estimateTokenCount, setEstimateTokenCount] = useState(0) const generating = useAppSelector((state) => state.runtime.generating) const inputRef = useRef(null) @@ -68,6 +68,8 @@ const Inputbar: FC = ({ assistant, setActiveTopic, messagesRef }) => { setText('') } + const inputTokenCount = useMemo(() => estimateInputTokenCount(text), [text]) + const handleKeyDown = (event: React.KeyboardEvent) => { if (sendMessageShortcut === 'Enter' && event.key === 'Enter') { if (event.shiftKey) { @@ -96,8 +98,6 @@ const Inputbar: FC = ({ assistant, setActiveTopic, messagesRef }) => { store.dispatch(setGenerating(false)) } - const textCount = text.length === 0 ? '' : (text: string) => estimateTokenCount(text, assistant, messagesRef.current) - // Command or Ctrl + N create new topic useEffect(() => { const onKeydown = (e) => { @@ -113,11 +113,13 @@ const Inputbar: FC = ({ assistant, setActiveTopic, messagesRef }) => { }, [addNewTopic, generating]) useEffect(() => { + const _setEstimateTokenCount = debounce(setEstimateTokenCount, 100, { leading: false, trailing: true }) const unsubscribes = [ EventEmitter.on(EVENT_NAMES.EDIT_MESSAGE, (message: Message) => { setText(message.content) inputRef.current?.focus() - }) + }), + EventEmitter.on(EVENT_NAMES.ESTIMATED_TOKEN_COUNT, _setEstimateTokenCount) ] return () => unsubscribes.forEach((unsub) => unsub()) }, []) @@ -155,7 +157,7 @@ const Inputbar: FC = ({ assistant, setActiveTopic, messagesRef }) => { - + @@ -188,19 +190,12 @@ const Inputbar: FC = ({ assistant, setActiveTopic, messagesRef }) => { contextMenu="true" variant="borderless" showCount - count={{ strategy: textCount }} ref={inputRef} - styles={{ - textarea: { paddingLeft: 0 }, - count: { - position: 'absolute', - right: 5, - bottom: 5, - fontSize: 11, - display: text.length === 0 ? 'none' : 'block' - } - }} + styles={{ textarea: { paddingLeft: 0 } }} /> + + {t('assistant.input.estimated_tokens')}: {`${inputTokenCount}/${estimateTokenCount}`} + ) } @@ -213,6 +208,7 @@ const Container = styled.div` border-top: 0.5px solid var(--color-border); padding: 5px 15px; transition: all 0.3s ease; + position: relative; ` const Textarea = styled(TextArea)` @@ -256,4 +252,12 @@ const ToolbarButton = styled(Button)` } ` +const TextCount = styled.div` + position: absolute; + right: 8px; + bottom: 8px; + font-size: 11px; + color: var(--color-text-3); +` + export default Inputbar diff --git a/src/renderer/src/pages/home/components/Message.tsx b/src/renderer/src/pages/home/components/Message.tsx index 068ae323b..39d4cd1bd 100644 --- a/src/renderer/src/pages/home/components/Message.tsx +++ b/src/renderer/src/pages/home/components/Message.tsx @@ -104,8 +104,8 @@ const MessageItem: FC = ({ message, index, showMenu, onDeleteMessage }) = {message.modelId} {message.usage && ( <> - - tokens used: {message.usage.total_tokens} (IN:{message.usage.prompt_tokens}/OUT: + + Tokens: {message.usage.total_tokens} (IN:{message.usage.prompt_tokens}/OUT: {message.usage.completion_tokens}) diff --git a/src/renderer/src/pages/home/components/Messages.tsx b/src/renderer/src/pages/home/components/Messages.tsx index 0461dd5f7..4b34ed875 100644 --- a/src/renderer/src/pages/home/components/Messages.tsx +++ b/src/renderer/src/pages/home/components/Messages.tsx @@ -1,13 +1,13 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { Assistant, Message, Topic } from '@renderer/types' import localforage from 'localforage' -import { FC, MutableRefObject, useCallback, useEffect, useRef, useState } from 'react' +import { FC, useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' import MessageItem from './Message' import { reverse } from 'lodash' import { fetchChatCompletion, fetchMessagesSummary } from '@renderer/services/api' import { useAssistant } from '@renderer/hooks/useAssistant' -import { runAsyncFunction } from '@renderer/utils' +import { estimateHistoryTokenCount, runAsyncFunction } from '@renderer/utils' import LocalStorage from '@renderer/services/storage' import { useProviderByAssistant } from '@renderer/hooks/useProvider' import { t } from 'i18next' @@ -15,10 +15,9 @@ import { t } from 'i18next' interface Props { assistant: Assistant topic: Topic - messagesRef: MutableRefObject } -const Messages: FC = ({ assistant, topic, messagesRef }) => { +const Messages: FC = ({ assistant, topic }) => { const [messages, setMessages] = useState([]) const [lastMessage, setLastMessage] = useState(null) const { updateTopic } = useAssistant(assistant.id) @@ -97,8 +96,11 @@ const Messages: FC = ({ assistant, topic, messagesRef }) => { useEffect(() => { containerRef.current?.scrollTo({ top: 100000, behavior: 'auto' }) - messagesRef.current = messages - }, [messages, messagesRef]) + }, [messages]) + + useEffect(() => { + EventEmitter.emit(EVENT_NAMES.ESTIMATED_TOKEN_COUNT, estimateHistoryTokenCount(assistant, messages)) + }, [assistant, messages]) return ( diff --git a/src/renderer/src/pages/home/components/Topics.tsx b/src/renderer/src/pages/home/components/Topics.tsx index 469c29a5a..b7882202d 100644 --- a/src/renderer/src/pages/home/components/Topics.tsx +++ b/src/renderer/src/pages/home/components/Topics.tsx @@ -82,12 +82,8 @@ const Topics: FC = ({ assistant, activeTopic, setActiveTopic }) => { } } - if (!showRightSidebar) { - return null - } - return ( - + {t('assistant.topics.title')} ({assistant.topics.length}) diff --git a/src/renderer/src/pages/settings/AssistantSettings.tsx b/src/renderer/src/pages/settings/AssistantSettings.tsx index 01b93f273..551f46e92 100644 --- a/src/renderer/src/pages/settings/AssistantSettings.tsx +++ b/src/renderer/src/pages/settings/AssistantSettings.tsx @@ -1,40 +1,38 @@ import { QuestionCircleOutlined } from '@ant-design/icons' -import { DEFAULT_CONEXTCOUNT, DEFAULT_MAXTOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' import { useDefaultAssistant } from '@renderer/hooks/useAssistant' import { Button, Col, Input, InputNumber, Row, Slider, Tooltip } from 'antd' import TextArea from 'antd/es/input/TextArea' -import { FC, useState } from 'react' +import { FC, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import { SettingContainer, SettingDivider, SettingSubtitle, SettingTitle } from './components' +import { debounce } from 'lodash' const AssistantSettings: FC = () => { const { defaultAssistant, updateDefaultAssistant } = useDefaultAssistant() - const [temperature, setTemperature] = useState(defaultAssistant.settings?.temperature || DEFAULT_TEMPERATURE) - const [maxTokens, setMaxTokens] = useState(defaultAssistant.settings?.maxTokens || DEFAULT_MAXTOKENS) - const [contextCount, setConextCount] = useState(defaultAssistant.settings?.contextCount || DEFAULT_CONEXTCOUNT) + const [temperature, setTemperature] = useState(defaultAssistant.settings?.temperature ?? DEFAULT_TEMPERATURE) + const [contextCount, setConextCount] = useState(defaultAssistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT) const { t } = useTranslation() - const onUpdateAssistantSettings = ({ - _temperature, - _maxTokens, - _contextCount - }: { - _temperature?: number - _maxTokens?: number - _contextCount?: number - }) => { - updateDefaultAssistant({ - ...defaultAssistant, - settings: { - ...defaultAssistant.settings, - temperature: _temperature || temperature, - maxTokens: _maxTokens || maxTokens, - contextCount: _contextCount || contextCount - } - }) - } + const onUpdateAssistantSettings = useCallback( + debounce( + ({ _temperature, _contextCount }: { _temperature?: number; _contextCount?: number }) => { + updateDefaultAssistant({ + ...defaultAssistant, + settings: { + ...defaultAssistant.settings, + temperature: _temperature ?? temperature, + contextCount: _contextCount ?? contextCount + } + }) + }, + 1000, + { leading: false, trailing: true } + ), + [] + ) const onTemperatureChange = (value) => { if (!isNaN(value as number)) { @@ -43,13 +41,6 @@ const AssistantSettings: FC = () => { } } - const onMaxTokensChange = (value) => { - if (!isNaN(value as number)) { - setMaxTokens(value) - onUpdateAssistantSettings({ _maxTokens: value }) - } - } - const onConextCountChange = (value) => { if (!isNaN(value as number)) { setConextCount(value) @@ -59,14 +50,12 @@ const AssistantSettings: FC = () => { const onReset = () => { setTemperature(DEFAULT_TEMPERATURE) - setMaxTokens(DEFAULT_MAXTOKENS) setConextCount(DEFAULT_CONEXTCOUNT) updateDefaultAssistant({ ...defaultAssistant, settings: { ...defaultAssistant.settings, temperature: DEFAULT_TEMPERATURE, - maxTokens: DEFAULT_MAXTOKENS, contextCount: DEFAULT_CONEXTCOUNT } }) @@ -147,35 +136,7 @@ const AssistantSettings: FC = () => { /> - - - - - - - - - - - - - - - diff --git a/src/renderer/src/services/ProviderSDK.ts b/src/renderer/src/services/ProviderSDK.ts index a770366a8..038a43ecb 100644 --- a/src/renderer/src/services/ProviderSDK.ts +++ b/src/renderer/src/services/ProviderSDK.ts @@ -32,11 +32,11 @@ export default class ProviderSDK { ) { const defaultModel = getDefaultModel() const model = assistant.model || defaultModel - const { contextCount, maxTokens } = getAssistantSettings(assistant) + const { contextCount } = getAssistantSettings(assistant) const systemMessage = assistant.prompt ? { role: 'system', content: assistant.prompt } : undefined - const userMessages = takeRight(messages, contextCount).map((message) => ({ + const userMessages = takeRight(messages, contextCount + 1).map((message) => ({ role: message.role, content: message.content })) @@ -46,7 +46,7 @@ export default class ProviderSDK { .stream({ model: model.id, messages: [systemMessage, ...userMessages].filter(Boolean) as MessageParam[], - max_tokens: assistant.settings?.maxTokens || maxTokens, + max_tokens: 4096, temperature: assistant.settings?.temperature }) .on('text', (text) => onChunk({ text: text || '' })) @@ -64,7 +64,6 @@ export default class ProviderSDK { model: model.id, messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[], stream: true, - max_tokens: assistant.settings?.maxTokens, temperature: assistant.settings?.temperature }) for await (const chunk of stream) { diff --git a/src/renderer/src/services/event.ts b/src/renderer/src/services/event.ts index 9750439f6..15b7ac48a 100644 --- a/src/renderer/src/services/event.ts +++ b/src/renderer/src/services/event.ts @@ -10,5 +10,6 @@ export const EVENT_NAMES = { ADD_ASSISTANT: 'ADD_ASSISTANT', EDIT_MESSAGE: 'EDIT_MESSAGE', REGENERATE_MESSAGE: 'REGENERATE_MESSAGE', - CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED' + CHAT_COMPLETION_PAUSED: 'CHAT_COMPLETION_PAUSED', + ESTIMATED_TOKEN_COUNT: 'ESTIMATED_TOKEN_COUNT' } diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index cf5a7cd0d..ac804885b 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -13,7 +13,6 @@ export type Assistant = { export type AssistantSettings = { contextCount: number temperature: number - maxTokens: number } export type Message = { diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts index a48014fc7..03c97af99 100644 --- a/src/renderer/src/utils/index.ts +++ b/src/renderer/src/utils/index.ts @@ -2,8 +2,8 @@ import { v4 as uuidv4 } from 'uuid' import imageCompression from 'browser-image-compression' import { Assistant, AssistantSettings, Message, Model } from '@renderer/types' import { GPTTokens } from 'gpt-tokens' -import { DEFAULT_CONEXTCOUNT, DEFAULT_MAXTOKENS, DEFAULT_TEMPERATURE } from '@renderer/config/constant' -import { take } from 'lodash' +import { DEFAULT_CONEXTCOUNT, DEFAULT_TEMPERATURE } from '@renderer/config/constant' +import { takeRight } from 'lodash' export const runAsyncFunction = async (fn: () => void) => { await fn() @@ -169,31 +169,32 @@ export function getFirstCharacter(str) { } export const getAssistantSettings = (assistant: Assistant): AssistantSettings => { + const contextCount = assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT return { - contextCount: assistant.settings?.contextCount ?? DEFAULT_CONEXTCOUNT, - temperature: assistant.settings?.temperature ?? DEFAULT_TEMPERATURE, - maxTokens: assistant.settings?.maxTokens ?? DEFAULT_MAXTOKENS + contextCount: contextCount === 20 ? 100000 : contextCount, + temperature: assistant.settings?.temperature ?? DEFAULT_TEMPERATURE } } -export function estimateTokenCount(text: string, assistant: Assistant, msgs: Message[]) { - const { contextCount } = getAssistantSettings(assistant) - - console.debug('contextCount', contextCount) - +export function estimateInputTokenCount(text: string) { const input = new GPTTokens({ model: 'gpt-4o', messages: [{ role: 'user', content: text }] }) + return input.usedTokens - 7 +} + +export function estimateHistoryTokenCount(assistant: Assistant, msgs: Message[]) { + const { contextCount } = getAssistantSettings(assistant) + const all = new GPTTokens({ model: 'gpt-4o', messages: [ { role: 'system', content: assistant.prompt }, - { role: 'user', content: text }, - ...take(msgs, contextCount).map((message) => ({ role: message.role, content: message.content })) + ...takeRight(msgs, contextCount).map((message) => ({ role: message.role, content: message.content })) ] }) - return `Token ${input.usedTokens - 7} / ${all.usedTokens}` as unknown as number + return all.usedTokens - 7 }