diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-context.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-context.tsx index 2eed011c5..452e67411 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-context.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-context.tsx @@ -1,15 +1,14 @@ -import { createMemo, createSignal, For } from "solid-js" +import { createMemo, For } from "solid-js" +import { TextAttributes } from "@opentui/core" import { useTheme } from "../context/theme" import { useSync } from "@tui/context/sync" import { useLocal } from "@tui/context/local" import type { AssistantMessage } from "@opencode-ai/sdk/v2" interface ContextItem { - type: "system" | "instruction" | "file" | "message" + type: "system" | "instruction" | "message" name: string tokens: number - content?: string - enabled: boolean } export function DialogContext(props: { sessionID: string }) { @@ -19,160 +18,126 @@ export function DialogContext(props: { sessionID: string }) { const messages = createMemo(() => sync.data.message[props.sessionID] ?? []) - // Calculate context breakdown - const contextItems = createMemo((): ContextItem[] => { - const items: ContextItem[] = [] + const contextBreakdown = createMemo(() => { + const breakdown: ContextItem[] = [] - // Get last assistant message for token info + // Get last assistant message for actual token info const lastAssistant = messages().findLast( (x) => x.role === "assistant" && (x as AssistantMessage).tokens?.output > 0 ) as AssistantMessage | undefined - // System prompt estimate (varies by provider) const model = local.model.current() + + // System prompt (varies by provider) if (model) { - items.push({ + breakdown.push({ type: "system", - name: `System Prompt (${model.providerID})`, - tokens: 8000, // Approximate - enabled: true, + name: `System prompt (${model.providerID})`, + tokens: lastAssistant?.tokens?.cache?.write ?? 8000, }) } - // Environment context - items.push({ + // Environment & file tree + breakdown.push({ type: "system", - name: "Environment & File Tree", - tokens: 2000, // Approximate - enabled: true, + name: "Environment & file tree", + tokens: 2000, }) - // Instructions files - const instructionFiles = [ - "~/context/PROMPT_TEMPLATE.md", - "~/context/README.md", - "AGENTS.md", - "CLAUDE.md", - ] - for (const file of instructionFiles) { - items.push({ - type: "instruction", - name: file, - tokens: 1500, // Approximate per file - enabled: true, - }) - } + // Instructions from config + breakdown.push({ + type: "instruction", + name: "Custom instructions", + tokens: 4000, + }) - // Session messages - let messageTokens = 0 + // Conversation history + let conversationTokens = 0 for (const msg of messages()) { if (msg.role === "assistant") { const assistant = msg as AssistantMessage - messageTokens += assistant.tokens?.input ?? 0 - messageTokens += assistant.tokens?.output ?? 0 + conversationTokens += assistant.tokens?.input ?? 0 } } - if (messageTokens > 0) { - items.push({ + + if (conversationTokens > 0) { + breakdown.push({ type: "message", - name: `Conversation History (${messages().length} messages)`, - tokens: messageTokens, - enabled: true, + name: `Messages (${messages().length} total)`, + tokens: conversationTokens, }) } - return items + return breakdown }) const totalTokens = createMemo(() => { - return contextItems().reduce((sum, item) => sum + (item.enabled ? item.tokens : 0), 0) + return contextBreakdown().reduce((sum, item) => sum + item.tokens, 0) }) - const [filter, setFilter] = createSignal("") - - const filteredItems = createMemo(() => { - const f = filter().toLowerCase() - if (!f) return contextItems() - return contextItems().filter((item) => item.name.toLowerCase().includes(f)) + const context = createMemo(() => { + const last = messages().findLast( + (x) => x.role === "assistant" && (x as AssistantMessage).tokens?.output > 0 + ) as AssistantMessage | undefined + if (!last) return undefined + const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID] + return { + tokens: totalTokens(), + percentage: model?.limit.context ? Math.round((totalTokens() / model.limit.context) * 100) : null, + } }) return ( - + + + Context Preview + + esc + + + - Context Preview + Total Context - Total: {totalTokens().toLocaleString()} tokens + {context()?.tokens.toLocaleString() ?? "0"} tokens + {context()?.percentage ? ` (${context()?.percentage}% of limit)` : ""} - - Filter: - {filter() || "(type to filter)"} - - - - - - - - {(item) => ( - - - - {item.type === "system" - ? "[SYS]" + + + Breakdown + + + {(item) => ( + + + - - {item.name} - - - - {item.tokens.toLocaleString()} tokens + ? theme.warning + : theme.primary + } + > + • + {item.name} - )} - - - + {item.tokens.toLocaleString()} + + )} + + - - - + - [SYS] System | [INS] Instructions | [FILE] Files | [MSG] Messages + This is an estimate of the context sent to the model. Actual token count may vary based on tokenization. - Press Esc to close )