MoonshotAI / MoonshotAI/kimi-cli

feat(vis): Reference implementation — capturing and visualizing raw Claude API requests/responses

Open
#2,340 0 comments 0 reactions 0 assignees View on GitHub

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 remote URL, 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:

  1. Request card — model, parameters (temperature, max_tokens), latency badge
  2. System prompt — rendered with markdown + syntax highlighting, with token count and collapsible sections
  3. Messages timeline — each message in the request.messages array rendered as:
    • User messages: blue bubble with avatar (reuse UserMessage pattern)
    • Assistant messages: with text content + thinking blocks + tool calls (reuse AssistantMessage pattern)
    • Tool results: indented with terminal icon (reuse ToolMessage pattern)
  4. Tool definitions panel — collapsible list of all tools with name, description, input schema
  5. Response card — stop reason badge, token usage breakdown (input/cache/output), duration
  6. 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_COLORS for type badge coloring
  • wire-event-card.tsx:ExpandedPayload for JSON viewer
  • context-viewer/assistant-message.tsx layout for message rendering
  • context-viewer/context-viewer.tsx:SystemPromptRow as base for enhanced system prompt viewer
  • state-viewer/state-viewer.tsx:JsonValue for 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.