Metadata-Version: 2.4
Name: ds4codex
Version: 0.1.6
Summary: Responses API to Chat Completions proxy for Codex-compatible model providers.
Author: Yuan Zan
Author-email: yfinddream@gmail.com
Requires-Python: >=3.10,<4.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: aiohttp (>=3.9.5,<4.0.0)
Requires-Dist: click (>=8.1.7,<9.0.0)
Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
Description-Content-Type: text/markdown

# ds4codex

`ds4codex` lets Codex use DeepSeek models through a small local proxy.

Codex talks to custom providers with the Responses API shape. DeepSeek exposes a Chat Completions compatible API. `ds4codex` sits between them and translates requests, responses, streaming events, tool calls, and thinking-mode options.

The package keeps the user-facing setup intentionally small:

- one human-edited config file: `~/.codex/config.toml`
- one generated model catalog for Codex `/model`: `~/.codex/ds4codex-model-catalog.json`
- two CLI commands: `ds4codex init` and `ds4codex run`

## Install

Install from PyPI:

```bash
pip install ds4codex -i https://pypi.org/simple
```

For local development from this repository:

```bash
pip install -e .
```

## Quick Start

Initialize Codex and write your DeepSeek API key into the managed provider block:

```bash
ds4codex init --apikey sk-your-deepseek-api-key
```

Start the local proxy:

```bash
ds4codex run
```

Keep `ds4codex run` running while using Codex. Codex will connect to `http://127.0.0.1:8099/v1`.

After initialization, start Codex and use `/model` to choose:

- `DeepSeek V4 Flash`
- `DeepSeek V4 Pro`

## What `init` Does

`ds4codex init` writes managed blocks into `~/.codex/config.toml` and generates the model catalog JSON used by Codex `/model`.

Generated file:

```text
~/.codex/ds4codex-model-catalog.json
```

Codex config blocks:

```toml
# BEGIN DS4CODEX ROOT
model = "deepseek-v4-flash"
model_provider = "ds4codex"
model_context_window = 1048576
model_catalog_json = "/home/you/.codex/ds4codex-model-catalog.json"
# END DS4CODEX ROOT

# BEGIN DS4CODEX SETTINGS
[ds4codex]
port = 8099
target_url = "https://api.deepseek.com/v1/chat/completions"
thinking = "disabled"
# END DS4CODEX SETTINGS

# BEGIN DS4CODEX PROVIDER
[model_providers.ds4codex]
name = "DeepSeek via ds4codex"
base_url = "http://127.0.0.1:8099/v1"
wire_api = "responses"
experimental_bearer_token = "sk-your-deepseek-api-key"
# END DS4CODEX PROVIDER
```

If `~/.codex/config.toml` already exists, `init` preserves existing user config and only adds or refreshes the managed ds4codex blocks.

Useful options:

```bash
ds4codex init --port 9123
ds4codex init --apikey sk-your-deepseek-api-key
ds4codex init --force
ds4codex init --config-path /path/to/config.toml
ds4codex init --model-catalog-path /path/to/ds4codex-model-catalog.json
```

## Model Catalog

The model catalog is required for a good Codex `/model` experience. It is a JSON file, not a directory.

`ds4codex init` points Codex at the catalog through:

```toml
model_catalog_json = "/home/you/.codex/ds4codex-model-catalog.json"
```

The catalog makes Codex aware of the two DeepSeek entries:

- `deepseek-v4-flash`
- `deepseek-v4-pro`

It also advertises Codex reasoning choices:

- `low`
- `medium`
- `high`
- `xhigh`

`ds4codex` first tries to reuse Codex's bundled model schema when the local `codex` executable is available. If `codex` is missing, not executable, or fails, `ds4codex` uses its own built-in template. This means `ds4codex init` does not require a working local `codex` binary.

## Runtime Settings

The proxy reads runtime settings from the `[ds4codex]` section inside `~/.codex/config.toml`:

```toml
[ds4codex]
port = 8099
target_url = "https://api.deepseek.com/v1/chat/completions"
thinking = "disabled"
```

`~/.config/ds4codex/config.toml` is not used.

Runtime values can also be overridden from the CLI:

```bash
ds4codex run --port 8099
ds4codex run --target-url https://api.deepseek.com/v1/chat/completions
ds4codex run --thinking high
```

or with environment variables:

```bash
DS4CODEX_PORT=8099 ds4codex run
DS4CODEX_TARGET_URL=https://api.deepseek.com/v1/chat/completions ds4codex run
DS4CODEX_THINKING=high ds4codex run
```

