Skip to content

Commit 9d4307d

Browse files
committed
ifcchat: Throttling of messages based on estimated token counts
1 parent ab7d9fd commit 9d4307d

1 file changed

Lines changed: 35 additions & 2 deletions

File tree

src/ifcchat/app.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ let messages = []; // running conversation state (Chat Completions style)
499499

500500
const MAX_TOOL_RESULT_CHARS = 0;
501501
const MAX_HISTORY_MESSAGES = 40;
502+
const ESTIMATED_CHARS_PER_TOKEN = 4;
503+
const MAX_ESTIMATED_TOKENS_PER_MINUTE = 20000;
504+
const minuteTokenMap = new Map();
502505

503506
function truncateToolResult(text) {
504507
if (MAX_TOOL_RESULT_CHARS == 0 || text.length <= MAX_TOOL_RESULT_CHARS) return text;
@@ -518,23 +521,50 @@ function trimHistory() {
518521
}
519522
}
520523

524+
function getEstimatedTokenMinuteLog(firstIterationMinuteBucket) {
525+
return Array.from(minuteTokenMap.entries())
526+
.filter(([minuteBucket]) => minuteBucket >= firstIterationMinuteBucket)
527+
.sort(([leftMinuteBucket], [rightMinuteBucket]) => leftMinuteBucket - rightMinuteBucket)
528+
.map(([minuteBucket, estimatedTokens]) => ({
529+
timestamp: new Date(minuteBucket * 60000).toISOString(),
530+
estimated_tokens: estimatedTokens,
531+
}));
532+
}
533+
521534
async function runAgentTurn(userText) {
522535
const apiKey = apiKeyEl.value.trim();
523536
if (!apiKey) throw new Error("Missing API key");
524537

525538
const provider = PROVIDERS[getProviderValue()];
526539
const { chat } = provider.api;
527540
const baseURL = provider.baseUrlDefault ? baseUrlEl.value.trim() : undefined;
541+
let firstIterationMinuteBucket = null;
528542

529543
messages.push({ role: "user", content: userText });
530544
trimHistory();
531545

532546
for (let i = 0; i < 64; i++) {
547+
const messages_with_system = [{ role: "system", content: SYSTEM_INSTRUCTIONS }, ...messages];
548+
const estimatedTokens = Math.max(1, Math.ceil(JSON.stringify(messages_with_system).length / ESTIMATED_CHARS_PER_TOKEN));
549+
const now = Date.now();
550+
let currentMinuteBucket = Math.floor(now / 60000);
551+
if (firstIterationMinuteBucket === null) {
552+
firstIterationMinuteBucket = currentMinuteBucket;
553+
}
554+
const estimateTokenUsage = (minuteTokenMap.get(currentMinuteBucket) ?? 0) + estimatedTokens;
555+
556+
if (estimateTokenUsage > MAX_ESTIMATED_TOKENS_PER_MINUTE) {
557+
currentMinuteBucket += 1;
558+
await new Promise((resolve) => setTimeout(() => resolve(), 60000));
559+
}
560+
561+
minuteTokenMap.set(currentMinuteBucket, (minuteTokenMap.get(currentMinuteBucket) ?? 0) + estimatedTokens);
562+
533563
const response = await chat({
534564
apiKey,
535565
baseURL,
536566
model: modelEl.value,
537-
messages: [{ role: "system", content: SYSTEM_INSTRUCTIONS }, ...messages],
567+
messages: messages_with_system,
538568
tools,
539569
});
540570

@@ -546,7 +576,10 @@ async function runAgentTurn(userText) {
546576
if (message.content) addMessage("assistant", message.content);
547577

548578
const calls = message.tool_calls ?? [];
549-
if (calls.length === 0) return;
579+
if (calls.length === 0) {
580+
console.log("Estimated token usage by minute", getEstimatedTokenMinuteLog(firstIterationMinuteBucket));
581+
return;
582+
}
550583

551584
for (const call of calls) {
552585
let args = {};

0 commit comments

Comments
 (0)