{"repo": "chi-cat/deepseek-responses-api", "files": {"README_EN.md": {"1a7c8f561f119e84ed5172f831013fec51bf5dd8": {"t": 1786068469.127762, "n": 12782, "h": "5bee4d17019581ce", "c": "# deepseek-responses-api\n\n[中文文档](README.md)\n\nA Go proxy for OpenAI-compatible models. It converts client requests in the [OpenAI Responses API](https://platform.openai.com/docs/api-reference/responses) format into upstream Chat Completions requests, then wraps the upstream result back into the Responses API format.\n\nUse it when an existing Responses API client needs to work with DeepSeek or another OpenAI-compatible upstream.\n\n## Core capabilities\n\n- **Responses API adaptation**: creation, streaming, persisted-response retrieval, deletion, background cancellation, and input-item listing.\n- **Compatible-upstream proxying**: converts Responses requests to `/chat/completions`; also exposes transparent Chat Completions and Models proxies.\n- **Multi-backend routing**: choose a backend with an optional path prefix such as `/deepseek/v1/responses`; every backend has its own client and upstream API keys.\n- **Context and background jobs**: SQLite-backed responses, continuation with `previous_response_id`, asynchronous execution with `background: true`, and cancellation.\n- **Reasoning and structured output**: maps `reasoning` parameters and upstream `reasoning_content`; supports JSON object / JSON schema response formats and common sampling parameters.\n- **Tool calls**: converts function tools, namespace tools, and `tool_choice`. Built-in `web_search` is executed in an automatic retrieval loop; ordinary function calls are returned for the client to execute.\n- **Web search**: SearXNG search, optionally enriched with Jina Reader page fetching, cleaning, and truncation.\n- **Token estimation**: estimates tokens for the current input locally, without calling or charging the upstream model.\n- **Upstream adaptation controls**: extra Headers / Body fields, allow-listed client overrides, and system-prompt or tool-definition rewrites.\n\n## Supported endpoints\n\nAll endpoints except the health check require `Authorization: Bearer `. `{backend}` is an optional prefix; without it, the service uses `backend_default`.\n\n| Method | Path | Description |\n| --- | --- | --- |\n| `GET` | `/healthz` | Unauthenticated health check |\n| `GET` | `/{backend}/v1/models` | Proxy the upstream model list |\n| `POST` | `/{backend}/v1/chat/completions` | Chat Completions proxy with streaming passthrough |\n| `POST` | `/{backend}/v1/responses` | Create a Response in normal, streaming, or background mode |\n| `POST` | `/{backend}/v1/responses/input_tokens` | Estimate tokens in the current input |\n| `GET` | `/{backend}/v1/responses/{id}` | Retrieve a stored Response |\n| `POST` | `/{backend}/v1/responses/{id}/cancel` | Cancel an in-progress background Response |\n| `DELETE` | `/{backend}/v1/responses/{id}` | Delete a stored Response |\n| `GET` | `/{backend}/v1/responses/{id}/input_items` | Page through stored input items |\n\nFor example:\n\n```text\nPOST /v1/responses # default backend\nPOST /deepseek/v1/responses # backend whose id is deepseek\nGET /openai/v1/models # backend whose id is openai\n```\n\nThis is not a complete implementation of the OpenAI Responses API. For example, `/v1/responses/compact` and conversation-management endpoints are not routed. Treat the table above as the supported surface.\n\n## Quick start\n\n### Prerequisites\n\n- Go 1.26 or later (see [go.mod](go.mod))\n- An upstream service compatible with the OpenAI Chat Completions API\n\n### 1. Create configuration\n\n```bash\ncp config.example.toml config.toml\ncp .env.example .env\n```\n\nEdit `config.toml` for the listening port, database location, and backend; put the upstream secret in `.env`. Here is a minimal configuration:\n\n```toml\n[server]\nport = \"8080\"\ndb_path = \"responses.db\"\n\ndefault_model = \"deepseek-chat\"\nmax_tool_hops = 6\nbackend_default = \"deepseek\"\n\n[[backends]]\nid = \"deepseek\"\n# This must be the upstream API's /v1 base URL. The service appends /chat/completions.\nurl = \"https://api.deepseek.com/v1\"\n# Client keys accepted by this proxy. More than one key is allowed.\nkeys = [\"sk-client-key\"]\n# Key used by this proxy to call the upstream service.\nupstream_key = \"${BACKEND_DEEPSEEK_UPSTREAM_KEY}\"\n```\n\n```dotenv\n# .env\nBACKEND_DEEPSEEK_UPSTREAM_KEY=sk-your-upstream-key\n```\n\n`upstream_key`, `searxng.token`, and `jina_reader.token` support `${VAR}` or `$VAR` environment-variable references. At startup, the service automatically loads `.env` from the working directory; the file is optional.\n\n### 2. Run\n\n```bash\ngo run .\n```\n\nThe default configuration path is `config.toml` in the working directory. To use another file:\n\n```bash\nCONFIG_PATH=/path/to/config.toml go run .\n```\n\nCheck the service:\n\n```bash\ncurl http://localhost:8080/healthz\n# {\"ok\":true}\n```\n\n### 3. Create your first Response\n\n```bash\ncurl http://localhost:8080/v1/responses \\\n -H 'Authorization: Bearer sk-client-key' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"model\": \"deepseek-chat\",\n \"input\": \"Introduce yourself in one sentence.\"\n }'\n```\n\n## Configuration reference\n\nSee [config.example.toml](config.example.toml) for every available field.\n\n### Service and defaults\n\n| Field | Description | Default |\n| --- | --- | --- |\n| `server.port` | Listen port | `8080` |\n| `server.db_path` | SQLite database path | `responses.db` |\n| `default_model` | Model used when a request omits `model` | `local-model` |\n| `max_tool_hops` | Maximum consecutive built-in Web Search tool rounds | `6` |\n| `backend_default` | Backend ID used without a path prefix | First backend |\n\nEvery `[[backends]]` entry must have an `id`, `url`, at least one `keys` item, and an `upstream_key`. `url` should be the upstream `/v1` base URL, for example `https://example.com/v1`.\n\n### Backend extensions and rewrites\n\nUse the optional configuration below when upstream compatibility needs adjustment:\n\n```toml\n[[backends]]\nid = \"custom\"\nurl = \"https://example.com/v1\"\nkeys = [\"sk-client-key\"]\nupstream_key = \"${CUSTOM_UPSTREAM_KEY}\"\n\n# Fixed headers and body fields added to upstream requests.\nheaders = { \"X-Provider-Feature\" = \"enabled\" }\nbody_extend = { \"extra_param\" = \"value\" }\n\n# Do not overwrite these fields if the client already supplied a value.\nclient_override_headers = [\"X-Provider-Feature\"]\nclient_override_body_fields = [\"extra_param\"]\n\n# Map Responses reasoning.effort to the upstream reasoning_effort.\n# \"-\" disables thinking.\neffort_map = { \"none\" = \"-\", \"low\" = \"medium\", \"high\" = \"max\" }\n\n[backends.rewrite.system_prompt]\nmode = \"append\" # prepend, append (default), or replace\ntext = \"Additional provider instructions.\"\n\n[backends.rewrite.tools.my_function]\ndescription = \"Replacement tool description\"\nparameters = { type = \"object\", properties = { query = { type = \"string\" } } }\n```\n\nRewrites apply both to converted Responses requests and directly proxied Chat Completions requests.\n\n### Web search\n\nDeclare `{\"type\":\"web_search\"}` (or a compatible alias) in a request's `tools` to enable built-in search. The service presents a function called `web_search` to the model. When the model invokes it, the service queries SearXNG, optionally fetches the top pages, and sends the resulting text back to the model for the next turn.\n\n```toml\n[websearch]\nprovider = \"searxng\"\ntop_k = 5\njina_fetch_n = 3\njina_max_content_len = 8000\njina_clean_content = true\njina_relevance_scoring = true\n# debug_dir = \"/tmp/websearch-debug\"\ndebug_retention_days = 7\n\n[searxng]\nurl = \"https://your-searxng.example.com\"\ntoken = \"${SEARXNG_TOKEN}\" # optional\n\n[jina_reader]\nurl = \"https://r.jina.ai\"\ntoken = \"${JINA_READER_TOKEN}\" # optional\n```\n\nWhen `websearch.debug_dir` is set, the service writes search debugging output there. Use a writable directory that does not contain sensitive data.\n\n## Common usage\n\n### Streaming\n\nSet `stream: true` in a Responses request to receive a Responses SSE event stream:\n\n```bash\ncurl -N http://localhost:8080/v1/responses \\\n -H 'Authorization: Bearer sk-client-key' \\\n -H 'Content-Type: application/json' \\\n -d '{\"model\":\"deepseek-chat\",\"input\":\"Write one line of poetry.\",\"stream\":true}'\n```\n\n`POST /v1/chat/completions` also supports `stream: true` and passes through upstream Chat Completions SSE data.\n\n### Multi-turn context and storage\n\nResponses use `store: true` by default. Send the ID returned by the previous request in the next request:\n\n```json\n{\n \"model\": \"deepseek-chat\",\n \"previous_response_id\": \"resp_...\",\n \"input\": \"Summarize the preceding answer in three points.\"\n}\n```\n\nWith `store: false`, the Response is not saved and cannot be used as `previous_response_id`. Retrieve a stored response with `GET /v1/responses/{id}`, or list saved input items with `GET /v1/responses/{id}/input_items?limit=20&order=desc`.\n\n### Background jobs and cancellation\n\n```bash\ncurl http://localhost:8080/v1/responses \\\n -H 'Authorization: Bearer sk-client-key' \\\n -H 'Content-Type: application/json' \\\n -d '{\"model\":\"deepseek-chat\",\"input\":\"Perform a long-running task.\",\"background\":true}'\n```\n\nThe create call immediately returns an `in_progress` Response. Poll `GET /v1/responses/{id}`. Only an in-progress response created with `background: true` can be cancelled through `POST /v1/responses/{id}/cancel`.\n\n### Tool calls\n\nOrdinary function tools are returned as Responses `function_call` output items. **This service does not run custom functions.** The client must run the function and include the corresponding `function_call_output` in the next request's `input`.\n\nBuilt-in `web_search` is the exception: it is run by the service and recorded as a `web_search_call` in the final output. Consecutive internal retrieval rounds are bounded by `max_tool_hops`.\n\n### Reasoning, structured output, and images\n\n- `reasoning.effort` and `reasoning.mode` are converted to upstream thinking / reasoning parameters; upstream `reasoning_content` becomes a Responses `reasoning` output item.\n- `text.format.type` values `json_object` and `json_schema` are converted to upstream `response_format`.\n- `temperature`, `top_p`, `max_output_tokens`, `frequency_penalty`, `presence_penalty`, `tool_choice`, `parallel_tool_calls`, and logprobs-related parameters are forwarded within the supported mapping.\n- Text inputs, `input_text`, and `input_image` items with a URL or data URL are converted to Chat Completions content. File inputs and other Responses content types do not have complete conversion support.\n\n### Input-token estimates\n\n```bash\ncurl http://localhost:8080/v1/responses/input_tokens \\\n -H 'Authorization: Bearer sk-client-key' \\\n -H 'Content-Type: application/json' \\\n -d '{\"input\":\"Estimate the tokens in this text.\"}'\n```\n\nThe response is `{\"object\":\"response.input_tokens\",\"input_tokens\":...}`. It is calculated with a local `o200k_base` approximation; it does not load `previous_response_id` history and can differ from the upstream's billed token count, especially for non-English text.\n\n## Docker deployment\n\n### Docker\n\n```bash\ndocker build -t deepseek-responses-api .\ndocker run --rm -p 8080:8080 \\\n -v \"$(pwd)/config.toml:/app/config.toml:ro\" \\\n -v \"$(pwd)/data:/app/data\" \\\n --env-file .env \\\n deepseek-responses-api\n```\n\nThe container reads `/app/config.toml` by default. To persist the database in the mounted directory, set this in that configuration file:\n\n```toml\n[server]\ndb_path = \"/app/data/responses.db\"\n```\n\nThe image's `PORT` and `DB_PATH` environment variables do not directly override TOML configuration. Control startup configuration with `config.toml` or `CONFIG_PATH`.\n\n### Docker Compose\n\n[docker-compose.yaml](docker-compose.yaml) starts the API with SearXNG:\n\n```bash\ndocker compose up --build\n```\n\nCreate and fill in `config.toml` and `.env` first, and set `server.db_path` to `/app/data/responses.db`. When using the Compose SearXNG service, set `searxng.url` to `http://searxng:8080`.\n\n## Development and verification\n\n```bash\ngo test ./...\n```\n\nThe repository also includes an integration script that uses real service credentials:\n\n```bash\nBASE_URL=http://localhost:8080 \\\nMODEL=deepseek-chat \\\nAPI_KEY=sk-client-key \\\nbash tests/integration_test.sh\n```\n\nThe integration script requires `curl` and `jq` and calls the configured upstream.\n\n## Project layout\n\n```text\nmain.go HTTP routing and service startup\ninternal/auth/ Client Bearer-key authentication and backend selection\ninternal/backend/ Upstream requests, extensions, and rewrite rules\ninternal/config/ TOML, .env, and configuration validation\ninternal/convert/ Responses ↔ Chat Completions conversion\ninternal/handler/ Response, SSE, proxy, and tool-call handlers\ninternal/store/ SQLite response storage\ninternal/tools/ SearXNG, Jina Reader, cleaning, and token estimation\ndocs/ API reference material and design documents\n```\n\n## License\n\nSee [LICENSE](LICENSE).\n"}}}}