@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.
Files changed (42) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +198 -0
  3. package/dist/src/cli.d.ts +2 -0
  4. package/dist/src/cli.js +28 -0
  5. package/dist/src/deepseek-budget.d.ts +51 -0
  6. package/dist/src/deepseek-budget.js +152 -0
  7. package/dist/src/deepseek-cache.d.ts +56 -0
  8. package/dist/src/deepseek-cache.js +151 -0
  9. package/dist/src/deepseek-capabilities.d.ts +4 -0
  10. package/dist/src/deepseek-capabilities.js +1520 -0
  11. package/dist/src/deepseek-conformance.d.ts +14 -0
  12. package/dist/src/deepseek-conformance.js +473 -0
  13. package/dist/src/deepseek-connector.d.ts +168 -0
  14. package/dist/src/deepseek-connector.js +386 -0
  15. package/dist/src/deepseek-models.d.ts +30 -0
  16. package/dist/src/deepseek-models.js +38 -0
  17. package/dist/src/deepseek-scenario.d.ts +55 -0
  18. package/dist/src/deepseek-scenario.js +170 -0
  19. package/dist/src/deepseek-server.d.ts +16 -0
  20. package/dist/src/deepseek-server.js +191 -0
  21. package/dist/src/deepseek-stub.d.ts +75 -0
  22. package/dist/src/deepseek-stub.js +191 -0
  23. package/dist/src/deepseek-twin.d.ts +77 -0
  24. package/dist/src/deepseek-twin.js +1103 -0
  25. package/dist/src/deepseek-types.d.ts +172 -0
  26. package/dist/src/deepseek-types.js +26 -0
  27. package/dist/src/index.d.ts +15 -0
  28. package/dist/src/index.js +93 -0
  29. package/package.json +68 -0
  30. package/src/cli.ts +27 -0
  31. package/src/deepseek-budget.ts +178 -0
  32. package/src/deepseek-cache.ts +159 -0
  33. package/src/deepseek-capabilities.ts +1443 -0
  34. package/src/deepseek-conformance.ts +512 -0
  35. package/src/deepseek-connector.ts +440 -0
  36. package/src/deepseek-models.ts +65 -0
  37. package/src/deepseek-scenario.ts +188 -0
  38. package/src/deepseek-server.ts +201 -0
  39. package/src/deepseek-stub.ts +200 -0
  40. package/src/deepseek-twin.ts +1163 -0
  41. package/src/deepseek-types.ts +201 -0
  42. 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
+ };