Use provider-reported pi session context usage to drive the Chat context indicator. - Track measured and pending session context usage in the dashboard. - Expose bounded context metadata from chat sessions and update token estimation. - Add regression coverage, localized labels, documentation, and a changeset. Files changed: .changeset/fn-165-chat-context-usage.md | 7 ++ docs/dashboard-guide.md | 4 +- packages/dashboard/app/components/ChatView.tsx | 49 ++++++++---- .../__tests__/ChatView.context-window.test.tsx | 84 +++++++++++++++++++- .../app/utils/__tests__/chatContextUsage.test.ts | 66 ++++++++++++++++ .../app/utils/__tests__/estimateChatTokens.test.ts | 10 +++ packages/dashboard/app/utils/chatContextUsage.ts | 73 ++++++++++++++++++ packages/dashboard/app/utils/estimateChatTokens.ts | 39 ++++++++-- .../dashboard/src/__tests__/chat-manager.test.ts | 89 +++++++++++++++++++++ packages/dashboard/src/chat.ts | 65 +++++++++++++--- packages/i18n/locales/en/app.json | 3 + packages/i18n/locales/es/app.json | 3 + packages/i18n/locales/fr/app.json | 3 + packages/i18n/locales/ko/app.json | 3 + packages/i18n/locales/pt-BR/app.json | 3 + packages/i18n/locales/zh-CN/app.json | 3 + packages/i18n/locales/zh-TW/app.json | 3 + packages/i18n/src/resources.d.ts | 19 +++-- 18 files changed, 483 insertions(+), 43 deletions(-) Fusion-Task-Id: FN-165 Fusion-Task-Lineage: a95a9c7f-80aa-41be-8807-1fbd383f0f1d Co-authored-by: Fusion <noreply@runfusion.ai>
74 lines
2.5 KiB
TypeScript
74 lines
2.5 KiB
TypeScript
import { estimateChatTokens, type ChatTokenEstimateMessage } from "./estimateChatTokens";
|
|
|
|
export interface ChatContextUsageRecord {
|
|
tokens: number | null;
|
|
contextWindow: number;
|
|
percent: number | null;
|
|
}
|
|
|
|
export type ChatContextUsageSource = "measured" | "pending" | "estimated";
|
|
|
|
export interface ChatContextUsageMessage extends ChatTokenEstimateMessage {
|
|
metadata?: unknown;
|
|
}
|
|
|
|
export interface ResolvedChatContextUsage {
|
|
source: ChatContextUsageSource;
|
|
used: number | null;
|
|
total: number;
|
|
approximate: boolean;
|
|
percent: number | null;
|
|
}
|
|
|
|
export function parseChatContextUsage(metadata: unknown): ChatContextUsageRecord | null {
|
|
if (!metadata || typeof metadata !== "object") return null;
|
|
const contextUsage = (metadata as Record<string, unknown>).contextUsage;
|
|
if (!contextUsage || typeof contextUsage !== "object") return null;
|
|
const { tokens, contextWindow, percent } = contextUsage as Record<string, unknown>;
|
|
if (typeof contextWindow !== "number" || !Number.isFinite(contextWindow) || contextWindow <= 0) return null;
|
|
if (tokens !== null && (typeof tokens !== "number" || !Number.isFinite(tokens))) return null;
|
|
return {
|
|
tokens: tokens === null ? null : Math.max(0, Math.trunc(tokens)),
|
|
contextWindow: Math.max(1, Math.trunc(contextWindow)),
|
|
percent: typeof percent === "number" && Number.isFinite(percent) ? percent : null,
|
|
};
|
|
}
|
|
|
|
export function resolveChatContextUsage({
|
|
messages,
|
|
streamingText,
|
|
fallbackContextWindow,
|
|
}: {
|
|
messages: ChatContextUsageMessage[];
|
|
streamingText?: string | null;
|
|
fallbackContextWindow: number | null | undefined;
|
|
}): ResolvedChatContextUsage | null {
|
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
const record = parseChatContextUsage(messages[index].metadata);
|
|
if (!record) continue;
|
|
if (record.tokens === null) {
|
|
return { source: "pending", used: null, total: record.contextWindow, approximate: false, percent: null };
|
|
}
|
|
const trailing = estimateChatTokens(messages.slice(index + 1), streamingText);
|
|
const used = record.tokens + trailing;
|
|
return {
|
|
source: "measured",
|
|
used,
|
|
total: record.contextWindow,
|
|
approximate: trailing > 0,
|
|
percent: (used / record.contextWindow) * 100,
|
|
};
|
|
}
|
|
|
|
if (typeof fallbackContextWindow !== "number" || !Number.isFinite(fallbackContextWindow) || fallbackContextWindow <= 0) {
|
|
return null;
|
|
}
|
|
return {
|
|
source: "estimated",
|
|
used: estimateChatTokens(messages, streamingText),
|
|
total: fallbackContextWindow,
|
|
approximate: true,
|
|
percent: null,
|
|
};
|
|
}
|