@deepthonk/providers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Xule Lin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # DeepThonk
2
+
3
+ *thonk harder, not richer.*
4
+
5
+ DeepThonk implements the OpenDeepThink algorithm (Zhou et al., 2026, [arXiv:2605.15177](https://arxiv.org/abs/2605.15177)) as a budget-friendly, provider-neutral "deep think / pro mode" wrapper, with first-class DeepSeek support. See [Acknowledgments](#acknowledgments).
6
+
7
+ DeepThonk runs a population of candidate answers through pairwise judging, Bradley-Terry ranking, critique-guided mutation, elite preservation, and a final dense ranking pass. The CLI and MCP server both call the same TypeScript core engine.
8
+
9
+ **Designed for agents.** Every algorithm dimension — population shape (`n`, `k`, `t`, `m`), regularization (`lambda`), per-phase temperatures, prompt style, and per-phase prompt templates — is reachable inline through MCP arguments and CLI flags. No filesystem detours required. Every intermediate artifact (candidates, comparisons, scores, per-call usage, status) is exposed as a streaming MCP resource so an agent can inspect any step. See [Customization](https://github.com/linxule/deepthonk/blob/main/docs/customization.md) for the full agent-composable surface.
10
+
11
+ Use it for hard, verifiable reasoning, coding, planning, and synthesis where breadth plus judgment can beat one expensive single shot. Avoid it for highly subjective tasks where judge noise dominates.
12
+
13
+ ## Quickstart
14
+
15
+ Run without installing:
16
+
17
+ ```bash
18
+ npx -y deepthonk plan --profile paper
19
+ npx -y deepthonk run --provider fake --profile quick \
20
+ --task "Find the smallest positive integer divisible by 3, 4, and 5." \
21
+ --out runs/test-quick
22
+ npx -y deepthonk inspect runs/test-quick
23
+ ```
24
+
25
+ Or install globally:
26
+
27
+ ```bash
28
+ npm install -g deepthonk
29
+ deepthonk plan --profile paper
30
+ ```
31
+
32
+ The paper profile plans 285 model calls and 8 sequential rounds. Confirm budget and provider pricing before pointing it at paid models. The short alias `dt` is installed alongside `deepthonk`. Develop from source: see [Development](#development).
33
+
34
+ > v0.1 scope: an independent TypeScript reimplementation of the OpenDeepThink algorithm as a CLI + MCP server. The published algorithm is the load-bearing part; this release is a practical integration layer with explicit limits documented below. Cost guarantees rely on provider pricing being present in config; resume only inspects trace state, it does not replay; the MCP HTTP transport is loopback-only. The included acceptance smoke uses the deterministic fake provider and a toy task — it is not a CF-73 / HLE reproduction; see `docs/` and [Acknowledgments](#acknowledgments) for the canonical paper and Python reference.
35
+
36
+ ## Setup
37
+
38
+ Create a reusable local config:
39
+
40
+ ```bash
41
+ deepthonk setup \
42
+ --provider deepseek \
43
+ --api-key-env DEEPSEEK_API_KEY \
44
+ --fast-model deepseek-v4-flash \
45
+ --judge-model deepseek-v4-pro
46
+ ```
47
+
48
+ By default this writes `~/.config/deepthonk/config.yaml`. `deepthonk run` loads that file automatically when `--config` is not supplied. If you pass `--api-key`, setup stores it in `~/.config/deepthonk/env`; otherwise it uses the named environment variable from your shell.
49
+
50
+ ## DeepSeek
51
+
52
+ ```bash
53
+ export DEEPSEEK_API_KEY=...
54
+ deepthonk run \
55
+ --task task.md \
56
+ --profile paper \
57
+ --provider deepseek \
58
+ --generator-model deepseek-v4-flash \
59
+ --mutator-model deepseek-v4-flash \
60
+ --judge-model deepseek-v4-pro \
61
+ --out runs/task-paper
62
+ ```
63
+
64
+ DeepSeek is implemented as an OpenAI-compatible profile using `https://api.deepseek.com/v1`. DeepThonk ships default USD pricing for `deepseek-v4-flash` and `deepseek-v4-pro` from the official DeepSeek pricing page, including cache-hit/cache-miss input rates. Model names and prices are still editable config because both can change.
65
+
66
+ Before paid runs, inspect cost shape and resolved config:
67
+
68
+ ```bash
69
+ deepthonk plan --config ~/.config/deepthonk/config.yaml
70
+ deepthonk run --task task.md --config ~/.config/deepthonk/config.yaml --profile quick --dry-run
71
+ ```
72
+
73
+ Start with `--profile quick` and consider `--max-concurrency`, `--max-input-tokens`, `--max-output-tokens`, `--max-usd`, and `--request-timeout-ms` before larger paid profiles. Runtime token/USD budgets are enforced after completed provider calls and can overshoot by at most the active concurrency window. Every completed run summary includes call/token usage, and includes `usage.usd` when matching model pricing is available.
74
+
75
+ ## OpenAI-Compatible Providers
76
+
77
+ ```bash
78
+ export DEEPTHONK_API_KEY=...
79
+ deepthonk run \
80
+ --task task.md \
81
+ --profile balanced \
82
+ --provider openai-compatible \
83
+ --base-url https://provider.example.com/v1 \
84
+ --api-key-env DEEPTHONK_API_KEY \
85
+ --generator-model cheap-model \
86
+ --judge-model strong-model
87
+ ```
88
+
89
+ The driver calls `POST {base_url}/chat/completions` and requests JSON mode for comparisons when supported. If a provider returns a `400` with a body that mentions `response_format` or `json`, the driver retries without JSON mode and uses robust JSON extraction on the plain text response. Providers that reject JSON mode with a different status code (e.g. `422`) surface as a normal provider error — set `supportsJsonMode: false` in YAML to disable JSON mode up front.
90
+
91
+ Provider names are flexible. For a custom OpenAI-compatible endpoint, use any provider label with `--base-url`, `--api-key-env`, and role-specific model flags. For OpenRouter:
92
+
93
+ ```bash
94
+ export OPENROUTER_API_KEY=...
95
+ deepthonk run \
96
+ --task task.md \
97
+ --profile balanced \
98
+ --provider openrouter \
99
+ --generator-model openrouter/auto \
100
+ --mutator-model openrouter/auto \
101
+ --judge-model openrouter/auto
102
+ ```
103
+
104
+ For mixed-provider runs, use YAML config and override individual roles under `providers`, especially `judge`.
105
+
106
+ Optional `finalizer_model` / `--finalizer-model` can post-process the ranked winner. Leave it unset when you want the raw ranked answer as the final artifact.
107
+
108
+ ## MCP
109
+
110
+ The MCP server exposes the same engine the CLI runs. Once wired into an MCP host, the host can plan budgets, kick off background runs, poll status, fetch winners, and stream structured trace artifacts — all through MCP tools, resources, and prompts.
111
+
112
+ Provider API keys come from the host process's environment, not from DeepThonk's config alone. Each host handles env passthrough slightly differently — see the concrete patterns below.
113
+
114
+ ### Claude Code
115
+
116
+ ```bash
117
+ claude mcp add deepthonk \
118
+ -e DEEPSEEK_API_KEY=$DEEPSEEK_API_KEY \
119
+ -- npx -y deepthonk serve-mcp --transport stdio
120
+ ```
121
+
122
+ Use `-s user` for cross-project scope or `-s project` to commit registration into `.claude/`. Verify with `claude mcp list`. See `claude mcp --help` for the authoritative flag set.
123
+
124
+ ### Claude Desktop
125
+
126
+ Config path: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS), `%APPDATA%\Claude\claude_desktop_config.json` (Windows), `~/.config/Claude/claude_desktop_config.json` (Linux). Add:
127
+
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "deepthonk": {
132
+ "command": "npx",
133
+ "args": ["-y", "deepthonk", "serve-mcp", "--transport", "stdio"],
134
+ "env": {
135
+ "DEEPSEEK_API_KEY": "sk-..."
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ Restart Claude Desktop after editing.
143
+
144
+ ### Cursor
145
+
146
+ Project-scoped: `.cursor/mcp.json` in the workspace. User-global: `~/.cursor/mcp.json`. Same JSON shape as Claude Desktop:
147
+
148
+ ```json
149
+ {
150
+ "mcpServers": {
151
+ "deepthonk": {
152
+ "command": "npx",
153
+ "args": ["-y", "deepthonk", "serve-mcp", "--transport", "stdio"],
154
+ "env": { "DEEPSEEK_API_KEY": "sk-..." }
155
+ }
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### Other MCP hosts (stdio)
161
+
162
+ Any host that speaks MCP stdio launches:
163
+
164
+ ```bash
165
+ npx -y deepthonk serve-mcp --transport stdio
166
+ ```
167
+
168
+ with the relevant provider env vars set on the process. If you'd rather install globally, `npm install -g deepthonk` then use `command: "deepthonk"` directly. Refer to the host's MCP registration docs for the configuration shape.
169
+
170
+ ### Streamable HTTP
171
+
172
+ For local web hosts, or when stdio isn't available:
173
+
174
+ ```bash
175
+ deepthonk serve-mcp --transport http --port 3333
176
+ ```
177
+
178
+ The server binds `127.0.0.1:3333` only (loopback) and the endpoint is `POST http://127.0.0.1:3333/mcp`. DNS rebinding protection is on (CVE-2025-66414): requests with `Host` headers outside `127.0.0.1:3333` / `localhost:3333` are rejected. Do not expose this port through a reverse proxy without re-evaluating that trust boundary.
179
+
180
+ ### Tools
181
+
182
+ | Tool | Purpose |
183
+ |---|---|
184
+ | `deepthonk.plan` | Estimate calls and sequential rounds for a profile (no model calls). Use before paid runs. |
185
+ | `deepthonk.start` | Start a run in the background; returns `run_dir` + `job_id`. |
186
+ | `deepthonk.status` | Poll job status from a `run_dir`. |
187
+ | `deepthonk.result` | Return final summary + winner once a job is complete. |
188
+ | `deepthonk.cancel` | Request cancellation by writing `cancel.json` into the run directory. |
189
+ | `deepthonk.run` | Blocking convenience: start + await completion in one call. Prefer `start` + polling for long-running jobs. |
190
+ | `deepthonk.rank` | Rank a user-supplied candidate set with pairwise judging + Bradley-Terry (skip generation). |
191
+ | `deepthonk.mutate` | Mutate one supplied candidate with critique (one-shot). |
192
+ | `deepthonk.resume` | Detect whether a run can be resumed. Reports state only; does not replay yet. |
193
+ | `deepthonk.export` | Export run summary or full trace in JSON or markdown. |
194
+
195
+ All tools accept inline provider/model fields, or `config_path` pointing at a DeepThonk YAML config.
196
+
197
+ ### Resources
198
+
199
+ - `deepthonk://runs` — JSON index of all runs in `runs/`.
200
+ - `deepthonk://runs/{run_id}/summary` — run summary (JSON).
201
+ - `deepthonk://runs/{run_id}/{candidates|comparisons|scores|trace}` — per-phase NDJSON.
202
+ - `deepthonk://runs/{run_id}/{winner|final}` — text artifacts.
203
+ - `deepthonk://runs/{run_id}/status` — run state (JSON).
204
+ - `deepthonk://jobs/{job_id}/{status|result}?run_dir=...` — job-scoped lookup; the `run_dir` query param is required.
205
+
206
+ ### Prompts
207
+
208
+ Four templates mirror the core loop: `deepthonk/generate`, `deepthonk/compare`, `deepthonk/mutate`, `deepthonk/finalize`. Hosts can render them directly to drive the algorithm by hand without invoking the tool surface.
209
+
210
+ ### Limits
211
+
212
+ MCP Sampling is deferred and not exposed as a provider mode — direct provider mode is the only option in v0.1. `deepthonk.resume` reports trace state and safe phase boundaries but does not replay interrupted runs. The HTTP transport is loopback-only by design.
213
+
214
+ ## Customization
215
+
216
+ Every algorithm dimension is reachable through MCP arguments and CLI flags:
217
+
218
+ - **Population shape**: `n`, `k`, `t`, `m` — override profile defaults inline.
219
+ - **Algorithm constants**: `lambda`, `sample_temperature`, `mutate_temperature`, `judge_temperature`.
220
+ - **Prompt style**: `general` or `paper-programming`.
221
+ - **Per-phase prompt templates**: override `generate`, `compare`, `mutate`, or `finalize` with custom system/user templates and variable substitution (`{task}`, `{rubric}`, `{candidate}`, `{candidateA}`, `{candidateB}`, `{critique}`). Unknown variables throw a fail-fast error at run-start.
222
+ - **Concurrency**: per-phase caps for `generate`, `judge`, `mutate`.
223
+
224
+ Example agent call (MCP), no YAML file required:
225
+
226
+ ```json
227
+ {
228
+ "task": "Draft a concise non-solicitation clause for a senior sales employee.",
229
+ "profile": "balanced",
230
+ "n": 6, "t": 1,
231
+ "provider": "deepseek",
232
+ "judge_model": "deepseek-v4-pro",
233
+ "prompts": {
234
+ "generate": { "system": "You are an experienced employment-law attorney." },
235
+ "compare": { "system": "Prefer enforceable clauses under California law. Return strict JSON only." }
236
+ }
237
+ }
238
+ ```
239
+
240
+ CLI accepts the same surface, except prompt overrides are loaded from a YAML file via `--prompts <yaml>` (shell-quoting multi-line templates is hostile). MCP and CLI both merge over any `--config`/`config_path` YAML defaults.
241
+
242
+ See the [Customization guide](https://github.com/linxule/deepthonk/blob/main/docs/customization.md) for the complete variable contract, the compare-phase JSON safety rule, and three worked examples. Per-role provider routing (`providers.judge.provider = openrouter`) and per-model pricing remain YAML-only — they're nested structured config, not inline ergonomics.
243
+
244
+ ## Trace Files
245
+
246
+ A successful run writes:
247
+
248
+ ```txt
249
+ runs/{run_id}/
250
+ config.json
251
+ events.jsonl
252
+ candidates.jsonl
253
+ comparisons.jsonl
254
+ scores.jsonl
255
+ usage.jsonl
256
+ summary.json
257
+ artifacts/winner.txt
258
+ artifacts/final.txt
259
+ ```
260
+
261
+ `status.json` is written when the run is launched through MCP `deepthonk.start` (so async clients can poll). `cancel.json` is written only when cancellation is requested. A `run.lock` file is held for the duration of the run.
262
+
263
+ JSONL writes are streamed as each candidate / comparison / call completes, and all appends go through a per-trace serialization queue. If a run is killed mid-tournament, completed pairs are durable on disk and the JSONL files remain readable.
264
+
265
+ `usage.jsonl` carries one row per provider call (generator, judge, mutator, finalizer) with phase/role/provider/model/token/USD/latency/retry. It contains no prompt content. `jq` over `usage.jsonl` is the simplest way to break a run's cost down by role.
266
+
267
+ Prompts and raw model outputs are off by default. API keys are never written. Candidate answers, critiques, scores, and final artifacts are trace data and are stored/exported by design, so do not use a shared run directory for sensitive tasks unless that is acceptable.
268
+
269
+ ## Development
270
+
271
+ ```bash
272
+ pnpm install
273
+ pnpm run build
274
+ pnpm test
275
+ pnpm run lint
276
+ ```
277
+
278
+ This repository is source-only. Generated run traces, paper notes, build output, coverage, logs, and `node_modules` are intentionally ignored.
279
+
280
+ Known limits for this release:
281
+
282
+ - Conservative resume reports trace state and safe phase boundaries, but does not replay interrupted runs yet.
283
+ - MCP Sampling is deferred until there is a real host-backed Sampling driver.
284
+ - `maxUsd` requires known model pricing; add explicit prices in YAML for custom providers and model IDs. Optional fields `longContextThresholdTokens`, `inputUsdPerMillionLong`, and `outputUsdPerMillionLong` enable tiered pricing for models with a long-context surcharge (e.g., Gemini at 200K input tokens). Cache hit/miss rates remain flat.
285
+
286
+ ## Acknowledgments
287
+
288
+ DeepThonk is an independent TypeScript reimplementation of the OpenDeepThink algorithm. It is not a fork; no source code from the reference implementation is vendored. Credit for the algorithm, the benchmark methodology, and the empirical results belongs to the paper authors.
289
+
290
+ - **Paper**: Shang Zhou, Wenhao Chai, Kaiyuan Liu, Huanzhi Mao, Qiuyang Mang, Jingbo Shang. *OpenDeepThink: Parallel Reasoning via Bradley–Terry Aggregation*. arXiv:2605.15177, 2026. <https://arxiv.org/abs/2605.15177>
291
+ - **Reference Python implementation**: <https://github.com/ZhouShang0817/open-deep-think> (MIT). This is the authors' code release and the canonical implementation for reproducing paper results (CF-73, HLE).
292
+
293
+ DeepThonk's contribution is an integration layer: a provider-neutral TypeScript core, an MCP server, a CLI, structured trace artifacts, and budget enforcement. The algorithm itself is theirs.
@@ -0,0 +1,25 @@
1
+ import type { ProviderConfig, ProviderRole, RoleProviderConfig } from "./types.js";
2
+ export type DirectProviderName = "fake" | "openai-compatible" | "deepseek" | "openrouter";
3
+ export interface ProviderModelOverrides {
4
+ generator?: string;
5
+ mutator?: string;
6
+ judge?: string;
7
+ finalizer?: string;
8
+ }
9
+ export interface ProviderConfigOptions {
10
+ provider: string;
11
+ baseUrl?: string;
12
+ apiKeyEnv?: string;
13
+ apiKey?: string;
14
+ models?: ProviderModelOverrides;
15
+ retry?: ProviderConfig["retry"];
16
+ supportsJsonMode?: boolean;
17
+ roleProviders?: Partial<Record<ProviderRole, Partial<Omit<RoleProviderConfig, "model">> & {
18
+ model?: string;
19
+ }>>;
20
+ }
21
+ export declare function resolveProviderModels(provider: string, overrides?: ProviderModelOverrides): ProviderConfig["models"];
22
+ export declare function resolveProviderConfig(options: ProviderConfigOptions): ProviderConfig;
23
+ export declare function defaultModel(provider: string, role: ProviderRole): string;
24
+ export declare function defaultBaseUrl(provider: string): string | undefined;
25
+ export declare function defaultApiKeyEnv(provider: string): string | undefined;
@@ -0,0 +1,71 @@
1
+ export function resolveProviderModels(provider, overrides = {}) {
2
+ return {
3
+ generator: overrides.generator ?? defaultModel(provider, "generator"),
4
+ mutator: overrides.mutator ?? defaultModel(provider, "mutator"),
5
+ judge: overrides.judge ?? defaultModel(provider, "judge"),
6
+ finalizer: overrides.finalizer
7
+ };
8
+ }
9
+ export function resolveProviderConfig(options) {
10
+ const models = resolveProviderModels(options.provider, options.models);
11
+ return {
12
+ provider: options.provider,
13
+ baseUrl: options.baseUrl ?? defaultBaseUrl(options.provider),
14
+ apiKeyEnv: options.apiKeyEnv ?? defaultApiKeyEnv(options.provider),
15
+ apiKey: options.apiKey,
16
+ models,
17
+ retry: options.retry,
18
+ supportsJsonMode: options.supportsJsonMode,
19
+ roleProviders: resolveRoleProviders(options.provider, models, options.roleProviders, options.retry)
20
+ };
21
+ }
22
+ export function defaultModel(provider, role) {
23
+ if (role === "finalizer")
24
+ return defaultModel(provider, "judge");
25
+ if (provider === "deepseek")
26
+ return role === "judge" ? "deepseek-v4-pro" : "deepseek-v4-flash";
27
+ if (provider === "fake")
28
+ return "fake-model";
29
+ if (provider === "openrouter")
30
+ return "openrouter/auto";
31
+ return role === "judge" ? "provider/model-large" : "provider/model-small";
32
+ }
33
+ export function defaultBaseUrl(provider) {
34
+ if (provider === "deepseek")
35
+ return "https://api.deepseek.com/v1";
36
+ if (provider === "openrouter")
37
+ return "https://openrouter.ai/api/v1";
38
+ return undefined;
39
+ }
40
+ export function defaultApiKeyEnv(provider) {
41
+ if (provider === "deepseek")
42
+ return "DEEPSEEK_API_KEY";
43
+ if (provider === "openrouter")
44
+ return "OPENROUTER_API_KEY";
45
+ if (provider === "openai-compatible")
46
+ return "DEEPTHONK_API_KEY";
47
+ return undefined;
48
+ }
49
+ function resolveRoleProviders(baseProvider, models, roleProviders, baseRetry) {
50
+ if (!roleProviders)
51
+ return undefined;
52
+ const resolved = {};
53
+ for (const role of ["generator", "mutator", "judge", "finalizer"]) {
54
+ const input = roleProviders[role];
55
+ if (!input)
56
+ continue;
57
+ const provider = input.provider ?? baseProvider;
58
+ const model = input.model ?? models[role] ?? defaultModel(provider, role);
59
+ resolved[role] = {
60
+ provider,
61
+ baseUrl: input.baseUrl ?? defaultBaseUrl(provider),
62
+ apiKeyEnv: input.apiKeyEnv ?? defaultApiKeyEnv(provider),
63
+ apiKey: input.apiKey,
64
+ model,
65
+ retry: input.retry ?? baseRetry,
66
+ supportsJsonMode: input.supportsJsonMode
67
+ };
68
+ }
69
+ return Object.keys(resolved).length ? resolved : undefined;
70
+ }
71
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,qBAAqB,CAAC,QAAgB,EAAE,YAAoC,EAAE;IAC5F,OAAO;QACL,SAAS,EAAE,SAAS,CAAC,SAAS,IAAI,YAAY,CAAC,QAAQ,EAAE,WAAW,CAAC;QACrE,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC;QAC/D,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC;QACzD,SAAS,EAAE,SAAS,CAAC,SAAS;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAA8B;IAClE,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC5D,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC;QAClE,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM;QACN,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,aAAa,EAAE,oBAAoB,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,KAAK,CAAC;KACpG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,IAAkB;IAC/D,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjE,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IAC/F,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO,YAAY,CAAC;IAC7C,IAAI,QAAQ,KAAK,YAAY;QAAE,OAAO,iBAAiB,CAAC;IACxD,OAAO,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,sBAAsB,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO,6BAA6B,CAAC;IAClE,IAAI,QAAQ,KAAK,YAAY;QAAE,OAAO,8BAA8B,CAAC;IACrE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO,kBAAkB,CAAC;IACvD,IAAI,QAAQ,KAAK,YAAY;QAAE,OAAO,oBAAoB,CAAC;IAC3D,IAAI,QAAQ,KAAK,mBAAmB;QAAE,OAAO,mBAAmB,CAAC;IACjE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,oBAAoB,CAC3B,YAAoB,EACpB,MAAgC,EAChC,aAAsD,EACtD,SAAmC;IAEnC,IAAI,CAAC,aAAa;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,QAAQ,GAAoC,EAAE,CAAC;IACrD,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAU,EAAE,CAAC;QAC3E,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,YAAY,CAAC;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC1E,QAAQ,CAAC,IAAI,CAAC,GAAG;YACf,QAAQ;YACR,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC,QAAQ,CAAC;YAClD,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,gBAAgB,CAAC,QAAQ,CAAC;YACxD,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK;YACL,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,SAAS;YAC/B,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;SACzC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC"}
package/dist/fake.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { CompareInput, FinalizeInput, GenerateInput, ModelDriver, ModelTextResult, MutateInput } from "@deepthonk/core";
2
+ export interface FakeDriverOptions {
3
+ seed?: number;
4
+ failEvery?: number;
5
+ invalidJsonEvery?: number;
6
+ }
7
+ export declare class FakeDriver implements ModelDriver {
8
+ private readonly options;
9
+ private calls;
10
+ constructor(options?: FakeDriverOptions);
11
+ generate(input: GenerateInput): Promise<ModelTextResult>;
12
+ compare(input: CompareInput): Promise<ModelTextResult>;
13
+ mutate(input: MutateInput): Promise<ModelTextResult>;
14
+ finalize(input: FinalizeInput): Promise<ModelTextResult>;
15
+ private tick;
16
+ private result;
17
+ }
18
+ export declare function quality(text: string): number;
package/dist/fake.js ADDED
@@ -0,0 +1,63 @@
1
+ export class FakeDriver {
2
+ options;
3
+ calls = 0;
4
+ constructor(options = {}) {
5
+ this.options = options;
6
+ }
7
+ async generate(input) {
8
+ this.tick();
9
+ const index = input.candidateIndex ?? 0;
10
+ const quality = (index + 1) * 10;
11
+ return this.result(`FAKE_QUALITY:${quality}\nCandidate ${index + 1}: deterministic answer for ${input.task}`);
12
+ }
13
+ async compare(input) {
14
+ this.tick();
15
+ if (this.options.invalidJsonEvery && this.calls % this.options.invalidJsonEvery === 0) {
16
+ return this.result("not json");
17
+ }
18
+ const qa = quality(input.candidateA.content);
19
+ const qb = quality(input.candidateB.content);
20
+ const winner = qa === qb ? "tie" : qa > qb ? "A" : "B";
21
+ return this.result(JSON.stringify({
22
+ winner,
23
+ confidence: winner === "tie" ? 0.5 : 0.9,
24
+ critique_for_A: qa >= qb ? "A is comparatively strong." : "A is weaker on the synthetic quality signal.",
25
+ critique_for_B: qb >= qa ? "B is comparatively strong." : "B is weaker on the synthetic quality signal.",
26
+ selection_reason: `Synthetic quality ${qa} vs ${qb}.`
27
+ }));
28
+ }
29
+ async mutate(input) {
30
+ this.tick();
31
+ const nextQuality = quality(input.candidate.content) + 7;
32
+ return this.result(`FAKE_QUALITY:${nextQuality}\nImproved from ${input.candidate.id}. ${input.candidate.content}`);
33
+ }
34
+ async finalize(input) {
35
+ this.tick();
36
+ return this.result(input.candidate.content.replace(/^FAKE_QUALITY:\d+\n/, ""));
37
+ }
38
+ tick() {
39
+ this.calls += 1;
40
+ if (this.options.failEvery && this.calls % this.options.failEvery === 0) {
41
+ throw new Error(`Fake seeded failure at call ${this.calls}.`);
42
+ }
43
+ }
44
+ result(text) {
45
+ return {
46
+ text,
47
+ model: "fake-model",
48
+ provider: "fake",
49
+ usage: {
50
+ inputTokens: Math.ceil(text.length / 6),
51
+ outputTokens: Math.ceil(text.length / 5),
52
+ totalTokens: Math.ceil(text.length / 3)
53
+ },
54
+ latencyMs: 0,
55
+ retryCount: 0
56
+ };
57
+ }
58
+ }
59
+ export function quality(text) {
60
+ const match = text.match(/FAKE_QUALITY:(-?\d+(?:\.\d+)?)/);
61
+ return match ? Number(match[1]) : 0;
62
+ }
63
+ //# sourceMappingURL=fake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake.js","sourceRoot":"","sources":["../src/fake.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from "./fake.js";
2
+ export * from "./defaults.js";
3
+ export * from "./openaiCompatible.js";
4
+ export * from "./pricing.js";
5
+ export * from "./registry.js";
6
+ export * from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./fake.js";
2
+ export * from "./defaults.js";
3
+ export * from "./openaiCompatible.js";
4
+ export * from "./pricing.js";
5
+ export * from "./registry.js";
6
+ export * from "./types.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type CompareInput, type FinalizeInput, type GenerateInput, type ModelDriver, type ModelTextResult, type MutateInput } from "@deepthonk/core";
2
+ import type { ProviderConfig } from "./types.js";
3
+ export declare class OpenAiCompatibleDriver implements ModelDriver {
4
+ private readonly config;
5
+ readonly provider: string;
6
+ readonly baseUrl: string;
7
+ readonly apiKey?: string;
8
+ readonly httpRetries: number;
9
+ readonly requestTimeoutMs?: number;
10
+ readonly supportsJsonMode: boolean;
11
+ constructor(config: ProviderConfig);
12
+ generate(input: GenerateInput): Promise<ModelTextResult>;
13
+ compare(input: CompareInput): Promise<ModelTextResult>;
14
+ mutate(input: MutateInput): Promise<ModelTextResult>;
15
+ finalize(input: FinalizeInput): Promise<ModelTextResult>;
16
+ private chat;
17
+ }
18
+ export declare function createDeepSeekDriver(config?: Partial<ProviderConfig>): OpenAiCompatibleDriver;
@@ -0,0 +1,203 @@
1
+ import { ProviderError } from "@deepthonk/core";
2
+ import { resolveProviderConfig } from "./defaults.js";
3
+ export class OpenAiCompatibleDriver {
4
+ config;
5
+ provider;
6
+ baseUrl;
7
+ apiKey;
8
+ httpRetries;
9
+ requestTimeoutMs;
10
+ supportsJsonMode;
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.provider = config.provider;
14
+ this.baseUrl = normalizeBaseUrl(config.baseUrl);
15
+ this.apiKey = config.apiKey ?? (config.apiKeyEnv ? process.env[config.apiKeyEnv] : undefined);
16
+ this.httpRetries = config.retry?.httpRetries ?? 12;
17
+ this.requestTimeoutMs = config.retry?.requestTimeoutMs ?? 120_000;
18
+ this.supportsJsonMode = config.supportsJsonMode ?? true;
19
+ if (!this.apiKey) {
20
+ throw new ProviderError(`Missing API key. Set ${config.apiKeyEnv ?? "apiKey"} for provider ${config.provider}.`, {
21
+ code: "provider.missing_api_key",
22
+ retryable: false,
23
+ fix: `Set ${config.apiKeyEnv ?? "apiKey"} or run deepthonk setup.`
24
+ });
25
+ }
26
+ }
27
+ async generate(input) {
28
+ return this.chat(input.model, input.temperature, messages(input), false);
29
+ }
30
+ async compare(input) {
31
+ return this.chat(input.model, input.temperature, messages(input), true);
32
+ }
33
+ async mutate(input) {
34
+ return this.chat(input.model, input.temperature, messages(input), false);
35
+ }
36
+ async finalize(input) {
37
+ return this.chat(input.model, 0.2, messages(input), false);
38
+ }
39
+ async chat(model, temperature, messagesValue, jsonMode) {
40
+ const started = Date.now();
41
+ let lastError;
42
+ let retryCount = 0;
43
+ for (let attempt = 0; attempt <= this.httpRetries; attempt += 1) {
44
+ let timeout;
45
+ try {
46
+ const body = {
47
+ model,
48
+ messages: messagesValue,
49
+ temperature,
50
+ max_tokens: 4096
51
+ };
52
+ if (jsonMode && this.supportsJsonMode)
53
+ body.response_format = { type: "json_object" };
54
+ const controller = this.requestTimeoutMs ? new AbortController() : undefined;
55
+ timeout = controller ? setTimeout(() => controller.abort(), this.requestTimeoutMs) : undefined;
56
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
57
+ method: "POST",
58
+ headers: {
59
+ "content-type": "application/json",
60
+ authorization: `Bearer ${this.apiKey}`
61
+ },
62
+ body: JSON.stringify(body),
63
+ signal: controller?.signal
64
+ });
65
+ if (timeout)
66
+ clearTimeout(timeout);
67
+ timeout = undefined;
68
+ if (!response.ok) {
69
+ const text = await response.text();
70
+ if ((response.status === 429 || response.status >= 500) && attempt < this.httpRetries) {
71
+ retryCount += 1;
72
+ await sleep(retryDelay(response, attempt));
73
+ continue;
74
+ }
75
+ if (jsonMode && this.supportsJsonMode && rejectsJsonMode(response.status, text)) {
76
+ return new OpenAiCompatibleDriver({ ...this.config, supportsJsonMode: false }).chat(model, temperature, messagesValue, false);
77
+ }
78
+ throw providerHttpError(this.provider, response.status);
79
+ }
80
+ const json = (await response.json());
81
+ const choice = json.choices?.[0];
82
+ if (choice?.finish_reason === "length") {
83
+ throw new ProviderError("Provider response was truncated by max_tokens.", {
84
+ code: "provider.output_truncated",
85
+ retryable: false,
86
+ fix: "Use a model/provider with shorter outputs or increase max output token support in provider config."
87
+ });
88
+ }
89
+ const text = choice?.message?.content;
90
+ if (!text)
91
+ throw new ProviderError("Provider response did not include choices[0].message.content.", { code: "provider.empty_response" });
92
+ return {
93
+ text,
94
+ model: json.model ?? model,
95
+ provider: this.provider,
96
+ usage: {
97
+ inputTokens: json.usage?.prompt_tokens,
98
+ inputCacheHitTokens: json.usage?.prompt_cache_hit_tokens,
99
+ inputCacheMissTokens: json.usage?.prompt_cache_miss_tokens,
100
+ outputTokens: json.usage?.completion_tokens,
101
+ totalTokens: json.usage?.total_tokens
102
+ },
103
+ latencyMs: Date.now() - started,
104
+ retryCount,
105
+ raw: json
106
+ };
107
+ }
108
+ catch (error) {
109
+ if (timeout)
110
+ clearTimeout(timeout);
111
+ lastError = error;
112
+ if (attempt < this.httpRetries && isRetryableError(error)) {
113
+ retryCount += 1;
114
+ await sleep(backoff(attempt));
115
+ continue;
116
+ }
117
+ break;
118
+ }
119
+ }
120
+ if (lastError instanceof ProviderError)
121
+ throw lastError;
122
+ if (isAbortError(lastError)) {
123
+ throw new ProviderError(`Provider request timed out after ${this.requestTimeoutMs}ms.`, {
124
+ code: "provider.timeout",
125
+ retryable: true,
126
+ fix: "Raise requestTimeoutMs or use a faster model/provider."
127
+ });
128
+ }
129
+ throw new ProviderError(`Provider request failed: ${lastError.message}`, {
130
+ code: "provider.request_failed",
131
+ retryable: true,
132
+ fix: "Check provider availability, base URL, and network connectivity."
133
+ });
134
+ }
135
+ }
136
+ export function createDeepSeekDriver(config = {}) {
137
+ return new OpenAiCompatibleDriver(resolveProviderConfig({
138
+ provider: "deepseek",
139
+ ...config
140
+ }));
141
+ }
142
+ function messages(input) {
143
+ if (!input.prompt)
144
+ throw new ProviderError("Core did not provide prompt messages.");
145
+ const chatMessages = [
146
+ { role: "system", content: input.prompt.system },
147
+ { role: "user", content: input.prompt.user }
148
+ ];
149
+ return chatMessages.filter((message) => message.content.trim().length > 0);
150
+ }
151
+ function normalizeBaseUrl(baseUrl) {
152
+ if (!baseUrl) {
153
+ throw new ProviderError("OpenAI-compatible provider requires baseUrl.", {
154
+ code: "provider.missing_base_url",
155
+ retryable: false,
156
+ fix: "Set base_url in config or pass --base-url."
157
+ });
158
+ }
159
+ return baseUrl.replace(/\/+$/, "");
160
+ }
161
+ function rejectsJsonMode(status, body) {
162
+ return status === 400 && /response_format|json/i.test(body);
163
+ }
164
+ function isRetryableError(error) {
165
+ if (error instanceof ProviderError)
166
+ return error.retryable;
167
+ return true;
168
+ }
169
+ function providerHttpError(provider, status) {
170
+ const retryable = status === 429 || status >= 500;
171
+ return new ProviderError(`Provider HTTP ${status} from ${provider}.`, {
172
+ code: `provider.http_${status}`,
173
+ retryable,
174
+ fix: retryable ? "Retry later or lower concurrency." : "Check provider configuration, request shape, and model access."
175
+ });
176
+ }
177
+ function retryDelay(response, attempt) {
178
+ const retryAfter = response.headers.get("retry-after");
179
+ if (!retryAfter)
180
+ return backoff(attempt);
181
+ const seconds = Number(retryAfter);
182
+ if (Number.isFinite(seconds))
183
+ return Math.max(0, seconds * 1000);
184
+ const timestamp = Date.parse(retryAfter);
185
+ if (Number.isFinite(timestamp))
186
+ return Math.max(0, timestamp - Date.now());
187
+ return backoff(attempt);
188
+ }
189
+ function isAbortError(error) {
190
+ return error instanceof Error && error.name === "AbortError";
191
+ }
192
+ function backoff(attempt) {
193
+ // Multiplicative jitter (±20% around the capped exponential base) matches the reference
194
+ // Python's burst dispersion. Additive jitter at the 60s cap was effectively ±0.4% and would
195
+ // align concurrent retries into the next provider rate-limit window.
196
+ const base = Math.min(60_000, 500 * 2 ** attempt);
197
+ const jitterFactor = 0.8 + Math.random() * 0.4;
198
+ return Math.floor(base * jitterFactor);
199
+ }
200
+ function sleep(ms) {
201
+ return new Promise((resolve) => setTimeout(resolve, ms));
202
+ }
203
+ //# sourceMappingURL=openaiCompatible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openaiCompatible.js","sourceRoot":"","sources":["../src/openaiCompatible.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAuH,MAAM,iBAAiB,CAAC;AACrK,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAoBtD,MAAM,OAAO,sBAAsB;IAQJ;IAPpB,QAAQ,CAAS;IACjB,OAAO,CAAS;IAChB,MAAM,CAAU;IAChB,WAAW,CAAS;IACpB,gBAAgB,CAAU;IAC1B,gBAAgB,CAAU;IAEnC,YAA6B,MAAsB;QAAtB,WAAM,GAAN,MAAM,CAAgB;QACjD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC9F,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,EAAE,WAAW,IAAI,EAAE,CAAC;QACnD,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,KAAK,EAAE,gBAAgB,IAAI,OAAO,CAAC;QAClE,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,IAAI,CAAC;QACxD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,aAAa,CAAC,wBAAwB,MAAM,CAAC,SAAS,IAAI,QAAQ,iBAAiB,MAAM,CAAC,QAAQ,GAAG,EAAE;gBAC/G,IAAI,EAAE,0BAA0B;gBAChC,SAAS,EAAE,KAAK;gBAChB,GAAG,EAAE,OAAO,MAAM,CAAC,SAAS,IAAI,QAAQ,0BAA0B;aACnE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAoB;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAmB;QAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAkB;QAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAoB;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7D,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,KAAa,EAAE,WAAmB,EAAE,aAA4B,EAAE,QAAiB;QACpG,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,SAAkB,CAAC;QACvB,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;YAChE,IAAI,OAAmC,CAAC;YACxC,IAAI,CAAC;gBACH,MAAM,IAAI,GAA4B;oBACpC,KAAK;oBACL,QAAQ,EAAE,aAAa;oBACvB,WAAW;oBACX,UAAU,EAAE,IAAI;iBACjB,CAAC;gBACF,IAAI,QAAQ,IAAI,IAAI,CAAC,gBAAgB;oBAAE,IAAI,CAAC,eAAe,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;gBACtF,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,eAAe,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC7E,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC/F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,mBAAmB,EAAE;oBAC/D,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACP,cAAc,EAAE,kBAAkB;wBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;qBACvC;oBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;oBAC1B,MAAM,EAAE,UAAU,EAAE,MAAM;iBAC3B,CAAC,CAAC;gBACH,IAAI,OAAO;oBAAE,YAAY,CAAC,OAAO,CAAC,CAAC;gBACnC,OAAO,GAAG,SAAS,CAAC;gBACpB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACnC,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;wBACtF,UAAU,IAAI,CAAC,CAAC;wBAChB,MAAM,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;wBAC3C,SAAS;oBACX,CAAC;oBACD,IAAI,QAAQ,IAAI,IAAI,CAAC,gBAAgB,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;wBAChF,OAAO,IAAI,sBAAsB,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;oBAChI,CAAC;oBACD,MAAM,iBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC1D,CAAC;gBACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuB,CAAC;gBAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjC,IAAI,MAAM,EAAE,aAAa,KAAK,QAAQ,EAAE,CAAC;oBACvC,MAAM,IAAI,aAAa,CAAC,gDAAgD,EAAE;wBACxE,IAAI,EAAE,2BAA2B;wBACjC,SAAS,EAAE,KAAK;wBAChB,GAAG,EAAE,oGAAoG;qBAC1G,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;gBACtC,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,aAAa,CAAC,+DAA+D,EAAE,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC,CAAC;gBACzI,OAAO;oBACL,IAAI;oBACJ,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK;oBAC1B,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,KAAK,EAAE;wBACL,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa;wBACtC,mBAAmB,EAAE,IAAI,CAAC,KAAK,EAAE,uBAAuB;wBACxD,oBAAoB,EAAE,IAAI,CAAC,KAAK,EAAE,wBAAwB;wBAC1D,YAAY,EAAE,IAAI,CAAC,KAAK,EAAE,iBAAiB;wBAC3C,WAAW,EAAE,IAAI,CAAC,KAAK,EAAE,YAAY;qBACtC;oBACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;oBAC/B,UAAU;oBACV,GAAG,EAAE,IAAI;iBACV,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,OAAO;oBAAE,YAAY,CAAC,OAAO,CAAC,CAAC;gBACnC,SAAS,GAAG,KAAK,CAAC;gBAClB,IAAI,OAAO,GAAG,IAAI,CAAC,WAAW,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1D,UAAU,IAAI,CAAC,CAAC;oBAChB,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;oBAC9B,SAAS;gBACX,CAAC;gBACD,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,SAAS,YAAY,aAAa;YAAE,MAAM,SAAS,CAAC;QACxD,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,aAAa,CAAC,oCAAoC,IAAI,CAAC,gBAAgB,KAAK,EAAE;gBACtF,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,IAAI;gBACf,GAAG,EAAE,wDAAwD;aAC9D,CAAC,CAAC;QACL,CAAC;QACD,MAAM,IAAI,aAAa,CAAC,4BAA6B,SAAmB,CAAC,OAAO,EAAE,EAAE;YAClF,IAAI,EAAE,yBAAyB;YAC/B,SAAS,EAAE,IAAI;YACf,GAAG,EAAE,kEAAkE;SACxE,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAkC,EAAE;IACvE,OAAO,IAAI,sBAAsB,CAAC,qBAAqB,CAAC;QACtD,QAAQ,EAAE,UAAU;QACpB,GAAG,MAAM;KACV,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,QAAQ,CAAC,KAAiE;IACjF,IAAI,CAAC,KAAK,CAAC,MAAM;QAAE,MAAM,IAAI,aAAa,CAAC,uCAAuC,CAAC,CAAC;IACpF,MAAM,YAAY,GAAkB;QAClC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE;QAChD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE;KAC7C,CAAC;IACF,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAgB;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,aAAa,CAAC,8CAA8C,EAAE;YACtE,IAAI,EAAE,2BAA2B;YACjC,SAAS,EAAE,KAAK;YAChB,GAAG,EAAE,4CAA4C;SAClD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,IAAY;IACnD,OAAO,MAAM,KAAK,GAAG,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,KAAK,YAAY,aAAa;QAAE,OAAO,KAAK,CAAC,SAAS,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB,EAAE,MAAc;IACzD,MAAM,SAAS,GAAG,MAAM,KAAK,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC;IAClD,OAAO,IAAI,aAAa,CAAC,iBAAiB,MAAM,SAAS,QAAQ,GAAG,EAAE;QACpE,IAAI,EAAE,iBAAiB,MAAM,EAAE;QAC/B,SAAS;QACT,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,mCAAmC,CAAC,CAAC,CAAC,gEAAgE;KACxH,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,QAAkB,EAAE,OAAe;IACrD,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3E,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC;AAC/D,CAAC;AAED,SAAS,OAAO,CAAC,OAAe;IAC9B,wFAAwF;IACxF,4FAA4F;IAC5F,qEAAqE;IACrE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC;IAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { ProviderConfig } from "./types.js";
2
+ export interface ModelPrice {
3
+ provider: string;
4
+ model: string;
5
+ inputUsdPerMillion?: number;
6
+ inputCacheHitUsdPerMillion?: number;
7
+ inputCacheMissUsdPerMillion?: number;
8
+ outputUsdPerMillion?: number;
9
+ longContextThresholdTokens?: number;
10
+ inputUsdPerMillionLong?: number;
11
+ outputUsdPerMillionLong?: number;
12
+ source?: string;
13
+ note?: string;
14
+ }
15
+ export declare const defaultProviderPricing: ModelPrice[];
16
+ export declare const editablePricing: ModelPrice[];
17
+ export declare function defaultPricesForProviderConfig(config: ProviderConfig): ModelPrice[];
@@ -0,0 +1,38 @@
1
+ export const defaultProviderPricing = [
2
+ {
3
+ provider: "deepseek",
4
+ model: "deepseek-v4-flash",
5
+ inputUsdPerMillion: 0.14,
6
+ inputCacheHitUsdPerMillion: 0.0028,
7
+ inputCacheMissUsdPerMillion: 0.14,
8
+ outputUsdPerMillion: 0.28,
9
+ source: "https://api-docs.deepseek.com/quick_start/pricing/",
10
+ note: "Official DeepSeek pricing page, checked 2026-05-18. Input uses cache-hit/cache-miss fields when provider usage reports them."
11
+ },
12
+ {
13
+ provider: "deepseek",
14
+ model: "deepseek-v4-pro",
15
+ inputUsdPerMillion: 0.435,
16
+ inputCacheHitUsdPerMillion: 0.003625,
17
+ inputCacheMissUsdPerMillion: 0.435,
18
+ outputUsdPerMillion: 0.87,
19
+ source: "https://api-docs.deepseek.com/quick_start/pricing/",
20
+ note: "Official DeepSeek V4-Pro discounted pricing, checked 2026-05-18; DeepSeek says the 75% discount is extended until 2026-05-31 15:59 UTC."
21
+ }
22
+ ];
23
+ export const editablePricing = defaultProviderPricing;
24
+ export function defaultPricesForProviderConfig(config) {
25
+ const keys = new Set();
26
+ const add = (provider, model) => {
27
+ if (model)
28
+ keys.add(`${provider}/${model}`);
29
+ };
30
+ for (const role of ["generator", "mutator", "judge", "finalizer"]) {
31
+ add(config.provider, config.models[role]);
32
+ const route = config.roleProviders?.[role];
33
+ if (route)
34
+ add(route.provider, route.model);
35
+ }
36
+ return defaultProviderPricing.filter((price) => keys.has(`${price.provider}/${price.model}`));
37
+ }
38
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAC,MAAM,sBAAsB,GAAiB;IAClD;QACE,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,mBAAmB;QAC1B,kBAAkB,EAAE,IAAI;QACxB,0BAA0B,EAAE,MAAM;QAClC,2BAA2B,EAAE,IAAI;QACjC,mBAAmB,EAAE,IAAI;QACzB,MAAM,EAAE,oDAAoD;QAC5D,IAAI,EAAE,8HAA8H;KACrI;IACD;QACE,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,iBAAiB;QACxB,kBAAkB,EAAE,KAAK;QACzB,0BAA0B,EAAE,QAAQ;QACpC,2BAA2B,EAAE,KAAK;QAClC,mBAAmB,EAAE,IAAI;QACzB,MAAM,EAAE,oDAAoD;QAC5D,IAAI,EAAE,yIAAyI;KAChJ;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAiB,sBAAsB,CAAC;AAEpE,MAAM,UAAU,8BAA8B,CAAC,MAAsB;IACnE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAG,CAAC,QAAgB,EAAE,KAAyB,EAAE,EAAE;QAC1D,IAAI,KAAK;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,CAAmB,EAAE,CAAC;QACpF,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,KAAK;YAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,sBAAsB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AAChG,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ModelDriver, ProviderConfig } from "./types.js";
2
+ export declare function createDriver(config: ProviderConfig): ModelDriver;
@@ -0,0 +1,71 @@
1
+ import { FakeDriver } from "./fake.js";
2
+ import { createDeepSeekDriver, OpenAiCompatibleDriver } from "./openaiCompatible.js";
3
+ import { ProviderError } from "@deepthonk/core";
4
+ export function createDriver(config) {
5
+ const baseDriver = createSingleDriver({ ...config, roleProviders: undefined });
6
+ if (config.roleProviders)
7
+ return new RoleRoutingDriver(baseDriver, config.roleProviders);
8
+ return baseDriver;
9
+ }
10
+ function createSingleDriver(config) {
11
+ if (config.provider === "fake")
12
+ return new FakeDriver();
13
+ if (config.provider === "deepseek")
14
+ return createDeepSeekDriver(config);
15
+ if (config.provider === "openai-compatible")
16
+ return new OpenAiCompatibleDriver(config);
17
+ if (config.baseUrl)
18
+ return new OpenAiCompatibleDriver(config);
19
+ throw new ProviderError(`Unknown provider: ${config.provider}. Use provider "openai-compatible" or provide baseUrl/apiKeyEnv for OpenAI-compatible providers.`, {
20
+ code: "provider.unknown_provider",
21
+ retryable: false,
22
+ fix: "Set provider to fake, deepseek, openrouter, openai-compatible, or provide baseUrl/apiKeyEnv for a custom OpenAI-compatible provider."
23
+ });
24
+ }
25
+ class RoleRoutingDriver {
26
+ baseDriver;
27
+ routes;
28
+ constructor(baseDriver, roleProviders) {
29
+ this.baseDriver = baseDriver;
30
+ this.routes = Object.fromEntries(Object.entries(roleProviders).map(([role, roleConfig]) => [role, { driver: createSingleDriver(roleProviderToConfig(roleConfig)), model: roleConfig.model }]));
31
+ }
32
+ generate(input) {
33
+ return this.route("generator").driver.generate(this.withRouteModel(input, "generator"));
34
+ }
35
+ compare(input) {
36
+ return this.route("judge").driver.compare(this.withRouteModel(input, "judge"));
37
+ }
38
+ mutate(input) {
39
+ return this.route("mutator").driver.mutate(this.withRouteModel(input, "mutator"));
40
+ }
41
+ finalize(input) {
42
+ const route = this.route("finalizer");
43
+ if (!route.driver.finalize)
44
+ return this.baseDriver.finalize?.(input) ?? Promise.resolve({ text: input.candidate.content });
45
+ return route.driver.finalize(this.withRouteModel(input, "finalizer"));
46
+ }
47
+ route(role) {
48
+ return this.routes[role] ?? { driver: this.baseDriver };
49
+ }
50
+ withRouteModel(input, role) {
51
+ const model = this.routes[role]?.model;
52
+ return model ? { ...input, model } : input;
53
+ }
54
+ }
55
+ function roleProviderToConfig(config) {
56
+ return {
57
+ provider: config.provider,
58
+ baseUrl: config.baseUrl,
59
+ apiKeyEnv: config.apiKeyEnv,
60
+ apiKey: config.apiKey,
61
+ models: {
62
+ generator: config.model,
63
+ mutator: config.model,
64
+ judge: config.model,
65
+ finalizer: config.model
66
+ },
67
+ retry: config.retry,
68
+ supportsJsonMode: config.supportsJsonMode
69
+ };
70
+ }
71
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export type { CompareInput, FinalizeInput, GenerateInput, ModelDriver, ModelTextResult, MutateInput } from "@deepthonk/core";
2
+ export type ProviderRole = "generator" | "mutator" | "judge" | "finalizer";
3
+ export interface RoleProviderConfig {
4
+ provider: string;
5
+ baseUrl?: string;
6
+ apiKeyEnv?: string;
7
+ apiKey?: string;
8
+ model: string;
9
+ retry?: {
10
+ httpRetries?: number;
11
+ requestTimeoutMs?: number;
12
+ };
13
+ supportsJsonMode?: boolean;
14
+ }
15
+ export interface ProviderConfig {
16
+ provider: "fake" | "openai-compatible" | "deepseek" | string;
17
+ baseUrl?: string;
18
+ apiKeyEnv?: string;
19
+ apiKey?: string;
20
+ models: {
21
+ generator: string;
22
+ mutator: string;
23
+ judge: string;
24
+ finalizer?: string;
25
+ };
26
+ retry?: {
27
+ httpRetries?: number;
28
+ requestTimeoutMs?: number;
29
+ };
30
+ supportsJsonMode?: boolean;
31
+ roleProviders?: Partial<Record<ProviderRole, RoleProviderConfig>>;
32
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@deepthonk/providers",
3
+ "version": "0.1.0",
4
+ "description": "Provider drivers and routing for DeepThonk.",
5
+ "license": "MIT",
6
+ "author": "Xule Lin",
7
+ "homepage": "https://github.com/linxule/deepthonk#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/linxule/deepthonk/issues"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "files": [
18
+ "dist",
19
+ "package.json",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
27
+ }
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/linxule/deepthonk.git",
32
+ "directory": "packages/providers"
33
+ },
34
+ "dependencies": {
35
+ "zod": "^3.25.28",
36
+ "@deepthonk/core": "0.1.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc -p tsconfig.json",
40
+ "typecheck": "tsc -p tsconfig.json --noEmit"
41
+ }
42
+ }