OpenAI-protocol proxy for context debugging, traffic capture, and safe mocking.
">https://img.shields.io/badge/Node-%3E%3D22-339933?logo=node.js&logoColor=white">
ModelBox sits between your agent and model provider so you can inspect what is actually sent to the model.
- Capture full request/response payloads as JSONL with
traceId - Switch between
mockandpassthroughwithout restarting - Keep OpenAI-compatible clients unchanged (
/v1/responses,/v1/chat/completions) - Debug context safely without polluting upstream model behavior
| Capability | Description |
|---|---|
| OpenAI-compatible endpoints | POST /v1/responses, POST /v1/chat/completions, GET /v1/models |
| Runtime control | GET/POST /admin/state to switch mode, capture, upstream |
| Structured logs | JSONL records for request/response with digest and summary |
| Mock mode | Returns deterministic DEBUG_CONTEXT_SUMMARY {...} output |
| Passthrough mode | Relays traffic to your real upstream model provider |
flowchart LR
A[Agent / App] -->|OpenAI API| B[ModelBox]
B -->|passthrough| C[Upstream Provider]
B -->|JSONL capture| D[(logs/modelbox.jsonl)]
E[Admin API] -->|/admin/state| B
MODELBOX_MODE=mock npm startDefault bind: 127.0.0.1:8787.
MODELBOX_MODE=passthrough \
MODELBOX_UPSTREAM_BASE_URL=https://api.openai.com \
MODELBOX_UPSTREAM_API_KEY="$OPENAI_API_KEY" \
npm startNote: MODELBOX_UPSTREAM_BASE_URL should be provider root URL (for OpenAI use https://api.openai.com, not /v1).
Gemini's OpenAI-compatible endpoint usually expects /chat/completions (without local /v1 prefix).
Use MODELBOX_UPSTREAM_STRIP_PREFIX=/v1 so ModelBox rewrites /v1/chat/completions to /chat/completions upstream.
MODELBOX_MODE=passthrough \
MODELBOX_UPSTREAM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai \
MODELBOX_UPSTREAM_API_KEY="$GEMINI_API_KEY" \
MODELBOX_UPSTREAM_STRIP_PREFIX=/v1 \
npm startopenclaw config set models.providers.modelbox --json '{
"baseUrl": "http://127.0.0.1:8787/v1",
"api": "openai-responses",
"apiKey": "modelbox-local",
"models": [
{
"id": "debug-model",
"name": "debug-model",
"reasoning": false,
"input": ["text", "image"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 1000000,
"maxTokens": 131072
}
]
}'/model modelbox/debug-model
# or
/new modelbox/debug-modelIf you use agents.defaults.models allowlist, include modelbox/debug-model there so /model and session overrides can use it.
- Set base URL to
http://127.0.0.1:8787/v1 - Keep your OpenAI-compatible SDK/client unchanged
- Use
MODELBOX_MODE=mockfor local context debugging - Use
MODELBOX_MODE=passthroughfor transparent relay
curl -s http://127.0.0.1:8787/admin/statecurl -s -X POST http://127.0.0.1:8787/admin/state \
-H 'Content-Type: application/json' \
-d '{
"mode": "passthrough",
"capture": true,
"upstreamBaseUrl": "https://api.openai.com",
"upstreamStripPrefix": "",
"maxCaptureBytes": 4194304
}'If MODELBOX_ADMIN_TOKEN is configured, pass:
-H 'Authorization: Bearer <token>'| Variable | Default | Description |
|---|---|---|
MODELBOX_BIND |
127.0.0.1 |
Bind address |
MODELBOX_PORT |
8787 |
Listen port |
MODELBOX_MODE |
passthrough |
mock or passthrough |
MODELBOX_CAPTURE |
true |
Enable JSONL capture |
MODELBOX_LOG_FILE |
./logs/modelbox.jsonl |
Log output file |
MODELBOX_MAX_CAPTURE_BYTES |
2097152 |
Max captured response bytes |
MODELBOX_UPSTREAM_BASE_URL |
empty | Upstream base URL (required in passthrough) |
MODELBOX_UPSTREAM_API_KEY |
empty | Optional upstream API key override |
MODELBOX_UPSTREAM_STRIP_PREFIX |
empty | Optional path prefix stripped before forwarding (for example /v1) |
MODELBOX_ADMIN_TOKEN |
empty | Optional admin API token |
Capture logs are written to MODELBOX_LOG_FILE (default ./logs/modelbox.jsonl), resolved relative to the process working directory.
Backward compatibility: legacy SIDECAR_* variables are still accepted.
Each JSONL line includes key fields such as:
traceIddirection(requestorresponse)mode(mockorpassthrough)path,method,statussummary(messageCount,roles,toolsCount,imagesCount,promptChars,promptTokensApprox)bodyandbodySha256
Mock output text is intentionally compact:
DEBUG_CONTEXT_SUMMARY {...}
Use the built-in analyzer to split a captured request into major prompt blocks and estimate token cost per block.
npm run analyze:prompt -- --file logs/modelbox.jsonlUseful options:
--traceId <id>: analyze one trace directly--index <n>: pick a request record by index (-1= latest)--json: machine-readable output