feat(ai): show a live streaming token count in the chat

This commit is contained in:
John Smith
2026-09-24 03:11:31 -04:00
parent 039c82d7ca
commit bc2e1ad69d
3 changed files with 30 additions and 4 deletions
+7 -1
View File
@@ -7,7 +7,7 @@
"asset-curator",
"qa-playtester"
],
"last_cycle": 226,
"last_cycle": 227,
"completed_targets": [
{
"cycle": 75,
@@ -1330,6 +1330,12 @@
"target": "Flow type-check over the session's additions (no change)",
"metric_delta": "No code change. Ran `npm run flow` on the committed session state: 1039 errors total (the environment's current baseline - the previously recorded 870 was a different reading; the fork's CI does not gate app Flow). Crucially, verified the errors are NOT from this session's work: AiRequestUtils.js (where the per-agent summary/token data layer lives) has ZERO Flow errors, and no error mentions summarizeSubAgentActivity, localAiRequestProviderTelemetry or onProviderTelemetry. The CustomAIClient errors cited (lines 197/595/1052/1070) are in pre-existing code (e.g. normalizePersistedMessages' `entry.type === 'text'` from cycle 95). eslint/build remain the gating checks and pass.",
"commit": "docs(autonomous): verify Flow over the session additions"
},
{
"cycle": 227,
"target": "Live streaming token counter + a slow-stream mock option (Tier 2)",
"metric_delta": "Added the Tier 2 'streaming token counter': PartialStreamText (fork-owned ChatMessages) now appends ' . ~N tokens' computed with estimateTokens(partialContent), so progress is legible while a local model generates. Also added MOCK_STREAM_DELAY_MS to the mock provider so streaming-only UI can be exercised (chunks spaced over time). Verified: unit (estimateTokens is tested) + eslint + production build exit 0 + divergence clean; the slow stream itself verified by curl (chunks + tool calls arrive over time). Honest caveat: the counter could not be caught in a Chrome DevTools snapshot mid-stream - the standalone form's transcript/working row was not captured while a turn ran - so it rests on build/unit evidence and the fact that PartialStreamText (its host) was live-verified earlier, not on a fresh live observation.",
"commit": "feat(ai): show a live streaming token count in the chat"
}
],
"backlog": [
@@ -73,6 +73,7 @@ import {
customGetAiRequestTokenTotal,
customGetAiRequestPartialContent,
customGetAiRequestProviderTelemetry,
estimateTokens,
} from '../../AI/CustomAIClient';
/**
@@ -93,9 +94,15 @@ const PartialStreamText = ({ aiRequestId }: {| aiRequestId: string |}) => {
);
if (!partialContent) return null;
const tail = partialContent.slice(-160);
// Tier 2 streaming token counter: an approximate running count of the
// streamed content, so progress is legible while a local model generates.
const streamedTokens = estimateTokens(partialContent);
return (
<Text noMargin displayInlineAsSpan size="body-small" color="secondary">
{tail}
{streamedTokens > 0
? ` · ≈${streamedTokens.toLocaleString()} tokens`
: ''}
</Text>
);
};
+16 -3
View File
@@ -165,13 +165,23 @@ const server = http.createServer((req, res) => {
Object.assign({ 'Content-Type': 'text/event-stream' }, CORS, headers)
);
const chunks = reply.match(/.{1,24}/g) || (reply ? [reply] : []);
for (const chunk of chunks) {
// MOCK_STREAM_DELAY_MS spaces out chunks so UI that only shows while a
// stream is live (e.g. the streaming token counter) can be observed.
const streamDelayMs = Number(process.env.MOCK_STREAM_DELAY_MS) || 0;
const writeChunk = index => {
if (index >= chunks.length) return sendTail();
res.write(
'data: ' +
JSON.stringify({ choices: [{ delta: { content: chunk } }] }) +
JSON.stringify({ choices: [{ delta: { content: chunks[index] } }] }) +
'\n\n'
);
}
if (streamDelayMs > 0) {
setTimeout(() => writeChunk(index + 1), streamDelayMs);
} else {
writeChunk(index + 1);
}
};
const sendTail = () => {
if (toolCalls.length > 0) {
res.write(
'data: ' +
@@ -205,6 +215,9 @@ const server = http.createServer((req, res) => {
);
res.write('data: [DONE]\n\n');
return res.end();
};
writeChunk(0);
return;
}
const message = { role: 'assistant', content: reply };