How to Use the Dev Capture Feature in DS2API for Debugging
Enable the dev capture feature by setting DS2API_DEV_PACKET_CAPTURE=true, wrap upstream HTTP responses using session.WrapBody(), and inspect the full request/response pairs via GET /admin/dev/captures.
The dev capture feature in DS2API provides a development-packet-capture system that records HTTP request/response pairs for downstream API calls, making it invaluable for debugging complex LLM integrations like Deepseek and Claude. This article explains how to use the dev capture feature in DS2API for debugging, based on the actual implementation in the CJackHwang/ds2api repository.
What Is the Dev Capture Feature?
DS2API’s dev capture system uses an in-memory circular buffer to store Entry logs for every HTTP transaction you choose to instrument. According to the source code in [internal/devcapture/store.go](https://github.com/CJackHwang/ds2api/blob/main/internal/devcapture/store.go), the system consists of three core components:
devcapture.Store– Manages the circular buffer, enforces entry limits, and provides snapshot/clear APIsSession.WrapBody– Intercepts response streams, buffers data up tomaxBodyBytes, and automatically records entries when the body is fully consumed- Admin HTTP routes – Expose capture data via REST endpoints defined in [
internal/httpapi/admin/devcapture/routes.go](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/devcapture/routes.go) and [handler_dev_capture.go](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/devcapture/handler_dev_capture.go)
The Deepseek client demonstrates real-world usage in [deepseek/client/client_completion.go](https://github.com/CJackHwang/ds2api/blob/main/internal/deepseek/client/client_completion.go), where response bodies are automatically wrapped for capture.
Enabling Dev Capture via Environment Variables
Configure the feature using these environment variables before starting the DS2API server:
| Variable | Description | Default |
|---|---|---|
DS2API_DEV_PACKET_CAPTURE |
Master toggle (true/false) |
true (disabled on Vercel) |
DS2API_DEV_PACKET_CAPTURE_LIMIT |
Maximum entries stored (hard cap at 50) | 20 |
DS2API_DEV_PACKET_CAPTURE_MAX_BODY_BYTES |
Maximum bytes retained per response | 5 MiB |
Vercel Detection: If VERCEL or NOW_REGION environment variables are detected, capture defaults to disabled regardless of the toggle setting.
# Example configuration
export DS2API_DEV_PACKET_CAPTURE=true
export DS2API_DEV_PACKET_CAPTURE_LIMIT=30
export DS2API_DEV_PACKET_CAPTURE_MAX_BODY_BYTES=1048576
How Dev Capture Works: Technical Implementation
Understanding the internal flow helps you implement custom captures correctly.
Store Initialization
On first use, devcapture.Global() creates a singleton Store via NewFromEnv(). This reads the environment variables and initializes the circular buffer.
Starting a Capture Session
Before dispatching an upstream HTTP request, call store.Start(label, url, accountID, requestPayload). This returns a *Session containing metadata:
store := devcapture.Global()
sess := store.Start(
"deepseek-chat-call", // label for identification
"https://api.deepseek.com/v1/chat/completions",
"", // optional account ID
requestPayload, // map[string]any of the request body
)
Wrapping Response Bodies
Pass the upstream response body to session.WrapBody(resp.Body, resp.StatusCode) before reading it. The wrapper streams data while buffering up to maxBodyBytes:
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
// Critical: wrap before reading
resp.Body = sess.WrapBody(resp.Body, resp.StatusCode)
// Consume as normal - capture happens transparently
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
When the wrapper encounters io.EOF or is closed, it creates a devcapture.Entry containing request metadata, HTTP status, the full or truncated response body, and a ResponseTruncated boolean flag.
Retrieving Captured Data
Admin routes expose the captured entries as JSON. Access them via:
curl http://localhost:8080/admin/dev/captures
Practical Usage Examples
Automatic Capture with Built-in Clients
The Deepseek client automatically wraps response bodies. Any call to the Deepseek API through DS2API’s internal client is captured without additional code. The implementation in [client_completion.go](https://github.com/CJackHwang/ds2api/blob/main/internal/deepseek/client/client_completion.go) calls captureSession.WrapBody immediately after receiving the HTTP response.
Manual Capture via Admin API
For arbitrary testing without built-in client integration, use the raw-sample capture endpoint defined in [internal/httpapi/admin/rawsamples/handler_raw_samples.go](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/rawsamples/handler_raw_samples.go):
curl -X POST http://localhost:8080/admin/dev/raw-samples/capture \
-H "Content-Type: application/json" \
-d '{
"label": "debug-test",
"url": "https://api.example.com/v1/data",
"method": "POST",
"headers": {"Authorization": "Bearer token"},
"body": {"key": "value"}
}'
Programmatic Implementation in Go
Here is a complete example demonstrating manual session management:
package main
import (
"io"
"net/http"
"ds2api/internal/devcapture"
)
func makeCapturedRequest() {
// Initialize from environment variables
store := devcapture.Global()
if !store.Enabled() {
return
}
// Start session with request metadata
sess := store.Start(
"manual-debug",
"https://api.deepseek.com/v1/chat/completions",
"account-123",
map[string]any{
"model": "deepseek-chat",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
},
)
if sess == nil {
return
}
// Execute request
resp, err := http.Post(
"https://api.deepseek.com/v1/chat/completions",
"application/json",
requestBody,
)
if err != nil {
panic(err)
}
// Wrap response to enable capture
resp.Body = sess.WrapBody(resp.Body, resp.StatusCode)
// Reading the body triggers the capture
io.ReadAll(resp.Body)
resp.Body.Close()
// Verify capture
captures := store.Snapshot()
}
Viewing and Clearing Capture Logs
View all captures:
curl http://localhost:8080/admin/dev/captures
Sample response:
{
"enabled": true,
"limit": 30,
"max_body_bytes": 1048576,
"items": [
{
"id": "cap_5e2b1ff8-a1c2",
"created_at": 1714132123,
"label": "deepseek-chat-call",
"url": "https://api.deepseek.com/v1/chat/completions",
"account_id": "account-123",
"status_code": 200,
"request_body": "{\"model\":\"deepseek-chat\"}",
"response_body": "{\"choices\":[...]}",
"response_truncated": false
}
]
}
Clear the capture log:
curl -X DELETE http://localhost:8080/admin/dev/captures
Response:
{"success": true, "detail": "capture logs cleared"}
Summary
- Enable the feature via
DS2API_DEV_PACKET_CAPTURE=trueenvironment variable (auto-disabled on Vercel) - Initialize the store using
devcapture.Global()which reads configuration from environment variables - Capture HTTP traffic by calling
store.Start()before the request andsession.WrapBody()on the response body - Inspect captured data through the admin endpoint
GET /admin/dev/capturesor programmatically viastore.Snapshot() - Clear logs using
DELETE /admin/dev/capturesto free memory - Reference the implementation in
internal/devcapture/store.goand usage examples indeepseek/client/client_completion.go
Frequently Asked Questions
How do I know if a response body was truncated in the capture?
Each capture entry includes a boolean field response_truncated that is set to true when the response body exceeds the DS2API_DEV_PACKET_CAPTURE_MAX_BODY_BYTES limit. As implemented in [store.go](https://github.com/CJackHwang/ds2api/blob/main/internal/devcapture/store.go), the captureBody wrapper silently truncates data beyond this limit while allowing the full stream to pass through to your application.
Can I use dev capture in production environments?
While technically possible by setting the environment variables, the capture system stores full request and response bodies in memory with no encryption or persistence to disk. According to the source code, this is designed for development debugging only. The circular buffer has a hard limit of 50 entries, but sensitive data (API keys, user content) remains in memory until cleared or until the server restarts.
Why is dev capture disabled on Vercel deployments?
The code explicitly checks for VERCEL or NOW_REGION environment variables and disables capture regardless of the DS2API_DEV_PACKET_CAPTURE setting. This prevents memory accumulation issues on serverless platforms where instance longevity is unpredictable and memory constraints are strictly enforced.
How do I capture traffic from custom HTTP clients not built into DS2API?
Wrap the response body using the Session.WrapBody() method pattern shown in [client_completion.go](https://github.com/CJackHwang/ds2api/blob/main/internal/deepseek/client/client_completion.go). You must import ds2api/internal/devcapture, obtain a session via store.Start(), and replace resp.Body with the wrapped version before reading. The capture triggers automatically when the body is fully consumed or closed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →