@warlock.js/ai-deepseek 4.6.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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog — @warlock.js/ai-deepseek
2
+
3
+ All notable changes to `@warlock.js/ai-deepseek` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
+
7
+ ## 4.6.0
8
+
9
+ ### Added
10
+
11
+ - **First release.** DeepSeek adapter for `@warlock.js/ai` — a thin wrapper over `@warlock.js/ai-openai`'s `OpenAISDK` pinned to `https://api.deepseek.com` (`provider: "deepseek"`), so all wire behavior (streaming, tool calls, structured output, error wrapping) is inherited unchanged.
12
+ - **`DeepSeekSDK`** — `.model()` / `.embedder()` / `.image()` / `.count()` delegated to the wrapped client. `baseURL` and `provider` are optional (default to DeepSeek's endpoint / label); every other `openai` `ClientOptions` value is forwarded verbatim.
13
+ - **DeepSeek-specific capability inference** (`inferReasoningCapability` / `inferVisionCapability`) — `reasoning` is auto-`true` for `deepseek-reasoner` and the `*-pro` tier, auto-`false` for `deepseek-chat` / `*-flash`; `vision` is `false` for every id (no documented vision surface). An explicit `reasoning` / `vision` per model always wins.
14
+ - **Built-in DeepSeek pricing defaults** (USD per 1M tokens) for `deepseek-chat`, `deepseek-reasoner`, `deepseek-v4-flash`, `deepseek-v4-pro`, so `usage.cost` is computed out of the box; overridable per model or per SDK.
15
+ - **`DEEPSEEK_CHAT_MODELS`** — informational list of the documented chat model ids.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Hassan Zohdy
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,125 @@
1
+ # @warlock.js/ai-deepseek
2
+
3
+ DeepSeek adapter for [`@warlock.js/ai`](../ai). DeepSeek's API is **OpenAI-compatible** on the wire, so this package is a **thin wrapper** over [`@warlock.js/ai-openai`](../ai-openai)'s `OpenAISDK`: it constructs one internal `OpenAISDK` pinned to `https://api.deepseek.com` with `provider: "deepseek"` and delegates `model()` / `embedder()` / `image()` / `count()` to it — while injecting DeepSeek's own capability inference and built-in pricing so the right flags are set even though the model names aren't OpenAI names.
4
+
5
+ ```bash
6
+ npm install @warlock.js/ai @warlock.js/ai-deepseek @warlock.js/seal openai
7
+ ```
8
+
9
+ > `@warlock.js/seal` is the recommended Standard Schema library for tool inputs and structured output. Any Standard Schema V1 library works (Zod, Valibot, …). `openai` is required because this adapter wraps `@warlock.js/ai-openai`.
10
+
11
+ ## Quick start
12
+
13
+ ```ts
14
+ import { DeepSeekSDK } from "@warlock.js/ai-deepseek";
15
+ import { ai } from "@warlock.js/ai";
16
+
17
+ const deepseek = new DeepSeekSDK({ apiKey: process.env.DEEPSEEK_API_KEY! });
18
+
19
+ const myAgent = ai.agent({
20
+ model: deepseek.model({ name: "deepseek-chat" }),
21
+ });
22
+
23
+ const result = await myAgent.execute("Hello!");
24
+ console.log(result.text);
25
+ ```
26
+
27
+ `apiKey` is the only required field — `baseURL` defaults to `https://api.deepseek.com` (DeepSeek's OpenAI-compatible endpoint) and `provider` defaults to `"deepseek"`. `DeepSeekSDK` is a class holding one long-lived wrapped client; construct one per account and reuse it.
28
+
29
+ ## API surface
30
+
31
+ ```ts
32
+ new DeepSeekSDK(config: DeepSeekSDKConfig) // Omit<OpenAISDKConfig, "baseURL" | "provider"> + optional baseURL/provider
33
+ .model(config: DeepSeekModelConfig) // → ModelContract
34
+ .embedder(config: DeepSeekEmbedderConfig) // → EmbedderContract
35
+ .image(config: DeepSeekImageConfig) // → ImageModelContract
36
+ .count(text, model?) // approximate token count
37
+
38
+ DeepSeekModelConfig {
39
+ name: string; // e.g. "deepseek-chat", "deepseek-reasoner"
40
+ temperature?: number;
41
+ maxTokens?: number;
42
+ vision?: boolean; // override auto-inference (always false today)
43
+ reasoning?: boolean; // override auto-inference
44
+ structuredOutput?: boolean; // override; defaults true
45
+ responseFormat?: "json_schema" | "json_object" | "text";
46
+ pdf?: boolean; // opt into PDF input
47
+ audio?: boolean; // opt into audio input
48
+ // ...any other ModelConfig field forwarded to the wrapped OpenAI model
49
+ }
50
+ ```
51
+
52
+ ## Models
53
+
54
+ | Model | Notes |
55
+ | ------------------- | --------------------------------------------------------------------- |
56
+ | `deepseek-chat` | Non-thinking surface (V3 lineage). Fast, cheap. Reasoning **off**. |
57
+ | `deepseek-reasoner` | Thinking surface (R1 lineage). Emits a reasoning channel. Reasoning **on**. |
58
+ | `deepseek-v4-flash` | Newer flash tier. Reasoning **off** by default. |
59
+ | `deepseek-v4-pro` | Newer quality tier. Reasoning **on** by default. |
60
+
61
+ DeepSeek announced the legacy `deepseek-chat` / `deepseek-reasoner` names retire 2026/07/24 in favor of the `deepseek-v4-*` family; both name sets are understood by the capability inference. `deepseek.model({ name })` accepts any id the upstream serves. `DEEPSEEK_CHAT_MODELS` exports the informational list.
62
+
63
+ ## Capabilities
64
+
65
+ Capabilities are inferred from the DeepSeek model **name** (via `inferReasoningCapability` / `inferVisionCapability`) and injected as explicit flags before delegating — an explicit value always wins.
66
+
67
+ | Capability | Default |
68
+ | ------------------ | --------------------------------------------------------------------------------------- |
69
+ | `reasoning` | `true` for `deepseek-reasoner` and the `*-pro` tier; `false` for `deepseek-chat` / `*-flash`. |
70
+ | `vision` | `false` for every DeepSeek model — no chat model documents image input (mid-2026). |
71
+ | `structuredOutput` | `true` (inherited from the wrapped OpenAI model), unless `responseFormat` forces a loose mode. |
72
+ | `promptCaching` | `true` (inherited). DeepSeek reports cache hits via `usage.cachedTokens`. |
73
+
74
+ ```ts
75
+ deepseek.model({ name: "deepseek-chat", reasoning: true }); // force the thinking flag
76
+ deepseek.model({ name: "deepseek-reasoner", reasoning: false }); // suppress it
77
+ ```
78
+
79
+ ## Reasoning effort
80
+
81
+ ```ts
82
+ const model = deepseek.model({ name: "deepseek-reasoner" }); // reasoning auto-true
83
+ await model.complete(messages, { reasoning: { effort: "high" } }); // → reasoning_effort: "high"
84
+ ```
85
+
86
+ `reasoning.effort` maps to the OpenAI-compatible `reasoning_effort` param and is dropped for a non-reasoning model.
87
+
88
+ ## Pricing
89
+
90
+ The adapter ships **built-in DeepSeek pricing defaults** (USD per 1M tokens, from DeepSeek's published table), so `usage.cost` is computed out of the box. Resolution at `model()` time: per-model `pricing` > SDK `pricing` registry > built-in DeepSeek default > `undefined`.
91
+
92
+ ```ts
93
+ new DeepSeekSDK({
94
+ apiKey,
95
+ pricing: { "deepseek-reasoner": { input: 0.55, output: 2.19 } },
96
+ });
97
+ ```
98
+
99
+ ## Embeddings & images
100
+
101
+ `deepseek.embedder({...})` and `deepseek.image({...})` delegate to the wrapped `OpenAISDK`. **DeepSeek does not officially document an OpenAI-compatible embeddings endpoint or an image-generation endpoint (mid-2026)** — these delegates exist for adapter parity and work against a compatible gateway you point `baseURL` at, but may fail against the stock DeepSeek endpoint. Use a dedicated embeddings provider (e.g. `@warlock.js/ai-openai` / `@warlock.js/ai-google`) for RAG.
102
+
103
+ ## OpenAI-compatible endpoints
104
+
105
+ Override `baseURL` (proxy / gateway) or `provider` (relabel the upstream):
106
+
107
+ ```ts
108
+ new DeepSeekSDK({
109
+ apiKey,
110
+ baseURL: "https://my-gateway.example.com/deepseek",
111
+ provider: "deepseek-proxy",
112
+ });
113
+ ```
114
+
115
+ ## Tests
116
+
117
+ ```bash
118
+ npm test
119
+ ```
120
+
121
+ Covers DeepSeek capability inference and the thin-wrapper delegation to `OpenAISDK`.
122
+
123
+ ## License
124
+
125
+ MIT
package/cjs/index.cjs ADDED
@@ -0,0 +1,255 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _warlock_js_ai_openai = require("@warlock.js/ai-openai");
3
+
4
+ //#region ../@warlock.js/ai-deepseek/src/known-models.ts
5
+ /**
6
+ * Capability inference for DeepSeek chat model ids.
7
+ *
8
+ * DeepSeek's API is OpenAI-compatible on the wire (`@warlock.js/ai-deepseek`
9
+ * wraps {@link OpenAISDK}), but its model **names** are not OpenAI names —
10
+ * so OpenAI's own prefix lists (`gpt-4o`, `o3`, …) never match them. This
11
+ * module supplies DeepSeek's OWN name-based inference, which the
12
+ * {@link DeepSeekSDK} injects as explicit capability booleans into the
13
+ * wrapped `openai.model(...)` call (explicit config wins over OpenAI's
14
+ * inference inside `OpenAIModel`).
15
+ *
16
+ * Verified against the official DeepSeek API docs (api-docs.deepseek.com,
17
+ * mid-2026): base URL `https://api.deepseek.com`, OpenAI-compatible Chat
18
+ * Completions. Two reasoning-capable surfaces exist — the legacy
19
+ * `deepseek-reasoner` (thinking mode) and the `*-pro` tier — while
20
+ * `deepseek-chat` is the non-thinking surface. No DeepSeek chat model
21
+ * documents image (vision) input, so vision inference is conservatively
22
+ * `false` for every id.
23
+ */
24
+ /**
25
+ * Default DeepSeek chat model ids, surfaced for discovery and docs.
26
+ *
27
+ * - `deepseek-chat` — the non-thinking surface (V3 lineage). Fast, cheap,
28
+ * broad production coverage. Not reasoning-capable.
29
+ * - `deepseek-reasoner` — the thinking surface (R1 lineage). Emits a
30
+ * reasoning channel and accepts the reasoning-effort knob.
31
+ *
32
+ * Both legacy names are scheduled for retirement (2026/07/24 per DeepSeek's
33
+ * deprecation notice) in favor of the `deepseek-v4-*` family
34
+ * (`deepseek-v4-flash`, `deepseek-v4-pro`), which this inference also
35
+ * understands. The list is informational only — `deepseek.model({ name })`
36
+ * accepts any id the upstream serves.
37
+ */
38
+ const DEEPSEEK_CHAT_MODELS = [
39
+ "deepseek-chat",
40
+ "deepseek-reasoner",
41
+ "deepseek-v4-flash",
42
+ "deepseek-v4-pro"
43
+ ];
44
+ /**
45
+ * Substrings identifying DeepSeek model ids that expose a reasoning /
46
+ * thinking channel and accept the reasoning-effort knob.
47
+ *
48
+ * - `deepseek-reasoner` — the dedicated thinking surface (R1 lineage).
49
+ * - `-pro` — the V4 quality tier (`deepseek-v4-pro`), which thinks by
50
+ * default per DeepSeek's docs.
51
+ *
52
+ * `deepseek-chat` (the V3 non-thinking surface) and `deepseek-v4-flash`
53
+ * stay `false`. A substring match tolerates date/preview suffixes the
54
+ * provider may append. Override per-model via
55
+ * `deepseek.model({ name, reasoning: true | false })`.
56
+ */
57
+ const REASONING_CAPABLE_SUBSTRINGS = ["deepseek-reasoner", "-pro"];
58
+ /**
59
+ * Infer whether a DeepSeek model id is reasoning-capable based on the
60
+ * known substrings. Unknown ids default to `false` so the adapter never
61
+ * forwards an unsupported reasoning param to a non-thinking model.
62
+ *
63
+ * @example
64
+ * inferReasoningCapability("deepseek-reasoner"); // → true
65
+ * inferReasoningCapability("deepseek-v4-pro"); // → true
66
+ * inferReasoningCapability("deepseek-chat"); // → false
67
+ * inferReasoningCapability("deepseek-v4-flash"); // → false
68
+ */
69
+ function inferReasoningCapability(modelId) {
70
+ const normalized = modelId.toLowerCase();
71
+ return REASONING_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));
72
+ }
73
+ /**
74
+ * Substrings identifying DeepSeek model ids that accept image (vision)
75
+ * input.
76
+ *
77
+ * Currently **empty**: as of mid-2026, no DeepSeek chat model documents
78
+ * vision input on the OpenAI-compatible Chat Completions endpoint, so
79
+ * inference returns `false` for every id and passing an image attachment
80
+ * surfaces a clear, agent-side capability error instead of an opaque 400.
81
+ * Append a fragment here (and a paired spec case) the day DeepSeek ships a
82
+ * multimodal chat model. Devs can always opt in per-model via
83
+ * `deepseek.model({ name, vision: true })`.
84
+ */
85
+ const VISION_CAPABLE_SUBSTRINGS = [];
86
+ /**
87
+ * Infer whether a DeepSeek model id supports vision based on the known
88
+ * substrings. Today this is always `false` (see
89
+ * {@link VISION_CAPABLE_SUBSTRINGS}); kept as a function so the
90
+ * {@link DeepSeekSDK} call site and the override semantics stay identical
91
+ * to the other adapters, ready for a future multimodal id.
92
+ *
93
+ * @example
94
+ * inferVisionCapability("deepseek-chat"); // → false
95
+ * inferVisionCapability("deepseek-reasoner"); // → false
96
+ */
97
+ function inferVisionCapability(modelId) {
98
+ const normalized = modelId.toLowerCase();
99
+ return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));
100
+ }
101
+
102
+ //#endregion
103
+ //#region ../@warlock.js/ai-deepseek/src/sdk.ts
104
+ /**
105
+ * DeepSeek's OpenAI-compatible Chat Completions endpoint. Verified against
106
+ * the official API docs (api-docs.deepseek.com, mid-2026).
107
+ */
108
+ const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
109
+ /** Default upstream label for models produced by this SDK. */
110
+ const DEEPSEEK_PROVIDER = "deepseek";
111
+ /**
112
+ * Built-in DeepSeek pricing defaults, in USD per 1,000,000 tokens, keyed
113
+ * by model name. Sourced from DeepSeek's official pricing table
114
+ * (mid-2026); `cachedInput` is the cache-hit input rate. A caller-supplied
115
+ * SDK `pricing` registry, or a per-model `pricing`, overrides any entry
116
+ * here. Update when DeepSeek revises its published rates.
117
+ */
118
+ const DEEPSEEK_DEFAULT_PRICING = {
119
+ "deepseek-chat": {
120
+ input: .14,
121
+ output: .28,
122
+ cachedInput: .0028
123
+ },
124
+ "deepseek-reasoner": {
125
+ input: .14,
126
+ output: .28,
127
+ cachedInput: .0028
128
+ },
129
+ "deepseek-v4-flash": {
130
+ input: .14,
131
+ output: .28,
132
+ cachedInput: .0028
133
+ },
134
+ "deepseek-v4-pro": {
135
+ input: .435,
136
+ output: .87,
137
+ cachedInput: .003625
138
+ }
139
+ };
140
+ /**
141
+ * DeepSeek-backed implementation of `SDKAdapterContract`.
142
+ *
143
+ * **Role.** The package entry point for DeepSeek models. DeepSeek's API is
144
+ * OpenAI-compatible on the wire, so rather than reimplementing the
145
+ * battle-tested Chat Completions translation, this is a **thin wrapper**
146
+ * over {@link OpenAISDK} from `@warlock.js/ai-openai`: it constructs one
147
+ * internal `OpenAISDK` pinned to DeepSeek's `baseURL` and the `"deepseek"`
148
+ * provider label, and delegates `model()` / `embedder()` / `image()` /
149
+ * `count()` straight to it. Construct one `DeepSeekSDK` per account and
150
+ * reuse it everywhere.
151
+ *
152
+ * **What the wrapper adds over a bare `OpenAISDK`.** DeepSeek's model
153
+ * **names** are not OpenAI names, so OpenAI's own vision/reasoning prefix
154
+ * lists never match them. This wrapper injects DeepSeek's OWN capability
155
+ * inference (see `known-models.ts`) and built-in DeepSeek pricing defaults
156
+ * as explicit values on every `model()` call — so `deepseek-reasoner` is
157
+ * correctly flagged reasoning-capable and `deepseek-chat` is not, even
158
+ * though neither matches an OpenAI prefix. An explicit `vision` /
159
+ * `reasoning` / `pricing` you pass per model always wins over these
160
+ * injected defaults.
161
+ *
162
+ * **Responsibility.**
163
+ * - Owns: one long-lived internal `OpenAISDK` (auth, DeepSeek base URL)
164
+ * and DeepSeek's capability + pricing defaults.
165
+ * - Does NOT own: the wire protocol, streaming loop, message/tool
166
+ * translation, or error wrapping — all inherited unchanged from
167
+ * `OpenAISDK` / `OpenAIModel`.
168
+ *
169
+ * @example
170
+ * const deepseek = new DeepSeekSDK({ apiKey: process.env.DEEPSEEK_API_KEY! });
171
+ * const chat = deepseek.model({ name: "deepseek-chat" }); // reasoning=false
172
+ * const reasoner = deepseek.model({ name: "deepseek-reasoner" }); // reasoning=true
173
+ *
174
+ * @example
175
+ * // Compose into an `ai.deepseek` namespace for ergonomic agent wiring
176
+ * const ai = { agent, tool, deepseek: new DeepSeekSDK({ apiKey }) };
177
+ * const myAgent = ai.agent({ model: ai.deepseek.model({ name: "deepseek-reasoner" }) });
178
+ */
179
+ var DeepSeekSDK = class {
180
+ constructor(config) {
181
+ const { provider, pricing, baseURL, ...clientOptions } = config;
182
+ this.provider = provider ?? DEEPSEEK_PROVIDER;
183
+ this.pricing = {
184
+ ...DEEPSEEK_DEFAULT_PRICING,
185
+ ...pricing
186
+ };
187
+ this.openai = new _warlock_js_ai_openai.OpenAISDK({
188
+ ...clientOptions,
189
+ baseURL: baseURL ?? DEEPSEEK_BASE_URL,
190
+ provider: this.provider
191
+ });
192
+ }
193
+ /**
194
+ * Build a `ModelContract` for a DeepSeek model. Delegates to the wrapped
195
+ * `OpenAISDK.model()`, but first injects this provider's defaults:
196
+ * - `vision` / `reasoning` inferred from DeepSeek's model-name lists
197
+ * (`known-models.ts`) — so the right capabilities are set even though
198
+ * the names aren't OpenAI names.
199
+ * - `pricing` resolved from the built-in DeepSeek table merged with any
200
+ * caller-supplied SDK registry.
201
+ *
202
+ * An explicit `vision` / `reasoning` / `pricing` on `config` always wins
203
+ * over the injected default. Everything else (sampling, structured
204
+ * output, streaming, error wrapping) is inherited unchanged from
205
+ * `OpenAIModel`.
206
+ */
207
+ model(config) {
208
+ const resolved = {
209
+ ...config,
210
+ vision: config.vision ?? inferVisionCapability(config.name),
211
+ reasoning: config.reasoning ?? inferReasoningCapability(config.name),
212
+ pricing: config.pricing ?? this.pricing[config.name]
213
+ };
214
+ return this.openai.model(resolved);
215
+ }
216
+ /**
217
+ * Rough offline token-count estimate. Delegated to the wrapped
218
+ * `OpenAISDK.count()` (the core character-heuristic) — good for
219
+ * budgeting/quota guards, not billing.
220
+ */
221
+ async count(text, model) {
222
+ return this.openai.count(text, model);
223
+ }
224
+ /**
225
+ * Build an `EmbedderContract`. Delegated verbatim to
226
+ * `OpenAISDK.embedder()`.
227
+ *
228
+ * NOTE: DeepSeek does not officially document an OpenAI-compatible
229
+ * embeddings endpoint (mid-2026); this delegate works against a
230
+ * compatible gateway you point `baseURL` at, but may fail against the
231
+ * stock DeepSeek endpoint. Prefer a dedicated embeddings provider for
232
+ * RAG.
233
+ */
234
+ embedder(config) {
235
+ return this.openai.embedder(config);
236
+ }
237
+ /**
238
+ * Build an `ImageModelContract`. Delegated verbatim to
239
+ * `OpenAISDK.image()`.
240
+ *
241
+ * NOTE: DeepSeek does not document an image-generation endpoint
242
+ * (mid-2026); provided for adapter parity. A non-image model id is
243
+ * rejected at construction by the wrapped OpenAI image model.
244
+ */
245
+ image(config) {
246
+ return this.openai.image(config);
247
+ }
248
+ };
249
+
250
+ //#endregion
251
+ exports.DEEPSEEK_CHAT_MODELS = DEEPSEEK_CHAT_MODELS;
252
+ exports.DeepSeekSDK = DeepSeekSDK;
253
+ exports.inferReasoningCapability = inferReasoningCapability;
254
+ exports.inferVisionCapability = inferVisionCapability;
255
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["OpenAISDK"],"sources":["../../../../../../@warlock.js/ai-deepseek/src/known-models.ts","../../../../../../@warlock.js/ai-deepseek/src/sdk.ts"],"sourcesContent":["/**\n * Capability inference for DeepSeek chat model ids.\n *\n * DeepSeek's API is OpenAI-compatible on the wire (`@warlock.js/ai-deepseek`\n * wraps {@link OpenAISDK}), but its model **names** are not OpenAI names —\n * so OpenAI's own prefix lists (`gpt-4o`, `o3`, …) never match them. This\n * module supplies DeepSeek's OWN name-based inference, which the\n * {@link DeepSeekSDK} injects as explicit capability booleans into the\n * wrapped `openai.model(...)` call (explicit config wins over OpenAI's\n * inference inside `OpenAIModel`).\n *\n * Verified against the official DeepSeek API docs (api-docs.deepseek.com,\n * mid-2026): base URL `https://api.deepseek.com`, OpenAI-compatible Chat\n * Completions. Two reasoning-capable surfaces exist — the legacy\n * `deepseek-reasoner` (thinking mode) and the `*-pro` tier — while\n * `deepseek-chat` is the non-thinking surface. No DeepSeek chat model\n * documents image (vision) input, so vision inference is conservatively\n * `false` for every id.\n */\n\n/**\n * Default DeepSeek chat model ids, surfaced for discovery and docs.\n *\n * - `deepseek-chat` — the non-thinking surface (V3 lineage). Fast, cheap,\n * broad production coverage. Not reasoning-capable.\n * - `deepseek-reasoner` — the thinking surface (R1 lineage). Emits a\n * reasoning channel and accepts the reasoning-effort knob.\n *\n * Both legacy names are scheduled for retirement (2026/07/24 per DeepSeek's\n * deprecation notice) in favor of the `deepseek-v4-*` family\n * (`deepseek-v4-flash`, `deepseek-v4-pro`), which this inference also\n * understands. The list is informational only — `deepseek.model({ name })`\n * accepts any id the upstream serves.\n */\nexport const DEEPSEEK_CHAT_MODELS = [\n \"deepseek-chat\",\n \"deepseek-reasoner\",\n \"deepseek-v4-flash\",\n \"deepseek-v4-pro\",\n] as const;\n\n/**\n * Substrings identifying DeepSeek model ids that expose a reasoning /\n * thinking channel and accept the reasoning-effort knob.\n *\n * - `deepseek-reasoner` — the dedicated thinking surface (R1 lineage).\n * - `-pro` — the V4 quality tier (`deepseek-v4-pro`), which thinks by\n * default per DeepSeek's docs.\n *\n * `deepseek-chat` (the V3 non-thinking surface) and `deepseek-v4-flash`\n * stay `false`. A substring match tolerates date/preview suffixes the\n * provider may append. Override per-model via\n * `deepseek.model({ name, reasoning: true | false })`.\n */\nconst REASONING_CAPABLE_SUBSTRINGS = [\"deepseek-reasoner\", \"-pro\"];\n\n/**\n * Infer whether a DeepSeek model id is reasoning-capable based on the\n * known substrings. Unknown ids default to `false` so the adapter never\n * forwards an unsupported reasoning param to a non-thinking model.\n *\n * @example\n * inferReasoningCapability(\"deepseek-reasoner\"); // → true\n * inferReasoningCapability(\"deepseek-v4-pro\"); // → true\n * inferReasoningCapability(\"deepseek-chat\"); // → false\n * inferReasoningCapability(\"deepseek-v4-flash\"); // → false\n */\nexport function inferReasoningCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return REASONING_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n\n/**\n * Substrings identifying DeepSeek model ids that accept image (vision)\n * input.\n *\n * Currently **empty**: as of mid-2026, no DeepSeek chat model documents\n * vision input on the OpenAI-compatible Chat Completions endpoint, so\n * inference returns `false` for every id and passing an image attachment\n * surfaces a clear, agent-side capability error instead of an opaque 400.\n * Append a fragment here (and a paired spec case) the day DeepSeek ships a\n * multimodal chat model. Devs can always opt in per-model via\n * `deepseek.model({ name, vision: true })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS: readonly string[] = [];\n\n/**\n * Infer whether a DeepSeek model id supports vision based on the known\n * substrings. Today this is always `false` (see\n * {@link VISION_CAPABLE_SUBSTRINGS}); kept as a function so the\n * {@link DeepSeekSDK} call site and the override semantics stay identical\n * to the other adapters, ready for a future multimodal id.\n *\n * @example\n * inferVisionCapability(\"deepseek-chat\"); // → false\n * inferVisionCapability(\"deepseek-reasoner\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n","import type {\n EmbedderContract,\n ImageModelContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { OpenAISDK } from \"@warlock.js/ai-openai\";\nimport type {\n DeepSeekEmbedderConfig,\n DeepSeekImageConfig,\n DeepSeekModelConfig,\n DeepSeekSDKConfig,\n} from \"./config.type\";\nimport { inferReasoningCapability, inferVisionCapability } from \"./known-models\";\n\n/**\n * DeepSeek's OpenAI-compatible Chat Completions endpoint. Verified against\n * the official API docs (api-docs.deepseek.com, mid-2026).\n */\nconst DEEPSEEK_BASE_URL = \"https://api.deepseek.com\";\n\n/** Default upstream label for models produced by this SDK. */\nconst DEEPSEEK_PROVIDER = \"deepseek\";\n\n/**\n * Built-in DeepSeek pricing defaults, in USD per 1,000,000 tokens, keyed\n * by model name. Sourced from DeepSeek's official pricing table\n * (mid-2026); `cachedInput` is the cache-hit input rate. A caller-supplied\n * SDK `pricing` registry, or a per-model `pricing`, overrides any entry\n * here. Update when DeepSeek revises its published rates.\n */\nconst DEEPSEEK_DEFAULT_PRICING: Record<string, ModelPricing> = {\n \"deepseek-chat\": { input: 0.14, output: 0.28, cachedInput: 0.0028 },\n \"deepseek-reasoner\": { input: 0.14, output: 0.28, cachedInput: 0.0028 },\n \"deepseek-v4-flash\": { input: 0.14, output: 0.28, cachedInput: 0.0028 },\n \"deepseek-v4-pro\": { input: 0.435, output: 0.87, cachedInput: 0.003625 },\n};\n\n/**\n * DeepSeek-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for DeepSeek models. DeepSeek's API is\n * OpenAI-compatible on the wire, so rather than reimplementing the\n * battle-tested Chat Completions translation, this is a **thin wrapper**\n * over {@link OpenAISDK} from `@warlock.js/ai-openai`: it constructs one\n * internal `OpenAISDK` pinned to DeepSeek's `baseURL` and the `\"deepseek\"`\n * provider label, and delegates `model()` / `embedder()` / `image()` /\n * `count()` straight to it. Construct one `DeepSeekSDK` per account and\n * reuse it everywhere.\n *\n * **What the wrapper adds over a bare `OpenAISDK`.** DeepSeek's model\n * **names** are not OpenAI names, so OpenAI's own vision/reasoning prefix\n * lists never match them. This wrapper injects DeepSeek's OWN capability\n * inference (see `known-models.ts`) and built-in DeepSeek pricing defaults\n * as explicit values on every `model()` call — so `deepseek-reasoner` is\n * correctly flagged reasoning-capable and `deepseek-chat` is not, even\n * though neither matches an OpenAI prefix. An explicit `vision` /\n * `reasoning` / `pricing` you pass per model always wins over these\n * injected defaults.\n *\n * **Responsibility.**\n * - Owns: one long-lived internal `OpenAISDK` (auth, DeepSeek base URL)\n * and DeepSeek's capability + pricing defaults.\n * - Does NOT own: the wire protocol, streaming loop, message/tool\n * translation, or error wrapping — all inherited unchanged from\n * `OpenAISDK` / `OpenAIModel`.\n *\n * @example\n * const deepseek = new DeepSeekSDK({ apiKey: process.env.DEEPSEEK_API_KEY! });\n * const chat = deepseek.model({ name: \"deepseek-chat\" }); // reasoning=false\n * const reasoner = deepseek.model({ name: \"deepseek-reasoner\" }); // reasoning=true\n *\n * @example\n * // Compose into an `ai.deepseek` namespace for ergonomic agent wiring\n * const ai = { agent, tool, deepseek: new DeepSeekSDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.deepseek.model({ name: \"deepseek-reasoner\" }) });\n */\nexport class DeepSeekSDK implements SDKAdapterContract {\n /** The wrapped OpenAI-compatible client, pinned to DeepSeek. */\n private readonly openai: OpenAISDK;\n private readonly provider: string;\n private readonly pricing: Record<string, ModelPricing>;\n\n public constructor(config: DeepSeekSDKConfig) {\n const { provider, pricing, baseURL, ...clientOptions } = config;\n\n this.provider = provider ?? DEEPSEEK_PROVIDER;\n // Caller pricing overrides the built-in DeepSeek defaults entry-by-entry.\n this.pricing = { ...DEEPSEEK_DEFAULT_PRICING, ...pricing };\n\n // The internal OpenAISDK carries the DeepSeek base URL + provider label;\n // pricing stays on THIS wrapper so it can be merged with the built-in\n // defaults before each per-model resolution below.\n this.openai = new OpenAISDK({\n ...clientOptions,\n baseURL: baseURL ?? DEEPSEEK_BASE_URL,\n provider: this.provider,\n });\n }\n\n /**\n * Build a `ModelContract` for a DeepSeek model. Delegates to the wrapped\n * `OpenAISDK.model()`, but first injects this provider's defaults:\n * - `vision` / `reasoning` inferred from DeepSeek's model-name lists\n * (`known-models.ts`) — so the right capabilities are set even though\n * the names aren't OpenAI names.\n * - `pricing` resolved from the built-in DeepSeek table merged with any\n * caller-supplied SDK registry.\n *\n * An explicit `vision` / `reasoning` / `pricing` on `config` always wins\n * over the injected default. Everything else (sampling, structured\n * output, streaming, error wrapping) is inherited unchanged from\n * `OpenAIModel`.\n */\n public model(config: DeepSeekModelConfig): ModelContract {\n const resolved: DeepSeekModelConfig = {\n ...config,\n vision: config.vision ?? inferVisionCapability(config.name),\n reasoning: config.reasoning ?? inferReasoningCapability(config.name),\n pricing: config.pricing ?? this.pricing[config.name],\n };\n\n return this.openai.model(resolved);\n }\n\n /**\n * Rough offline token-count estimate. Delegated to the wrapped\n * `OpenAISDK.count()` (the core character-heuristic) — good for\n * budgeting/quota guards, not billing.\n */\n public async count(text: string, model?: string): Promise<number> {\n return this.openai.count(text, model);\n }\n\n /**\n * Build an `EmbedderContract`. Delegated verbatim to\n * `OpenAISDK.embedder()`.\n *\n * NOTE: DeepSeek does not officially document an OpenAI-compatible\n * embeddings endpoint (mid-2026); this delegate works against a\n * compatible gateway you point `baseURL` at, but may fail against the\n * stock DeepSeek endpoint. Prefer a dedicated embeddings provider for\n * RAG.\n */\n public embedder(config: DeepSeekEmbedderConfig): EmbedderContract {\n return this.openai.embedder(config);\n }\n\n /**\n * Build an `ImageModelContract`. Delegated verbatim to\n * `OpenAISDK.image()`.\n *\n * NOTE: DeepSeek does not document an image-generation endpoint\n * (mid-2026); provided for adapter parity. A non-image model id is\n * rejected at construction by the wrapped OpenAI image model.\n */\n public image(config: DeepSeekImageConfig): ImageModelContract {\n return this.openai.image(config);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;AAeA,MAAM,+BAA+B,CAAC,qBAAqB,MAAM;;;;;;;;;;;;AAajE,SAAgB,yBAAyB,SAA0B;CACjE,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,6BAA6B,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACtF;;;;;;;;;;;;;AAcA,MAAM,4BAA+C,CAAC;;;;;;;;;;;;AAatD,SAAgB,sBAAsB,SAA0B;CAC9D,MAAM,aAAa,QAAQ,YAAY;CAEvC,OAAO,0BAA0B,MAAM,aAAa,WAAW,SAAS,QAAQ,CAAC;AACnF;;;;;;;;AClFA,MAAM,oBAAoB;;AAG1B,MAAM,oBAAoB;;;;;;;;AAS1B,MAAM,2BAAyD;CAC7D,iBAAiB;EAAE,OAAO;EAAM,QAAQ;EAAM,aAAa;CAAO;CAClE,qBAAqB;EAAE,OAAO;EAAM,QAAQ;EAAM,aAAa;CAAO;CACtE,qBAAqB;EAAE,OAAO;EAAM,QAAQ;EAAM,aAAa;CAAO;CACtE,mBAAmB;EAAE,OAAO;EAAO,QAAQ;EAAM,aAAa;CAAS;AACzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAa,cAAb,MAAuD;CAMrD,AAAO,YAAY,QAA2B;EAC5C,MAAM,EAAE,UAAU,SAAS,SAAS,GAAG,kBAAkB;EAEzD,KAAK,WAAW,YAAY;EAE5B,KAAK,UAAU;GAAE,GAAG;GAA0B,GAAG;EAAQ;EAKzD,KAAK,SAAS,IAAIA,gCAAU;GAC1B,GAAG;GACH,SAAS,WAAW;GACpB,UAAU,KAAK;EACjB,CAAC;CACH;;;;;;;;;;;;;;;CAgBA,AAAO,MAAM,QAA4C;EACvD,MAAM,WAAgC;GACpC,GAAG;GACH,QAAQ,OAAO,UAAU,sBAAsB,OAAO,IAAI;GAC1D,WAAW,OAAO,aAAa,yBAAyB,OAAO,IAAI;GACnE,SAAS,OAAO,WAAW,KAAK,QAAQ,OAAO;EACjD;EAEA,OAAO,KAAK,OAAO,MAAM,QAAQ;CACnC;;;;;;CAOA,MAAa,MAAM,MAAc,OAAiC;EAChE,OAAO,KAAK,OAAO,MAAM,MAAM,KAAK;CACtC;;;;;;;;;;;CAYA,AAAO,SAAS,QAAkD;EAChE,OAAO,KAAK,OAAO,SAAS,MAAM;CACpC;;;;;;;;;CAUA,AAAO,MAAM,QAAiD;EAC5D,OAAO,KAAK,OAAO,MAAM,MAAM;CACjC;AACF"}
@@ -0,0 +1,146 @@
1
+ import { OpenAISDKConfig } from "@warlock.js/ai-openai";
2
+ import { EmbedderConfig, ImageModelConfig, ModelConfig } from "@warlock.js/ai";
3
+
4
+ //#region ../@warlock.js/ai-deepseek/src/config.type.d.ts
5
+ /**
6
+ * Per-model `response_format` override forwarded to the wrapped OpenAI
7
+ * model. Mirrors the OpenAI adapter's union (DeepSeek speaks the same
8
+ * OpenAI-compatible Chat Completions protocol):
9
+ *
10
+ * - `"json_schema"` — strict token-level shape enforcement.
11
+ * - `"json_object"` — guarantees valid JSON, NOT shape; the agent
12
+ * re-injects the schema as a soft prompt hint.
13
+ * - `"text"` — no `response_format` on the wire; relies on the prompt hint.
14
+ */
15
+ type DeepSeekResponseFormat = "json_schema" | "json_object" | "text";
16
+ /**
17
+ * Configuration for the DeepSeek SDK adapter.
18
+ *
19
+ * DeepSeek's API is OpenAI-compatible on the wire, so this config is the
20
+ * {@link OpenAISDKConfig} shape with one ergonomic change: `baseURL` and
21
+ * `provider` are **optional** because the adapter defaults them to
22
+ * DeepSeek's endpoint (`https://api.deepseek.com`) and the `"deepseek"`
23
+ * label. Pass only your `apiKey` for the common path; every other upstream
24
+ * `openai` `ClientOptions` value (`timeout`, `maxRetries`,
25
+ * `defaultHeaders`, `fetch`, …) is forwarded verbatim to the wrapped
26
+ * `OpenAISDK`.
27
+ *
28
+ * `provider` flows through to `ModelContract.provider`, `AgentReport.model`,
29
+ * logs, and any provider-aware middleware. Defaults to `"deepseek"`.
30
+ *
31
+ * `pricing` is an optional SDK-level registry keyed by model name —
32
+ * resolution at `model()` time is per-model `pricing` > this SDK registry >
33
+ * the adapter's built-in DeepSeek defaults > `undefined`.
34
+ *
35
+ * @example
36
+ * new DeepSeekSDK({ apiKey: process.env.DEEPSEEK_API_KEY! });
37
+ *
38
+ * @example
39
+ * // Override the default endpoint (e.g. a proxy) or the provider label:
40
+ * new DeepSeekSDK({
41
+ * apiKey,
42
+ * baseURL: "https://my-gateway.example.com/deepseek",
43
+ * provider: "deepseek-proxy",
44
+ * });
45
+ *
46
+ * @example
47
+ * new DeepSeekSDK({
48
+ * apiKey,
49
+ * pricing: { "deepseek-chat": { input: 0.14, output: 0.28, cachedInput: 0.0028 } },
50
+ * });
51
+ */
52
+ type DeepSeekSDKConfig = Omit<OpenAISDKConfig, "baseURL" | "provider"> & {
53
+ /**
54
+ * Override the DeepSeek base URL. Defaults to
55
+ * `https://api.deepseek.com` (the OpenAI-compatible endpoint). Set this
56
+ * only to route through a proxy or a self-hosted gateway.
57
+ */
58
+ baseURL?: string;
59
+ /**
60
+ * Override the upstream label. Defaults to `"deepseek"`. Flows through
61
+ * to `ModelContract.provider`, `AgentReport.model`, and logs.
62
+ */
63
+ provider?: string;
64
+ };
65
+ /**
66
+ * Per-model configuration for `DeepSeekSDK.model()`. `name` is a DeepSeek
67
+ * chat model id (e.g. `"deepseek-chat"`, `"deepseek-reasoner"`).
68
+ *
69
+ * Mirrors the OpenAI adapter's per-model overrides field-for-field —
70
+ * DeepSeek runs on the same OpenAI-compatible wire. The difference from
71
+ * using `OpenAISDK` directly is the **default** capability inference:
72
+ * `DeepSeekSDK` derives `vision` and `reasoning` from DeepSeek's own
73
+ * model-name lists (see `known-models.ts`) rather than OpenAI's prefix
74
+ * lists, then injects them as explicit booleans. An explicit value you
75
+ * pass here always wins over that inference.
76
+ *
77
+ * @example
78
+ * deepseek.model({ name: "deepseek-chat" }); // reasoning auto-false
79
+ * deepseek.model({ name: "deepseek-reasoner" }); // reasoning auto-true
80
+ * deepseek.model({ name: "deepseek-chat", reasoning: true }); // explicit override
81
+ */
82
+ type DeepSeekModelConfig = ModelConfig & {
83
+ /**
84
+ * Override the auto-inferred vision capability. When omitted, the
85
+ * adapter infers from the DeepSeek model name (always `false` today —
86
+ * no DeepSeek chat model documents image input). Explicit `true`/`false`
87
+ * always wins.
88
+ */
89
+ vision?: boolean;
90
+ /**
91
+ * Override the auto-inferred reasoning capability. When omitted, the
92
+ * adapter infers from the DeepSeek model name — `true` for
93
+ * `deepseek-reasoner` and the `*-pro` tier, `false` for `deepseek-chat`
94
+ * / `*-flash`. Drives whether the reasoning-effort knob is forwarded.
95
+ * Explicit `true`/`false` always wins.
96
+ */
97
+ reasoning?: boolean;
98
+ /**
99
+ * Override the inferred `structuredOutput` capability. Forwarded to the
100
+ * wrapped OpenAI model, where it defaults to `true` unless
101
+ * `responseFormat` forces a loose mode.
102
+ */
103
+ structuredOutput?: boolean;
104
+ /**
105
+ * Override the wire-level `response_format` the adapter emits for a
106
+ * response schema. Use a loose mode (`"json_object"` / `"text"`) for a
107
+ * route that rejects strict `json_schema`. Forwarded to the wrapped
108
+ * OpenAI model unchanged.
109
+ */
110
+ responseFormat?: DeepSeekResponseFormat;
111
+ /**
112
+ * Opt into PDF / document **input**. Forwarded to the wrapped OpenAI
113
+ * model; off by default.
114
+ */
115
+ pdf?: boolean;
116
+ /**
117
+ * Opt into audio **input**. Forwarded to the wrapped OpenAI model; off
118
+ * by default.
119
+ */
120
+ audio?: boolean;
121
+ };
122
+ /**
123
+ * Per-embedder configuration for `DeepSeekSDK.embedder()`. Mirrors the
124
+ * neutral {@link EmbedderConfig}.
125
+ *
126
+ * NOTE: DeepSeek does not officially document an OpenAI-compatible
127
+ * embeddings endpoint (mid-2026). `embedder()` is delegated to the wrapped
128
+ * `OpenAISDK` for completeness, so it works against any compatible gateway
129
+ * you point `baseURL` at — but calling `.embed()` against the stock
130
+ * `https://api.deepseek.com` endpoint may fail upstream. Prefer a
131
+ * dedicated embeddings provider for RAG.
132
+ */
133
+ type DeepSeekEmbedderConfig = EmbedderConfig;
134
+ /**
135
+ * Per-model configuration for `DeepSeekSDK.image()`. Mirrors the neutral
136
+ * {@link ImageModelConfig}.
137
+ *
138
+ * NOTE: DeepSeek does not document an image-generation endpoint
139
+ * (mid-2026). `image()` is delegated to the wrapped `OpenAISDK` for
140
+ * parity; a non-image model id is rejected by the wrapped OpenAI image
141
+ * model at construction exactly as it would be there.
142
+ */
143
+ type DeepSeekImageConfig = ImageModelConfig;
144
+ //#endregion
145
+ export { DeepSeekEmbedderConfig, DeepSeekImageConfig, DeepSeekModelConfig, DeepSeekSDKConfig };
146
+ //# sourceMappingURL=config.type.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../@warlock.js/ai-deepseek/src/config.type.ts"],"mappings":";;;;;;AAaA;;;;AAAkC;AAsClC;;;KAtCY,sBAAA;;;;;;AAiDF;AAoBV;;;;;;;;;;;;;AAsCO;AAcP;;;;AAAmD;AAWnD;;;;AAAkD;;;;;;;KA9FtC,iBAAA,GAAoB,IAAI,CAAC,eAAA;;;;;;EAMnC,OAAA;;;;;EAKA,QAAA;AAAA;;;;;;;;;;;;;;;;;;KAoBU,mBAAA,GAAsB,WAAA;;;;;;;EAOhC,MAAA;;;;;;;;EAQA,SAAA;;;;;;EAMA,gBAAA;;;;;;;EAOA,cAAA,GAAiB,sBAAsB;;;;;EAKvC,GAAA;;;;;EAKA,KAAA;AAAA;;;;;;;;;;;;KAcU,sBAAA,GAAyB,cAAc;;;;;;;;;;KAWvC,mBAAA,GAAsB,gBAAgB"}
@@ -0,0 +1,4 @@
1
+ import { DeepSeekEmbedderConfig, DeepSeekImageConfig, DeepSeekModelConfig, DeepSeekSDKConfig } from "./config.type.mjs";
2
+ import { DeepSeekSDK } from "./sdk.mjs";
3
+ import { DEEPSEEK_CHAT_MODELS, inferReasoningCapability, inferVisionCapability } from "./known-models.mjs";
4
+ export { DEEPSEEK_CHAT_MODELS, type DeepSeekEmbedderConfig, type DeepSeekImageConfig, type DeepSeekModelConfig, DeepSeekSDK, type DeepSeekSDKConfig, inferReasoningCapability, inferVisionCapability };
package/esm/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { DEEPSEEK_CHAT_MODELS, inferReasoningCapability, inferVisionCapability } from "./known-models.mjs";
2
+ import { DeepSeekSDK } from "./sdk.mjs";
3
+
4
+ export { DEEPSEEK_CHAT_MODELS, DeepSeekSDK, inferReasoningCapability, inferVisionCapability };