@volter/twin-deepseek 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 +202 -0
- package/README.md +198 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +28 -0
- package/dist/src/deepseek-budget.d.ts +51 -0
- package/dist/src/deepseek-budget.js +152 -0
- package/dist/src/deepseek-cache.d.ts +56 -0
- package/dist/src/deepseek-cache.js +151 -0
- package/dist/src/deepseek-capabilities.d.ts +4 -0
- package/dist/src/deepseek-capabilities.js +1520 -0
- package/dist/src/deepseek-conformance.d.ts +14 -0
- package/dist/src/deepseek-conformance.js +473 -0
- package/dist/src/deepseek-connector.d.ts +168 -0
- package/dist/src/deepseek-connector.js +386 -0
- package/dist/src/deepseek-models.d.ts +30 -0
- package/dist/src/deepseek-models.js +38 -0
- package/dist/src/deepseek-scenario.d.ts +55 -0
- package/dist/src/deepseek-scenario.js +170 -0
- package/dist/src/deepseek-server.d.ts +16 -0
- package/dist/src/deepseek-server.js +191 -0
- package/dist/src/deepseek-stub.d.ts +75 -0
- package/dist/src/deepseek-stub.js +191 -0
- package/dist/src/deepseek-twin.d.ts +77 -0
- package/dist/src/deepseek-twin.js +1103 -0
- package/dist/src/deepseek-types.d.ts +172 -0
- package/dist/src/deepseek-types.js +26 -0
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.js +93 -0
- package/package.json +68 -0
- package/src/cli.ts +27 -0
- package/src/deepseek-budget.ts +178 -0
- package/src/deepseek-cache.ts +159 -0
- package/src/deepseek-capabilities.ts +1443 -0
- package/src/deepseek-conformance.ts +512 -0
- package/src/deepseek-connector.ts +440 -0
- package/src/deepseek-models.ts +65 -0
- package/src/deepseek-scenario.ts +188 -0
- package/src/deepseek-server.ts +201 -0
- package/src/deepseek-stub.ts +200 -0
- package/src/deepseek-twin.ts +1163 -0
- package/src/deepseek-types.ts +201 -0
- package/src/index.ts +133 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Shared wire-shape types for the DeepSeek Platform API. These mirror the REAL vendor JSON shapes
|
|
2
|
+
// as published by DeepSeek's own API reference (api-docs.deepseek.com, read 2026-08-31) and by the
|
|
3
|
+
// zod schemas `@ai-sdk/deepseek@3.0.37` decodes responses with (src/chat/deepseek-chat-api-types.ts,
|
|
4
|
+
// src/files/deepseek-files-api.ts). The twin never imports the SDK at runtime; the SDK is exercised
|
|
5
|
+
// only in *.test.ts.
|
|
6
|
+
//
|
|
7
|
+
// DEEPSEEK IS OPENAI-COMPATIBLE, NOT OPENAI, and the differences below are load-bearing — they are
|
|
8
|
+
// exactly what a "copy the openai pack" twin gets wrong (ADDING_A_TWIN.md §0, the compatible-vendor
|
|
9
|
+
// warning). The REJECTIONS are the fidelity surface:
|
|
10
|
+
// • there is NO `/v1` path prefix — `base_url` is `https://api.deepseek.com` and the endpoint is
|
|
11
|
+
// `/chat/completions` (api-docs.deepseek.com "Your First API Call", read 2026-08-31);
|
|
12
|
+
// • the documented error table has NO 404 and DOES have 402 (Insufficient Balance) and
|
|
13
|
+
// 422 (Invalid Parameters) — OpenAI 400s where DeepSeek 422s
|
|
14
|
+
// (api-docs.deepseek.com/quick_start/error_codes);
|
|
15
|
+
// • `usage` carries the KV-cache split `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens`
|
|
16
|
+
// (OpenAI reports `prompt_tokens_details.cached_tokens` instead);
|
|
17
|
+
// • the assistant message/delta carries `reasoning_content` (OpenAI has no such field);
|
|
18
|
+
// • `finish_reason` includes DeepSeek's own `insufficient_system_resource`;
|
|
19
|
+
// • the end-user identifier is `user_id` (regex + 512-char cap), NOT OpenAI's `user`;
|
|
20
|
+
// • `response_format` accepts ONLY `text` and `json_object` — `json_schema` is OpenAI-only;
|
|
21
|
+
// • `n`, `seed`, `logit_bias` and `top_k` are not DeepSeek parameters at all;
|
|
22
|
+
// • `frequency_penalty` / `presence_penalty` are DEPRECATED but accepted with NO effect (they
|
|
23
|
+
// must NOT be errors), as are `temperature` / `top_p` while thinking is enabled;
|
|
24
|
+
// • assistant-prefix completion and strict tool calls live only under the `/beta` base URL;
|
|
25
|
+
// • a `GET /models` row is `{id, object, owned_by}` — no `created`, no `context_window`.
|
|
26
|
+
|
|
27
|
+
// ── Chat Completions ────────────────────────────────────────────────────────────────────
|
|
28
|
+
/** A chat message param as the caller sends it (content is a string OR a content-part array). */
|
|
29
|
+
export type DeepSeekMessageParam = {
|
|
30
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
31
|
+
content?: string | Array<Record<string, unknown>> | null;
|
|
32
|
+
/** Optional participant name — supported on system, user and assistant messages only.
|
|
33
|
+
* `@ai-sdk/deepseek` warns `unsupported: message name on tool messages`. */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** Beta-only: continue this assistant message's content instead of starting a new turn.
|
|
36
|
+
* Requires the `/beta` base URL and must be the FINAL message. */
|
|
37
|
+
prefix?: boolean;
|
|
38
|
+
/** The chain-of-thought DeepSeek returned on a previous assistant turn. When the request carries
|
|
39
|
+
* `tools`, DeepSeek requires previous turns to carry it back or answers 400
|
|
40
|
+
* (api-docs.deepseek.com/guides/thinking_mode). */
|
|
41
|
+
reasoning_content?: string | null;
|
|
42
|
+
tool_calls?: DeepSeekToolCall[];
|
|
43
|
+
tool_call_id?: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** A function tool_call inside an assistant message (faithful shape). */
|
|
47
|
+
export type DeepSeekToolCall = {
|
|
48
|
+
id: string;
|
|
49
|
+
type: 'function';
|
|
50
|
+
function: { name: string; arguments: string };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The chat.completion `usage` object. `prompt_cache_hit_tokens` + `prompt_cache_miss_tokens` are
|
|
55
|
+
* DeepSeek's KV-cache accounting and always sum to `prompt_tokens`
|
|
56
|
+
* (api-docs.deepseek.com/guides/kv_cache: "the number of tokens in the input of this request that
|
|
57
|
+
* resulted in a cache hit" / "…that did not result in a cache hit"). `@ai-sdk/deepseek` reads both
|
|
58
|
+
* into `providerMetadata.deepseek.promptCacheHitTokens` / `…MissTokens` and derives
|
|
59
|
+
* `usage.inputTokens.cacheRead` from the hit count (src/chat/convert-to-deepseek-usage.ts).
|
|
60
|
+
*/
|
|
61
|
+
export type DeepSeekUsage = {
|
|
62
|
+
prompt_tokens: number;
|
|
63
|
+
completion_tokens: number;
|
|
64
|
+
total_tokens: number;
|
|
65
|
+
prompt_cache_hit_tokens: number;
|
|
66
|
+
prompt_cache_miss_tokens: number;
|
|
67
|
+
prompt_tokens_details?: { cached_tokens: number };
|
|
68
|
+
completion_tokens_details?: { reasoning_tokens: number };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The assistant message DeepSeek returns. NOTE `reasoning_content` (no OpenAI counterpart) and the
|
|
73
|
+
* absence of OpenAI's `refusal` / `annotations`.
|
|
74
|
+
*/
|
|
75
|
+
export type DeepSeekAssistantMessage = {
|
|
76
|
+
role: 'assistant';
|
|
77
|
+
content: string | null;
|
|
78
|
+
reasoning_content?: string | null;
|
|
79
|
+
tool_calls?: DeepSeekToolCall[];
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** One entry of a `logprobs.content` / `logprobs.reasoning_content` array. */
|
|
83
|
+
export type DeepSeekLogprob = {
|
|
84
|
+
token: string;
|
|
85
|
+
logprob: number;
|
|
86
|
+
bytes: number[] | null;
|
|
87
|
+
top_logprobs: Array<{ token: string; logprob: number; bytes: number[] | null }>;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/** DeepSeek splits logprobs by channel: generated content AND reasoning content. */
|
|
91
|
+
export type DeepSeekLogprobs = {
|
|
92
|
+
content?: DeepSeekLogprob[] | null;
|
|
93
|
+
reasoning_content?: DeepSeekLogprob[] | null;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** `insufficient_system_resource` is DeepSeek's own value — `@ai-sdk/deepseek` maps it to the
|
|
97
|
+
* unified `error` finish reason (src/chat/map-deepseek-finish-reason.ts). */
|
|
98
|
+
export type DeepSeekFinishReason = 'stop' | 'length' | 'content_filter' | 'tool_calls' | 'insufficient_system_resource';
|
|
99
|
+
|
|
100
|
+
export type DeepSeekChoice = {
|
|
101
|
+
index: number;
|
|
102
|
+
message: DeepSeekAssistantMessage;
|
|
103
|
+
logprobs: DeepSeekLogprobs | null;
|
|
104
|
+
finish_reason: DeepSeekFinishReason;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** The unary chat.completion response envelope (faithful shape). */
|
|
108
|
+
export type DeepSeekChatCompletion = {
|
|
109
|
+
id: string;
|
|
110
|
+
object: 'chat.completion';
|
|
111
|
+
created: number;
|
|
112
|
+
model: string;
|
|
113
|
+
choices: DeepSeekChoice[];
|
|
114
|
+
usage: DeepSeekUsage;
|
|
115
|
+
system_fingerprint: string;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ── FIM completions (beta) ──────────────────────────────────────────────────────────────
|
|
119
|
+
/** `POST https://api.deepseek.com/beta/completions` — fill-in-the-middle. `object` is
|
|
120
|
+
* `text_completion` and the choice carries `text`, not a message. */
|
|
121
|
+
export type DeepSeekCompletionChoice = {
|
|
122
|
+
index: number;
|
|
123
|
+
text: string;
|
|
124
|
+
finish_reason: 'stop' | 'length' | 'content_filter' | 'insufficient_system_resource';
|
|
125
|
+
logprobs: { tokens: string[]; token_logprobs: number[]; text_offset: number[]; top_logprobs: Array<Record<string, number>> } | null;
|
|
126
|
+
};
|
|
127
|
+
export type DeepSeekCompletion = {
|
|
128
|
+
id: string;
|
|
129
|
+
object: 'text_completion';
|
|
130
|
+
created: number;
|
|
131
|
+
model: string;
|
|
132
|
+
choices: DeepSeekCompletionChoice[];
|
|
133
|
+
usage: DeepSeekUsage;
|
|
134
|
+
system_fingerprint: string;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// ── Models ──────────────────────────────────────────────────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* A DeepSeek model row. THREE keys, and that is the whole object: DeepSeek's own
|
|
140
|
+
* `GET /models` example response is `{"id": "...", "object": "model", "owned_by": "deepseek"}`
|
|
141
|
+
* (api-docs.deepseek.com/api/list-models, read 2026-08-31). OpenAI's row additionally carries
|
|
142
|
+
* `created`; DeepSeek's does not, and inventing one would be surface the vendor does not have.
|
|
143
|
+
*/
|
|
144
|
+
export type DeepSeekModel = {
|
|
145
|
+
id: string;
|
|
146
|
+
object: 'model';
|
|
147
|
+
owned_by: string;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// ── User balance ────────────────────────────────────────────────────────────────────────
|
|
151
|
+
/** `GET /user/balance` (api-docs.deepseek.com/api/get-user-balance). Balances are STRINGS. */
|
|
152
|
+
export type DeepSeekBalanceInfo = {
|
|
153
|
+
currency: 'CNY' | 'USD';
|
|
154
|
+
total_balance: string;
|
|
155
|
+
granted_balance: string;
|
|
156
|
+
topped_up_balance: string;
|
|
157
|
+
};
|
|
158
|
+
export type DeepSeekBalance = {
|
|
159
|
+
is_available: boolean;
|
|
160
|
+
balance_infos: DeepSeekBalanceInfo[];
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// ── Files ───────────────────────────────────────────────────────────────────────────────
|
|
164
|
+
/** The file object (api-docs.deepseek.com/guides/files_api; validated key-for-key by
|
|
165
|
+
* `@ai-sdk/deepseek`'s `deepSeekFilesResponseSchema`). `expires_at` appears only when an
|
|
166
|
+
* expiration was requested. */
|
|
167
|
+
export type DeepSeekFile = {
|
|
168
|
+
id: string;
|
|
169
|
+
object: 'file';
|
|
170
|
+
bytes: number;
|
|
171
|
+
created_at: number;
|
|
172
|
+
filename: string;
|
|
173
|
+
purpose: 'user_data';
|
|
174
|
+
expires_at?: number;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// ── Errors ──────────────────────────────────────────────────────────────────────────────
|
|
178
|
+
/**
|
|
179
|
+
* DeepSeek's error envelope. `message` is the only REQUIRED key; `type`, `param` and `code` are
|
|
180
|
+
* nullish. This is not folklore from a rendered docs example — it is the zod schema
|
|
181
|
+
* `@ai-sdk/deepseek@3.0.37` actually decodes every failed response with
|
|
182
|
+
* (`deepSeekErrorSchema`, src/chat/deepseek-chat-api-types.ts), which is the stronger first-party
|
|
183
|
+
* oracle (ADDING_A_TWIN.md §6, "When first-party sources conflict").
|
|
184
|
+
*/
|
|
185
|
+
export type DeepSeekError = {
|
|
186
|
+
error: {
|
|
187
|
+
message: string;
|
|
188
|
+
type?: string | null;
|
|
189
|
+
param?: unknown;
|
|
190
|
+
code?: string | number | null;
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// ── Streaming ───────────────────────────────────────────────────────────────────────────
|
|
195
|
+
/** A single Server-Sent Event the streaming path emits (collected, never socketed in tests).
|
|
196
|
+
* `data` is the JSON payload; `[DONE]` is signalled with `done: true` (no data object). */
|
|
197
|
+
export type SseEvent = { data?: Record<string, unknown>; done?: boolean };
|
|
198
|
+
|
|
199
|
+
/** A sink the streaming path writes events into (an injected collector in tests / a real
|
|
200
|
+
* HTTP SSE writer in the server). NO real sockets or setTimeout in the handler. */
|
|
201
|
+
export type SseSink = (event: SseEvent) => void;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// @volter/twin-deepseek — the DeepSeek twin (one vendor, one package), built on the shared
|
|
2
|
+
// @volter/world-core kernel. The DeepSeek Platform API: a vendor-faithful protocol envelope (chat
|
|
3
|
+
// completions/streaming/tool_calls, `reasoning_content`, DeepSeek's KV-cache-bearing `usage`), the
|
|
4
|
+
// published model catalog, the account balance, beta FIM completion, and a STATEFUL image Files API
|
|
5
|
+
// — over an event/action log. (DeepSeek is an API-first vendor: platform.deepseek.com is a
|
|
6
|
+
// keys/billing/usage console, not where the work happens — docs/contributing/architecture.md C1b — so this pack ships
|
|
7
|
+
// no mirror.)
|
|
8
|
+
//
|
|
9
|
+
// THE DETERMINISTIC STUB IS THE ANSWER: the twin runs no model, so POST /chat/completions returns a
|
|
10
|
+
// DETERMINISTIC STUB completion (clearly labeled), its `reasoning_content` is a labeled stub, and
|
|
11
|
+
// POST /beta/completions returns a labeled FIM stub — never pretending to be real inference.
|
|
12
|
+
// Everything around them — the wire protocol, and above all WHAT DEEPSEEK REFUSES — is faithful.
|
|
13
|
+
// (Conformance + capability tooling live in @volter/world-tooling, a dev dependency — NOT
|
|
14
|
+
// re-exported here, per E2.)
|
|
15
|
+
export { handleDeepSeekTwinRequest, streamChat, buildChatCompletion, DEEPSEEK_BETA_PREFIX, DEEPSEEK_ANTHROPIC_PREFIX } from './deepseek-twin.ts';
|
|
16
|
+
export type { DeepSeekRequest, DeepSeekResponseEnvelope } from './deepseek-twin.ts';
|
|
17
|
+
export { createDeepSeekTwinFetch, createDeepSeekTwinServer, type DeepSeekTwinFetchOptions } from './deepseek-server.ts';
|
|
18
|
+
export { DEEPSEEK_MODELS, FIM_MODELS, RETIRED_MODEL_IDS, VISION_MODELS, findModel, isThinkingModel } from './deepseek-models.ts';
|
|
19
|
+
export { cacheHitTokens, canonicalMessage, prefixId, prefixKey, recordCachePrefixes } from './deepseek-cache.ts';
|
|
20
|
+
export {
|
|
21
|
+
buildUsage, contentToText, countPromptTokens, estimateTokens, fnv1a, lastUserText, messageTokens,
|
|
22
|
+
stubAssistantText, stubFimText, stubFingerprint, stubJsonObject, stubReasoningText,
|
|
23
|
+
stubToolArguments, stubToolCall,
|
|
24
|
+
} from './deepseek-stub.ts';
|
|
25
|
+
export type {
|
|
26
|
+
DeepSeekAssistantMessage, DeepSeekBalance, DeepSeekBalanceInfo, DeepSeekChatCompletion,
|
|
27
|
+
DeepSeekChoice, DeepSeekCompletion, DeepSeekCompletionChoice, DeepSeekError, DeepSeekFile,
|
|
28
|
+
DeepSeekFinishReason, DeepSeekLogprob, DeepSeekLogprobs, DeepSeekMessageParam, DeepSeekModel,
|
|
29
|
+
DeepSeekToolCall, DeepSeekUsage, SseEvent, SseSink,
|
|
30
|
+
} from './deepseek-types.ts';
|
|
31
|
+
export { createDeepSeekScenarioEngine, deepseekScenarioAdapter, loadDeepSeekScenarioDocument, realizeDeepSeekRespond } from './deepseek-scenario.ts';
|
|
32
|
+
export type { DeepSeekScenarioEngine, DeepSeekScenarioRequest, DeepSeekScenarioRespond, ScenarioToolCall, ScriptedResult } from './deepseek-scenario.ts';
|
|
33
|
+
export {
|
|
34
|
+
fullSyncDeepSeek,
|
|
35
|
+
externalIdFor,
|
|
36
|
+
deepseekRequestForAction,
|
|
37
|
+
liveDeepSeekExecute,
|
|
38
|
+
mapBalance,
|
|
39
|
+
mapFile,
|
|
40
|
+
mapModel,
|
|
41
|
+
pullDeepSeekState,
|
|
42
|
+
pushDeepSeekAction,
|
|
43
|
+
pushPendingDeepSeekActions,
|
|
44
|
+
syncDeepSeekFromReal,
|
|
45
|
+
unpushableReason,
|
|
46
|
+
} from './deepseek-connector.ts';
|
|
47
|
+
export type { DeepSeekExecute, LiveDeepSeekOptions } from './deepseek-connector.ts';
|
|
48
|
+
// The client-side rate budget — the fail-closed backstop `liveDeepSeekExecute` routes every live
|
|
49
|
+
// request through. The MECHANISM is the kernel's shared, vendor-agnostic `RateBudget`; what lives
|
|
50
|
+
// here is DeepSeek's DECLARATION (window/ceiling/per-endpoint weights) plus the vendor-bound
|
|
51
|
+
// bindings. Exported so an operator can inspect spend (`snapshot`) and so a caller can catch
|
|
52
|
+
// `DeepSeekBudgetError` by type; there is deliberately no export that disables the guard.
|
|
53
|
+
export {
|
|
54
|
+
DEEPSEEK_BUDGET_CEILING,
|
|
55
|
+
DEEPSEEK_BUDGET_MAX_RETRY_AFTER_S,
|
|
56
|
+
DEEPSEEK_BUDGET_WINDOW_MS,
|
|
57
|
+
DEEPSEEK_CALL_WEIGHTS,
|
|
58
|
+
DEEPSEEK_RATE_BUDGET,
|
|
59
|
+
DeepSeekBudget,
|
|
60
|
+
DeepSeekBudgetError,
|
|
61
|
+
deepseekBudgetPath,
|
|
62
|
+
deepseekCallWeight,
|
|
63
|
+
} from './deepseek-budget.ts';
|
|
64
|
+
export type { DeepSeekBudgetErrorKind, DeepSeekBudgetOptions, DeepSeekBudgetReservation, DeepSeekBudgetSnapshot } from './deepseek-budget.ts';
|
|
65
|
+
|
|
66
|
+
// Registry descriptor: the pack self-describes so tooling can discover it.
|
|
67
|
+
import type { TwinPack } from '@volter/world-core';
|
|
68
|
+
import { DEEPSEEK_RATE_BUDGET as RATE_BUDGET } from './deepseek-budget.ts';
|
|
69
|
+
export const pack: TwinPack = {
|
|
70
|
+
vendor: 'deepseek',
|
|
71
|
+
// The SAME object deepseek-budget.ts declares at module load — one source of truth, so registering
|
|
72
|
+
// the pack and importing the connector can never arm two different ceilings.
|
|
73
|
+
rateBudget: RATE_BUDGET,
|
|
74
|
+
transport: 'rest',
|
|
75
|
+
archetype: 'generative',
|
|
76
|
+
bin: 'world-deepseek',
|
|
77
|
+
// R2 adopted-as-debt, legibly: declared types no replay can create, each with its reason.
|
|
78
|
+
resourcesUnreachable: {
|
|
79
|
+
'model': 'the vendor catalog',
|
|
80
|
+
'balance': 'the account ledger the vendor keeps; read-only',
|
|
81
|
+
},
|
|
82
|
+
resources: ['model', 'file', 'balance', 'cache_prefix'],
|
|
83
|
+
specSource: "DeepSeek Platform API — enumerated top-down from api-docs.deepseek.com (quick_start/{error_codes,rate_limit,pricing}, api/{create-chat-completion,create-completion,list-models,get-user-balance}, guides/{kv_cache,thinking_mode,files_api,anthropic_api,chat_prefix_completion}; read 2026-08-31) and cross-checked key-for-key against the zod schemas @ai-sdk/deepseek@3.0.37 encodes/decodes with (src/chat/deepseek-chat-api-types.ts, src/chat/deepseek-chat-language-model{,-options}.ts, src/chat/deepseek-prepare-tools.ts, src/files/deepseek-files{,-api,-options}.ts) plus the provider doc page shipped in that tarball (docs/30-deepseek.mdx). DeepSeek publishes no OpenAPI document. Envelope-faithful; model output, reasoning_content, FIM text and logprob values are labeled deterministic stubs.",
|
|
84
|
+
description: "DeepSeek Platform API twin — faithful protocol envelope (chat completions/streaming/tool_calls, reasoning_content, KV-cache-split usage), the published catalog, user balance, beta FIM + prefix completion + strict tools, and a stateful image Files API; generative output is a labeled stub and the OpenAI-divergent REFUSALS (402/422, no json_schema, no n/seed/logit_bias/user) are modeled.",
|
|
85
|
+
// DeepSeek's base_url is https://api.deepseek.com with NO version segment — the endpoint is
|
|
86
|
+
// `/chat/completions` (api-docs.deepseek.com "Your First API Call", read 2026-08-31). This is the
|
|
87
|
+
// single most likely thing to be copied wrong from an OpenAI-shaped exemplar.
|
|
88
|
+
browserRouting: { apiPathPrefix: '/', loaderHost: 'https://api.deepseek.com' },
|
|
89
|
+
// ADOPTION (adding-a-twin.md §3): how an app repo betrays that it talks to DeepSeek. Declared
|
|
90
|
+
// HERE, not in world-runtime's central SDK_TWINS/ENV_STEM_VENDORS maps — declaring in both throws.
|
|
91
|
+
//
|
|
92
|
+
// `@ai-sdk/deepseek` is the Vercel AI SDK provider and the ONLY first-party npm client of this
|
|
93
|
+
// surface — DeepSeek publishes no SDK of its own; its docs tell integrators to point the OpenAI
|
|
94
|
+
// or Anthropic SDK at api.deepseek.com instead ("The DeepSeek API uses an API format compatible
|
|
95
|
+
// with OpenAI/Anthropic"). That is exactly why `openai` is NOT claimed here: a repo depending on
|
|
96
|
+
// `openai` is an OpenAI repo unless its base URL says otherwise, and claiming the package would
|
|
97
|
+
// mis-attribute every OpenAI integration in the census to this pack. The 2026-08-31-b census
|
|
98
|
+
// bears this out — 5 of the 6 demanding repos signal `@ai-sdk/deepseek`; the sixth (nextchat)
|
|
99
|
+
// signals the bare `deepseek` stem via DEEPSEEK_API_KEY / DEEPSEEK_URL.
|
|
100
|
+
//
|
|
101
|
+
// `envStems: ['DEEPSEEK']` normalizes (covers.ts `normalizeId`: lowercase + strip
|
|
102
|
+
// non-alphanumerics) to `deepseek`, which is what DEEPSEEK_API_KEY — the one env
|
|
103
|
+
// `@ai-sdk/deepseek` reads (`loadApiKey({ environmentVariableName: 'DEEPSEEK_API_KEY' })`,
|
|
104
|
+
// src/deepseek-provider.ts) — and NextChat's DEEPSEEK_URL both reduce to.
|
|
105
|
+
//
|
|
106
|
+
// No `scopes`: `@ai-sdk/` is Vercel's scope shared by every provider package, not DeepSeek's.
|
|
107
|
+
adoption: {
|
|
108
|
+
// No official Python SDK: DeepSeek's own docs tell Python callers to use the `openai` client
|
|
109
|
+
// against api.deepseek.com, and that distribution belongs to the openai pack (the pypi list is
|
|
110
|
+
// the distribution; DEEPSEEK_BASE_URL is what routes it). The `deepseek` name on PyPI is an
|
|
111
|
+
// unaffiliated third-party wrapper with no adoption, deliberately not claimed.
|
|
112
|
+
pypi: [],
|
|
113
|
+
sdks: ['@ai-sdk/deepseek'], envStems: ['DEEPSEEK'],
|
|
114
|
+
},
|
|
115
|
+
// INTERCEPTION: the one host every client addresses. `@ai-sdk/deepseek` defaults its baseURL to
|
|
116
|
+
// `https://api.deepseek.com` and builds every URL as `${baseURL}${path}` for chat and
|
|
117
|
+
// `${baseURL}/files` for uploads (src/deepseek-provider.ts, src/files/deepseek-files.ts), so the
|
|
118
|
+
// standard surface, the `/beta` surface and the `/anthropic` surface all live on this one host.
|
|
119
|
+
// No second host exists anywhere in the SDK.
|
|
120
|
+
hosts: [{ host: 'api.deepseek.com' }],
|
|
121
|
+
// The injector intercepts api.deepseek.com (see `hosts`), so a world wires this pack through
|
|
122
|
+
// DEEPSEEK_TWIN_URL rather than an app-read base-URL var.
|
|
123
|
+
//
|
|
124
|
+
// THE GROUNDING FOR THE "NONE" HALF: `@ai-sdk/deepseek` reads exactly ONE environment variable —
|
|
125
|
+
// `DEEPSEEK_API_KEY`, via `loadApiKey` — and takes `baseURL` as a constructor option only
|
|
126
|
+
// (`DeepSeekProviderSettings.baseURL`, src/deepseek-provider.ts). There is no DEEPSEEK_BASE_URL
|
|
127
|
+
// in the SDK, in DeepSeek's docs, or anywhere in the tarball. NextChat's `DEEPSEEK_URL` is that
|
|
128
|
+
// one application's own convention, not a vendor-documented var, and injecting it would make
|
|
129
|
+
// `covers` report every DeepSeek world covered while the five repos on `@ai-sdk/deepseek` — which
|
|
130
|
+
// read no such var — still talked to the real vendor. That is precisely the class of lie
|
|
131
|
+
// ADDING_A_TWIN.md §7 point 12 forbids ("never invent a <VENDOR>_BASE_URL the app does not read").
|
|
132
|
+
endpointEnvNone: "no app-read endpoint env: @ai-sdk/deepseek — the only first-party npm client of this surface — reads exactly one environment variable (DEEPSEEK_API_KEY, via loadApiKey in src/deepseek-provider.ts) and takes baseURL as a constructor option, so there is no vendor-documented base-URL var to inject. The injector covers api.deepseek.com instead (see `hosts`). NextChat's DEEPSEEK_URL is one application's own convention, not vendor surface, and claiming it would report a world covered while the SDK-based majority still reached the real vendor.",
|
|
133
|
+
};
|