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:

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=true environment 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 and session.WrapBody() on the response body
  • Inspect captured data through the admin endpoint GET /admin/dev/captures or programmatically via store.Snapshot()
  • Clear logs using DELETE /admin/dev/captures to free memory
  • Reference the implementation in internal/devcapture/store.go and usage examples in deepseek/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →