MoonshotAI / MoonshotAI/kimi-cli
feat(vis): Reference implementation — capturing and visualizing raw Claude API requests/responses
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 11.4k
- Forks
- 1.3k
- Avg merge
- 9h 47m
- Merged PRs (30d)
- 2
Description
Reference Implementation: Capturing and Visualizing Raw Claude API Requests/Responses
Related to #2339
Background
Issue #2339 identified that the vis module lacks the ability to show the raw API request/response sent to the LLM provider. We have built a tool called claude-tap-plus that solves exactly this problem for Claude Code, by intercepting API calls at the proxy level. This issue documents our approach as a reference implementation that could be integrated into kimi-cli's vis.
How claude-tap-plus captures the data
claude-tap-plus is a reverse proxy that sits between Claude Code and the Anthropic API. Instead of building event-by-event wire protocol capture, it takes a simpler approach: intercept the full HTTP request/response and record them as-is.
Data structure — AnthropicTraceRecord
Each API call is recorded as a single JSONL line:
{
"timestamp": "2026-05-21T10:00:00Z",
"request_id": "req_xxx",
"session_id": "uuid-from-metadata",
"turn": 3,
"duration_ms": 4500,
"request": {
"model": "claude-sonnet-4-20250514",
"system": "...full system prompt text...",
"messages": [
{"role": "user", "content": "..."},
{"role": "assistant", "content": "...", "tool_calls": [...]},
{"role": "tool", "content": "...", "tool_call_id": "..."},
{"role": "user", "content": "..."}
],
"tools": [
{
"name": "Bash",
"description": "Executes a bash command...",
"input_schema": { ... }
}
],
"temperature": 1.0,
"max_tokens": 16384,
"stream": true,
"metadata": {
"user_id": "{\"session_id\": \"uuid-xxxx\"}"
}
},
"response": {
"id": "msg_xxx",
"model": "claude-sonnet-4-20250514",
"stop_reason": "tool_use",
"usage": {
"input_tokens": 15000,
"output_tokens": 500,
"cache_creation_input_tokens": 12000,
"cache_read_input_tokens": 0
},
"content": [
{"type": "text", "text": "I will read that file..."},
{"type": "tool_use", "id": "toolu_xxx", "name": "ReadFile", "input": {"path": "/src/main.go"}}
]
}
}
This is exactly the data that kimi-cli vis is missing — the complete request body (model, system, messages, tools, parameters) and the complete response body (model, stop_reason, content, usage).
Session ID extraction
Session IDs are not generated by the proxy — they are extracted from the request body:
request.metadata.user_id → JSON parse → session_id field
This is the same session ID that Claude Code uses internally, so trace records can be correlated with kimi-cli's own session data.
Storage format
{executable_dir}/.traces/{project_name}/2026-05-20_100000_a3f2c1.jsonl
- One JSONL file per proxy session
- Project name derived from
git remoteURL, fallback to directory name - Thread-safe via
sync.Mutex - Each line is a complete request/response pair, filterable by
session_id
What this gives you that kimi-cli vis lacks
| Data point | kimi-cli vis (current) | claude-tap-plus |
|---|---|---|
| Full system prompt (as sent to API) | Stored in context.jsonl as _system_prompt, rendered as plain text |
Captured verbatim in request.system |
| Assembled messages array | Not captured (only individual context messages) | Full request.messages array |
| Tool definitions (JSON schema) | Not captured in wire events | Full request.tools array |
| Model name per request | Not in wire events | request.model + response.model |
| Temperature / max_tokens / top_p | Not captured | request.temperature, request.max_tokens |
| Stop reason | Not captured | response.stop_reason |
| Token usage per request | Partially in StatusUpdate | Full response.usage with cache breakdown |
| Request latency | Not captured | duration_ms per request |
How this could be integrated into kimi-cli vis
There are two possible integration paths:
Option A: Add APIRequest/APIResponse wire events (recommended in #2339)
Add new wire event types in src/kimi_cli/wire/types.py and emit them from src/kimi_cli/soul/kimisoul.py before/after the kosong.ChatProvider.step() call. This keeps everything in kimi-cli's existing infrastructure.
Option B: Record full request/response alongside wire events
Add a separate JSONL file (e.g., api_traces.jsonl) in the session directory, recording the full request/response in the same format as claude-tap-plus. The vis backend API (src/kimi_cli/vis/api/sessions.py) would get a new endpoint to serve this data. This avoids bloating wire.jsonl with potentially large payloads.
Frontend viewer design
Regardless of backend choice, the frontend needs a new view (e.g., "API Traces" tab) that renders:
- Request card — model, parameters (temperature, max_tokens), latency badge
- System prompt — rendered with markdown + syntax highlighting, with token count and collapsible sections
- Messages timeline — each message in the
request.messagesarray rendered as:- User messages: blue bubble with avatar (reuse
UserMessagepattern) - Assistant messages: with text content + thinking blocks + tool calls (reuse
AssistantMessagepattern) - Tool results: indented with terminal icon (reuse
ToolMessagepattern)
- User messages: blue bubble with avatar (reuse
- Tool definitions panel — collapsible list of all tools with name, description, input schema
- Response card — stop reason badge, token usage breakdown (input/cache/output), duration
- Turn diff — highlight what changed between consecutive requests (new messages added, compaction occurred, etc.)
This can reuse most of the existing component patterns from kimi-cli's vis:
wire-event-card.tsx:TYPE_COLORSfor type badge coloringwire-event-card.tsx:ExpandedPayloadfor JSON viewercontext-viewer/assistant-message.tsxlayout for message renderingcontext-viewer/context-viewer.tsx:SystemPromptRowas base for enhanced system prompt viewerstate-viewer/state-viewer.tsx:JsonValuefor recursive JSON tree rendering
Summary
claude-tap-plus demonstrates that capturing the full API request/response at the proxy/provider level is straightforward and provides exactly the debugging visibility that users need. The data structure (AnthropicTraceRecord) could serve as a reference for how to structure the wire events or storage format in kimi-cli.
The key insight is: recording one trace record per API call (with full request + response) is simpler and more useful than trying to reconstruct the API call from individual wire events.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading src/kimi_cli/wire/types.py and src/kimi_cli/soul/kimisoul.py around the kosong.ChatProvider.step() call, then inspect the vis API at src/kimi_cli/vis/api/sessions.py. Review the referenced frontend components, including wire-event-card.tsx, context-viewer/assistant-message.tsx, context-viewer/context-viewer.tsx, and state-viewer/state-viewer.tsx. Done means an agreed capture and storage approach plus an API Traces view showing the specified request, response, and message details.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, typescript
- Domain
- api, frontend, full-stack
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100