## API Key Handling

The recommended path is:

```bash
ds4codex init --apikey sk-your-deepseek-api-key
```

This writes the token into:

```toml
[model_providers.ds4codex]
experimental_bearer_token = "sk-your-deepseek-api-key"
```

Codex sends that token to the local proxy as the incoming bearer token, and the proxy forwards it to DeepSeek.

Alternative runtime sources are also supported:

```bash
DS4CODEX_API_KEY=sk-your-deepseek-api-key ds4codex run
DEEPSEEK_API_KEY=sk-your-deepseek-api-key ds4codex run
ds4codex run --api-key sk-your-deepseek-api-key
```

## Thinking Mode

`thinking` in `[ds4codex]` is only the default used when Codex does not send an explicit reasoning level.

Accepted practical values:

- `disabled`
- `enabled`
- `high`
- `max`

Codex `/model` exposes these reasoning levels through the generated catalog:

- `low`
- `medium`
- `high`
- `xhigh`

DeepSeek currently documents `high` and `max` for reasoning effort, so `ds4codex` maps Codex-style values before forwarding upstream:

- `low`, `medium`, `minimal` -> `high`
- `high` -> `high`
- `xhigh` -> `max`

## What's New in 0.1.6 — Tool Call Fixes

v0.1.6 fixes several critical tool-call issues discovered by comparing
`ds4codex` against the mature `annodex` translation layer. Without these
fixes, multi-turn tool use (especially with streaming) would silently fail
or produce incomplete results.

- **`reasoning_content` injection for DeepSeek tool calls** — DeepSeek rejects
  assistant messages carrying `tool_calls` without `reasoning_content`.
  Every `function_call` and `custom_tool_call` now includes the required
  placeholder.
- **Streaming tool-call deltas** — `translate_stream` now processes
  `delta.tool_calls` incrementally, emitting proper
  `response.output_item.added` / `function_call_arguments.delta` / `.done`
  events.
- **Inline `<think>` tag parsing** — providers that embed reasoning inside
  `<think>...</think>` tags instead of using the `reasoning_content` delta
  field are now supported.
- **Namespace tool expansion** — `type: "namespace"` tool definitions (used
  by MCP servers) are flattened into `namespace__tool` entries that Chat
  Completions APIs consume.
- **`custom_tool_call` / `custom_tool_call_output` support** — previously
  dropped silently; now mapped to standard assistant/tool message format.

See [CHANGELOG.md](CHANGELOG.md) for full release history.

## Why the Proxy Is Required

The proxy is still required even though configuration lives in `~/.codex/config.toml`.

Codex custom providers use:

```toml
wire_api = "responses"
```

DeepSeek uses a Chat Completions compatible endpoint:

```text
https://api.deepseek.com/v1/chat/completions
```

`ds4codex` translates between those two protocols.

## Troubleshooting

### Tool call errors

If tool calls fail silently or DeepSeek returns errors about missing fields
when Codex attempts tool use, upgrade to `ds4codex >= 0.1.6`:

```bash
pip install --upgrade ds4codex -i https://pypi.org/simple
```

If streaming tool calls stop mid-way or produce incomplete results, make sure
you are on 0.1.6 or later. The streaming translator in earlier versions only
forwarded text deltas and could not relay tool calls in streaming mode.

If `/model` does not show DeepSeek models, rerun:

```bash
ds4codex init --force
```

If you need a different local port, rerun:

```bash
ds4codex init --port 9123
```

Then confirm `~/.codex/config.toml` contains `model_catalog_json` pointing to an existing `ds4codex-model-catalog.json` file.

If `ds4codex init` previously failed with `PermissionError: [Errno 13] Permission denied: 'codex'`, upgrade to `ds4codex >= 0.1.2`. Current versions fall back to the built-in model template when local `codex` cannot be executed.

If DeepSeek returns `messages[0].role: unknown variant developer`, upgrade to `ds4codex >= 0.1.3`. Current versions map Codex `developer` messages to DeepSeek-compatible `system` messages.

If DeepSeek returns `tools[0]: missing field function`, upgrade to `ds4codex >= 0.1.4`. Current versions map Codex Responses-style function tools to the nested Chat Completions `function` shape DeepSeek expects.

If requests fail with an API-key error, check that one of these is true:

- `experimental_bearer_token` in `~/.codex/config.toml` contains a real DeepSeek key
- `DS4CODEX_API_KEY` is set when running `ds4codex run`
- `DEEPSEEK_API_KEY` is set when running `ds4codex run`
- `ds4codex run --api-key ...` was used

