()
| 376 | } |
| 377 | |
| 378 | public String getContextStatus() { |
| 379 | com.tcode.context.ContextProfile profile = memoryManager.getContextProfile(); |
| 380 | int window = profile.maxContextWindow(); |
| 381 | int triggerTokens = profile.compressionTriggerTokens(); |
| 382 | |
| 383 | // 分类估算 token 占用 |
| 384 | int systemTokens = 0, userTokens = 0, assistantTokens = 0, toolTokens = 0; |
| 385 | int systemCount = 0, userCount = 0, assistantCount = 0, toolCount = 0; |
| 386 | for (LlmClient.Message msg : conversationHistory) { |
| 387 | int t = com.tcode.memory.TokenBudget.estimateMessagesTokens(java.util.List.of(msg)); |
| 388 | switch (msg.role()) { |
| 389 | case "system" -> { systemTokens += t; systemCount++; } |
| 390 | case "user" -> { userTokens += t; userCount++; } |
| 391 | case "assistant" -> { assistantTokens += t; assistantCount++; } |
| 392 | case "tool" -> { toolTokens += t; toolCount++; } |
| 393 | } |
| 394 | } |
| 395 | int messagesTokens = userTokens + assistantTokens + toolTokens; |
| 396 | int toolsSchemaTokens = estimateToolsSchemaTokens(); |
| 397 | int total = systemTokens + messagesTokens + toolsSchemaTokens; |
| 398 | double ratio = window > 0 ? (double) total / window : 0; |
| 399 | int triggerRemaining = Math.max(0, triggerTokens - total); |
| 400 | |
| 401 | StringBuilder sb = new StringBuilder(); |
| 402 | sb.append(String.format("📊 Context Usage %s window: %s%n", |
| 403 | modelLabel(), formatTokens(window))); |
| 404 | sb.append("\n ").append(progressBar(ratio, 30)) |
| 405 | .append(String.format(" %d%% (%s / %s)%n", |
| 406 | (int) Math.round(ratio * 100), formatTokens(total), formatTokens(window))); |
| 407 | sb.append("\n 当前占用细分:\n"); |
| 408 | sb.append(formatLine("System prompt", systemTokens, window, systemCount)); |
| 409 | sb.append(formatLine("Tools schema", toolsSchemaTokens, window, -1)); |
| 410 | sb.append(formatLine("Conversation", messagesTokens, window, |
| 411 | userCount + assistantCount + toolCount)); |
| 412 | sb.append(" ─────────────────────────────────\n"); |
| 413 | sb.append(String.format(" 合计: %8s (%4.1f%%)%n", |
| 414 | formatTokens(total), ratio * 100)); |
| 415 | sb.append(String.format("%n 压缩阈值: %s (%d%%) 距压缩还有: %s%n", |
| 416 | formatTokens(triggerTokens), |
| 417 | (int) (profile.compressionTriggerRatio() * 100), |
| 418 | formatTokens(triggerRemaining))); |
| 419 | sb.append(" MCP resources 自动索引: ") |
| 420 | .append(profile.mcpResourceIndexEnabled() ? "开启" : "关闭(window 不足 32k)") |
| 421 | .append("\n"); |
| 422 | sb.append(" prompt cache: ").append(profile.promptCacheMode()).append("\n"); |
| 423 | sb.append("\n"); |
| 424 | sb.append(memoryManager.getSystemStatus()); |
| 425 | return sb.toString(); |
| 426 | } |
| 427 | |
| 428 | private String modelLabel() { |
| 429 | if (llmClient == null) return "(no model)"; |
no test coverage detected