1
0
Fork 0

Tokens counter

This commit is contained in:
Pabloader 2026-03-23 20:18:46 +00:00
parent b2b84fa819
commit ca4f87db4b
3 changed files with 70 additions and 4 deletions

View File

@ -125,6 +125,18 @@
border-top: 1px solid var(--border); border-top: 1px solid var(--border);
} }
.optionsRow {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.tokenCounter {
font-size: 11px;
color: var(--text-muted);
}
.toggleContainer { .toggleContainer {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -44,6 +44,7 @@ export const ChatSidebar = () => {
const [input, setInput] = useInputState(''); const [input, setInput] = useInputState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isCollapsed, setCollapsed] = useState(false); const [isCollapsed, setCollapsed] = useState(false);
const [tokenCount, setTokenCount] = useState<{ taken: number; total: number } | null>(null);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const messagesRef = useRef<HTMLDivElement>(null); const messagesRef = useRef<HTMLDivElement>(null);
@ -73,6 +74,43 @@ export const ChatSidebar = () => {
}; };
}, []); }, []);
useEffect(() => {
if (!currentStory || !connection || !model) {
setTokenCount(null);
return;
}
const countTokens = async () => {
try {
const messages: LLM.ChatMessage[] = [];
if (input.trim()) {
messages.push({ role: 'user', content: input.trim() });
}
const chatRequest = Prompt.compilePrompt(appState, messages);
const countRequest: LLM.CountTokensRequest = {
model: model.id,
input: chatRequest?.messages ?? [],
tools: chatRequest?.tools,
enable_thinking: chatRequest?.enable_thinking,
};
const response = await LLM.countTokens(connection, countRequest);
setTokenCount({
taken: response.input_tokens,
total: model.max_context ?? response.input_tokens,
});
} catch {
setTokenCount(null);
}
};
const timeoutId = setTimeout(countTokens, 300);
return () => clearTimeout(timeoutId);
}, [currentStory, connection, model, input, currentStory?.chatMessages.length]);
const sendMessage = useCallback(async (newMessages: ChatMessage[]) => { const sendMessage = useCallback(async (newMessages: ChatMessage[]) => {
if (!currentStory || !connection || !model) return; if (!currentStory || !connection || !model) return;
@ -277,7 +315,7 @@ export const ChatSidebar = () => {
)} )}
{currentStory && ( {currentStory && (
<div class={styles.inputContainer}> <div class={styles.inputContainer}>
{model?.support_thinking && <div class={styles.optionsRow}>
<label class={styles.toggleContainer}> <label class={styles.toggleContainer}>
<input <input
type="checkbox" type="checkbox"
@ -290,7 +328,12 @@ export const ChatSidebar = () => {
/> />
<span>Enable thinking</span> <span>Enable thinking</span>
</label> </label>
} {tokenCount && (
<div class={styles.tokenCounter}>
{tokenCount.taken} / {tokenCount.total} tokens
</div>
)}
</div>
<textarea <textarea
class={styles.input} class={styles.input}
value={input} value={input}

View File

@ -153,11 +153,22 @@ namespace LLM {
data: ModelInfo[]; data: ModelInfo[];
} }
export interface CountTokensRequest {
interface CountTokensRequestString {
model: string; model: string;
input: string | ChatMessage[]; input: string;
} }
interface CountTokensRequestMessages {
model: string;
input: LLM.ChatMessage[];
tools?: LLM.Tool[];
add_generation_prompt?: boolean;
enable_thinking?: boolean;
}
export type CountTokensRequest = CountTokensRequestString | CountTokensRequestMessages;
export interface CountTokensResponse { export interface CountTokensResponse {
object: 'response.input_tokens'; object: 'response.input_tokens';
input_tokens: number; input_tokens: number;