melchizedek-agents 1.10.2 → 1.11.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/.env.example +10 -0
- package/AGENT_SETUP.md +2 -1
- package/CHANGELOG.md +44 -0
- package/DOCUMENTATION.md +43 -7
- package/ONBOARDING.md +7 -4
- package/QUICKSTART.md +3 -3
- package/README.md +5 -5
- package/config/agents/examples/{augustin.yaml → current_events.yaml} +22 -50
- package/config/agents/examples/model_zoo.yaml +21 -9
- package/config/agents/syndicate.schema.json +58 -0
- package/config/agents/syndicateSchema.yaml +9 -5
- package/db/migrations/0019_deepseek_kimi_pricing.sql +48 -0
- package/dist/lib/config.d.ts +11 -0
- package/dist/lib/config.js +11 -0
- package/dist/lib/doctor.js +2 -0
- package/dist/lib/loadSyndicate.d.ts +1 -1
- package/dist/lib/model.d.ts +2 -0
- package/dist/lib/model.js +1 -0
- package/dist/lib/models/adapterResolver.js +3 -1
- package/dist/lib/models/capabilities.js +11 -0
- package/dist/lib/models/chatCompletionsAdapter.d.ts +24 -1
- package/dist/lib/models/chatCompletionsAdapter.js +42 -5
- package/dist/lib/models/contract.d.ts +1 -1
- package/dist/lib/models/deepseekAdapter.d.ts +127 -0
- package/dist/lib/models/deepseekAdapter.js +157 -0
- package/dist/lib/models/endpoints.d.ts +2 -1
- package/dist/lib/models/endpoints.js +5 -1
- package/dist/lib/models/gateway.d.ts +3 -2
- package/dist/lib/models/gateway.js +5 -4
- package/dist/lib/models/gatewayAdapter.d.ts +1 -1
- package/dist/lib/models/gatewayAdapter.js +1 -1
- package/dist/lib/models/providerMap.d.ts +3 -2
- package/dist/lib/models/providerMap.js +5 -1
- package/dist/lib/models/reasoning.d.ts +1 -1
- package/dist/lib/models/reasoning.js +4 -2
- package/dist/lib/models/registry.js +4 -1
- package/dist/lib/observability/tracer.d.ts +1 -1
- package/dist/lib/onboarding.js +3 -1
- package/dist/lib/syndicateSchema.d.ts +16 -0
- package/dist/lib/syndicateSchema.js +1 -0
- package/dist/lib/toolRegistry.js +1 -1
- package/dist/lib/tools/credentialEnv.js +1 -1
- package/package.json +2 -2
- package/skills/briefs/melchizedek-author.md +1 -1
- package/skills/briefs/melchizedek-models.md +7 -6
- package/skills/briefs/melchizedek.md +5 -5
- package/skills/melchizedek/SKILL.md +2 -2
- package/skills/melchizedek-onboard/SKILL.md +1 -1
- package/skills/melchizedek-onboard-keys/SKILL.md +2 -2
|
@@ -269,6 +269,7 @@ orchestrator:
|
|
|
269
269
|
# gpt-* /o* → OpenAI (OPENAI_API_KEY)
|
|
270
270
|
# grok-* → xAI (XAI_API_KEY)
|
|
271
271
|
# kimi-* → Moonshot Kimi (MOONSHOT_API_KEY)
|
|
272
|
+
# deepseek-* → DeepSeek (DEEPSEEK_API_KEY)
|
|
272
273
|
# ollama/* → local Ollama (keyless)
|
|
273
274
|
# Subagents inherit this if not set on their own config.
|
|
274
275
|
model: "gemini-3.5-flash-lite"
|
|
@@ -443,14 +444,16 @@ orchestrator:
|
|
|
443
444
|
# that effort; none is the model's off switch,
|
|
444
445
|
# or low effort on Opus 5 and 5.5, Fable and
|
|
445
446
|
# Mythos (ADR 0049)
|
|
446
|
-
# gpt-*/o*, grok-*, kimi-*, ollama/*, a gateway
|
|
447
|
+
# gpt-*/o*, grok-*, kimi-*, deepseek-*, ollama/*, a gateway
|
|
447
448
|
# → reasoningEffort (an effort word; where the
|
|
448
|
-
# provider lacks one, its nearest setting above
|
|
449
|
+
# provider lacks one, its nearest setting above;
|
|
450
|
+
# deepseek-* beside thinking enabled, and none
|
|
451
|
+
# as thinking disabled)
|
|
449
452
|
# A budget is sent as-is to Gemini and to Claude 4.6 and earlier, and as
|
|
450
453
|
# the smallest level that covers it everywhere else. Unset, each provider
|
|
451
454
|
# keeps its default.
|
|
452
455
|
# xhigh and max (ADR 0117) reach the models that take them: both on later
|
|
453
|
-
# claude-*, max on kimi-k3 (xhigh rounds up to it), xhigh on gpt-5.2 and
|
|
456
|
+
# claude-*, max on kimi-k3 and deepseek-* (xhigh rounds up to it), xhigh on gpt-5.2 and
|
|
454
457
|
# later and grok-4.7. Elsewhere the model's highest setting is sent and
|
|
455
458
|
# the span carries llm.reasoning.weakened; a gateway gets high.
|
|
456
459
|
# Cannot be combined with generateContentConfig.thinkingConfig or
|
|
@@ -466,7 +469,8 @@ orchestrator:
|
|
|
466
469
|
# Sampling temperature. Lower = more deterministic. Range: [0.0, 2.0]
|
|
467
470
|
# ⚠ Do NOT set this on current Gemini models — they are tuned for their
|
|
468
471
|
# own default sampling and the knob is not part of their contract.
|
|
469
|
-
# Set it only for claude-*, gpt-*, grok-*, kimi-*, or ollama/* agents
|
|
472
|
+
# Set it only for claude-*, gpt-*, grok-*, kimi-*, or ollama/* agents
|
|
473
|
+
# (DeepSeek ignores it while thinking, its default).
|
|
470
474
|
# temperature: 0.7
|
|
471
475
|
|
|
472
476
|
# Nucleus sampling. Tokens sampled until cumulative prob = top_p.
|
|
@@ -504,7 +508,7 @@ orchestrator:
|
|
|
504
508
|
# ── model_overrides ───────────────────────────────────────
|
|
505
509
|
# [optional] A prompt nuance for one provider (ADR 0115). Keys are the
|
|
506
510
|
# providers a model id routes to: gemini, anthropic, openai, xai,
|
|
507
|
-
# moonshot, ollama. Each holds ONE of:
|
|
511
|
+
# moonshot, deepseek, ollama. Each holds ONE of:
|
|
508
512
|
# instruction: replaces the instruction on that provider
|
|
509
513
|
# instruction_append: added after the instruction, after a blank line
|
|
510
514
|
# Applied for the provider of this agent's model, before the skills
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- 0019_deepseek_kimi_pricing — price rows for DeepSeek and Moonshot Kimi
|
|
3
|
+
-- (extends ADR 0136 and ADR 0139)
|
|
4
|
+
-- ============================================================================
|
|
5
|
+
-- Rows only, in 0017's column layout (USD per million tokens):
|
|
6
|
+
-- input_per_mtok an input token that is not a cache hit
|
|
7
|
+
-- output_per_mtok an output token (thinking included: NULL thinking
|
|
8
|
+
-- rate, so thinking prices at the output rate)
|
|
9
|
+
-- cache_read_per_mtok an input token read from the provider's cache
|
|
10
|
+
-- cache_write_per_mtok NULL: neither vendor bills a cache write, so a
|
|
11
|
+
-- written token prices as input
|
|
12
|
+
--
|
|
13
|
+
-- DeepSeek (lib/models/deepseekAdapter.ts): the PEAK rates. DeepSeek bills
|
|
14
|
+
-- off-peak at half, and peak (01:00–04:00 and 06:00–10:00 UTC on weekdays)
|
|
15
|
+
-- at exactly double the off-peak list; a flat row cannot say the hour, so
|
|
16
|
+
-- it holds the higher rate and a budget never underestimates a call.
|
|
17
|
+
-- Off-peak per 1M: deepseek-flash $0.15 in / $0.003 cache hit / $0.60 out;
|
|
18
|
+
-- deepseek-v4-pro $0.66 / $0.022 / $1.98. Checked 2026-10-22 against
|
|
19
|
+
-- api-docs.deepseek.com/quick_start/pricing. The retired deepseek-v4-flash
|
|
20
|
+
-- and deepseek-v4-flash-vision-exp ids are still accepted, served by
|
|
21
|
+
-- V4.1-Flash and billed at Flash's price, so they get Flash's row.
|
|
22
|
+
--
|
|
23
|
+
-- Moonshot Kimi (lib/models/kimiAdapter.ts): no row until now. The rates
|
|
24
|
+
-- are the family table in the adapter's header (platform.moonshot.ai ›
|
|
25
|
+
-- Model list, October 2026); verified_at stays NULL because this migration
|
|
26
|
+
-- did not re-check them against the vendor's list (0015's convention: spend
|
|
27
|
+
-- built on the row is a steering estimate until someone does).
|
|
28
|
+
--
|
|
29
|
+
-- New rows never overwrite a row that exists: an operator's own rates
|
|
30
|
+
-- survive. Nothing secret, nothing personal. Idempotent; safe to re-run.
|
|
31
|
+
-- ============================================================================
|
|
32
|
+
|
|
33
|
+
INSERT INTO model_pricing (model, input_per_mtok, output_per_mtok, cache_read_per_mtok, cache_write_per_mtok, verified_at, source, note) VALUES
|
|
34
|
+
-- DeepSeek, peak rates (double the off-peak list).
|
|
35
|
+
('deepseek-flash', 0.30, 1.20, 0.006, NULL, '2026-10-22', 'api-docs.deepseek.com/quick_start/pricing', 'DeepSeek first-party, checked 2026-10-22; PEAK rate (01:00-04:00 and 06:00-10:00 UTC weekdays), double the off-peak $0.15 / $0.003 / $0.60, held so budgets never underestimate'),
|
|
36
|
+
('deepseek-v4-pro', 1.32, 3.96, 0.044, NULL, '2026-10-22', 'api-docs.deepseek.com/quick_start/pricing', 'DeepSeek first-party, checked 2026-10-22; PEAK rate (01:00-04:00 and 06:00-10:00 UTC weekdays), double the off-peak $0.66 / $0.022 / $1.98, held so budgets never underestimate'),
|
|
37
|
+
('deepseek-v4-flash', 0.30, 1.20, 0.006, NULL, '2026-10-22', 'api-docs.deepseek.com/quick_start/pricing', 'DeepSeek first-party, checked 2026-10-22; retired id served by V4.1-Flash and billed at Flash price; PEAK rate, double the off-peak $0.15 / $0.003 / $0.60'),
|
|
38
|
+
('deepseek-v4-flash-vision-exp', 0.30, 1.20, 0.006, NULL, '2026-10-22', 'api-docs.deepseek.com/quick_start/pricing', 'DeepSeek first-party, checked 2026-10-22; retired id served by V4.1-Flash and billed at Flash price; PEAK rate, double the off-peak $0.15 / $0.003 / $0.60'),
|
|
39
|
+
-- Moonshot Kimi, from the family table in lib/models/kimiAdapter.ts.
|
|
40
|
+
('kimi-k3', 3.00, 15.00, 0.30, NULL, NULL, 'platform.moonshot.ai (Model list, October 2026), via the family table in lib/models/kimiAdapter.ts', 'Moonshot first-party; not re-checked by this migration'),
|
|
41
|
+
('kimi-k2.6', 0.95, 4.00, 0.16, NULL, NULL, 'platform.moonshot.ai (Model list, October 2026), via the family table in lib/models/kimiAdapter.ts', 'Moonshot first-party; not re-checked by this migration'),
|
|
42
|
+
('kimi-k2.7-code', 0.95, 4.00, 0.19, NULL, NULL, 'platform.moonshot.ai (Model list, October 2026), via the family table in lib/models/kimiAdapter.ts', 'Moonshot first-party; not re-checked by this migration'),
|
|
43
|
+
('kimi-k2.7-code-highspeed', 1.90, 8.00, 0.38, NULL, NULL, 'platform.moonshot.ai (Model list, October 2026), via the family table in lib/models/kimiAdapter.ts', 'Moonshot first-party; not re-checked by this migration')
|
|
44
|
+
ON CONFLICT (model) DO NOTHING;
|
|
45
|
+
|
|
46
|
+
INSERT INTO melchizedek_schema_version (version, name)
|
|
47
|
+
VALUES (19, '0019_deepseek_kimi_pricing')
|
|
48
|
+
ON CONFLICT (version) DO NOTHING;
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -77,6 +77,17 @@ export declare const DEFAULT_KIMI_MODEL = "kimi-k3";
|
|
|
77
77
|
* override. Source: platform.moonshot.ai › Guides › Reasoning effort.
|
|
78
78
|
*/
|
|
79
79
|
export declare const DEFAULT_KIMI_REASONING_EFFORT = "high";
|
|
80
|
+
/**
|
|
81
|
+
* Default DeepSeek model used when a deepseek-* model is requested but no
|
|
82
|
+
* specific identifier is provided. Requires DEEPSEEK_API_KEY
|
|
83
|
+
* (lib/models/deepseekAdapter.ts). deepseek-flash (DeepSeek-V4.1-Flash) is
|
|
84
|
+
* the cheap tier with vision, at $0.15 / $0.60 per 1M off-peak;
|
|
85
|
+
* deepseek-v4-pro is the deeper, text-only tier at about four times that
|
|
86
|
+
* (the family table and peak pricing are in the adapter's header).
|
|
87
|
+
* No reasoning effort is pinned: DeepSeek's own default is `high`, the
|
|
88
|
+
* middle of its three levels (low | high | max), not its slowest.
|
|
89
|
+
*/
|
|
90
|
+
export declare const DEFAULT_DEEPSEEK_MODEL = "deepseek-flash";
|
|
80
91
|
/**
|
|
81
92
|
* Model calls one turn may make, subagents included, when the syndicate sets
|
|
82
93
|
* no `max_steps`. A runaway tool loop stops here instead of at the provider's
|
package/dist/lib/config.js
CHANGED
|
@@ -78,6 +78,17 @@ export const DEFAULT_KIMI_MODEL = 'kimi-k3';
|
|
|
78
78
|
* override. Source: platform.moonshot.ai › Guides › Reasoning effort.
|
|
79
79
|
*/
|
|
80
80
|
export const DEFAULT_KIMI_REASONING_EFFORT = 'high';
|
|
81
|
+
/**
|
|
82
|
+
* Default DeepSeek model used when a deepseek-* model is requested but no
|
|
83
|
+
* specific identifier is provided. Requires DEEPSEEK_API_KEY
|
|
84
|
+
* (lib/models/deepseekAdapter.ts). deepseek-flash (DeepSeek-V4.1-Flash) is
|
|
85
|
+
* the cheap tier with vision, at $0.15 / $0.60 per 1M off-peak;
|
|
86
|
+
* deepseek-v4-pro is the deeper, text-only tier at about four times that
|
|
87
|
+
* (the family table and peak pricing are in the adapter's header).
|
|
88
|
+
* No reasoning effort is pinned: DeepSeek's own default is `high`, the
|
|
89
|
+
* middle of its three levels (low | high | max), not its slowest.
|
|
90
|
+
*/
|
|
91
|
+
export const DEFAULT_DEEPSEEK_MODEL = 'deepseek-flash';
|
|
81
92
|
/**
|
|
82
93
|
* Model calls one turn may make, subagents included, when the syndicate sets
|
|
83
94
|
* no `max_steps`. A runaway tool loop stops here instead of at the provider's
|
package/dist/lib/doctor.js
CHANGED
|
@@ -178,6 +178,7 @@ export const CONSOLE_URLS = {
|
|
|
178
178
|
openai: 'https://platform.openai.com/api-keys',
|
|
179
179
|
xai: 'https://console.x.ai',
|
|
180
180
|
moonshot: 'https://platform.moonshot.ai',
|
|
181
|
+
deepseek: 'https://platform.deepseek.com/api_keys',
|
|
181
182
|
ollama: 'https://ollama.com',
|
|
182
183
|
};
|
|
183
184
|
// ── Discovery ────────────────────────────────────────────────────────────────
|
|
@@ -603,6 +604,7 @@ const PROBE_ID = {
|
|
|
603
604
|
openai: 'gpt-x',
|
|
604
605
|
xai: 'grok-x',
|
|
605
606
|
moonshot: 'kimi-x',
|
|
607
|
+
deepseek: 'deepseek-x',
|
|
606
608
|
ollama: 'ollama/x',
|
|
607
609
|
};
|
|
608
610
|
/**
|
|
@@ -149,7 +149,7 @@ export interface AgentYamlConfig {
|
|
|
149
149
|
* appended after a blank line, for the provider of the model the agent runs
|
|
150
150
|
* on. Applied at compile time, before the skills index; not for fallback_model.
|
|
151
151
|
*/
|
|
152
|
-
model_overrides?: Partial<Record<'gemini' | 'anthropic' | 'openai' | 'xai' | 'moonshot' | 'ollama', {
|
|
152
|
+
model_overrides?: Partial<Record<'gemini' | 'anthropic' | 'openai' | 'xai' | 'moonshot' | 'deepseek' | 'ollama', {
|
|
153
153
|
instruction?: string;
|
|
154
154
|
instruction_append?: string;
|
|
155
155
|
}>>;
|
package/dist/lib/model.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export { ChatCompletionsAdapter } from './models/chatCompletionsAdapter.ts';
|
|
|
30
30
|
export type { ChatCompletionsAdapterOptions } from './models/chatCompletionsAdapter.ts';
|
|
31
31
|
export { KimiAdapter } from './models/kimiAdapter.ts';
|
|
32
32
|
export type { KimiAdapterOptions } from './models/kimiAdapter.ts';
|
|
33
|
+
export { DeepSeekAdapter } from './models/deepseekAdapter.ts';
|
|
34
|
+
export type { DeepSeekAdapterOptions } from './models/deepseekAdapter.ts';
|
|
33
35
|
export { OllamaAdapter } from './models/ollamaAdapter.ts';
|
|
34
36
|
export type { OllamaAdapterOptions } from './models/ollamaAdapter.ts';
|
|
35
37
|
export { GatewayAdapter } from './models/gatewayAdapter.ts';
|
package/dist/lib/model.js
CHANGED
|
@@ -23,6 +23,7 @@ export { GptAdapter } from './models/gptAdapter.js';
|
|
|
23
23
|
export { GrokAdapter } from './models/grokAdapter.js';
|
|
24
24
|
export { ChatCompletionsAdapter } from './models/chatCompletionsAdapter.js';
|
|
25
25
|
export { KimiAdapter } from './models/kimiAdapter.js';
|
|
26
|
+
export { DeepSeekAdapter } from './models/deepseekAdapter.js';
|
|
26
27
|
export { OllamaAdapter } from './models/ollamaAdapter.js';
|
|
27
28
|
export { GatewayAdapter } from './models/gatewayAdapter.js';
|
|
28
29
|
export { GeminiAdapter } from './models/geminiAdapter.js';
|
|
@@ -25,6 +25,7 @@ import { GeminiAdapter } from './geminiAdapter.js';
|
|
|
25
25
|
import { GptAdapter } from './gptAdapter.js';
|
|
26
26
|
import { GrokAdapter } from './grokAdapter.js';
|
|
27
27
|
import { KimiAdapter } from './kimiAdapter.js';
|
|
28
|
+
import { DeepSeekAdapter } from './deepseekAdapter.js';
|
|
28
29
|
import { OllamaAdapter } from './ollamaAdapter.js';
|
|
29
30
|
import { providerForModel } from './providerMap.js';
|
|
30
31
|
/**
|
|
@@ -53,7 +54,7 @@ export function scopedKey(model, apiKey, keyProvider) {
|
|
|
53
54
|
/** A provider name as the A2A X-Provider header gives it; anything unknown reads as gemini. */
|
|
54
55
|
export function normalizeProvider(provider) {
|
|
55
56
|
const p = provider?.toLowerCase();
|
|
56
|
-
if (p === 'ollama' || p === 'anthropic' || p === 'openai' || p === 'xai' || p === 'moonshot') {
|
|
57
|
+
if (p === 'ollama' || p === 'anthropic' || p === 'openai' || p === 'xai' || p === 'moonshot' || p === 'deepseek') {
|
|
57
58
|
return p;
|
|
58
59
|
}
|
|
59
60
|
return 'gemini';
|
|
@@ -84,6 +85,7 @@ const CONTRACT_ADAPTER = {
|
|
|
84
85
|
: new GptAdapter({ model: r.model, apiKey: r.apiKey, endpoint: r.endpoint }),
|
|
85
86
|
xai: (r) => new GrokAdapter({ model: r.model, apiKey: r.apiKey }),
|
|
86
87
|
moonshot: (r) => new KimiAdapter({ model: r.model, apiKey: r.apiKey, baseUrl: r.endpoint?.baseURL }),
|
|
88
|
+
deepseek: (r) => new DeepSeekAdapter({ model: r.model, apiKey: r.apiKey, baseUrl: r.endpoint?.baseURL }),
|
|
87
89
|
};
|
|
88
90
|
/** A resolver over the one prefix table. A Gemini id gets GeminiAdapter (ADR 0100, ADR 0107). */
|
|
89
91
|
export function adapterResolver() {
|
|
@@ -193,6 +193,16 @@ export const CAPABILITY_MATRIX = {
|
|
|
193
193
|
vision: ok('test', 'user-turn images only, sent as base64 (Moonshot takes no public image URLs)'),
|
|
194
194
|
native_search: unsupported("Moonshot's model-side $web_search retires 2026-10-20 and its successor is a REST call, not a request field; the web_search sentinel is dropped (use web_extract)"),
|
|
195
195
|
},
|
|
196
|
+
deepseek: {
|
|
197
|
+
delegation: ok(),
|
|
198
|
+
memory_tools: ok(),
|
|
199
|
+
structured_output: degraded('JSON mode (response_format json_object), not a strict schema: the schema is asked for in the system message and the engine validates the reply'),
|
|
200
|
+
structured_output_with_tools: degraded(setModelResponseNote('DeepSeek takes JSON mode only, no strict schema')),
|
|
201
|
+
thinking_with_tools: ok('test', "reasoning_content is sent back on every assistant message of a request with tools, earlier turns included, as DeepSeek requires (else a 400): the same model's stored state, an empty string where none is stored (ADR 0046); effort travels as reasoning_effort beside a thinking switch"),
|
|
202
|
+
streaming: ok(),
|
|
203
|
+
vision: ok('test', 'user-turn images only, sent as base64; deepseek-flash only (deepseek-v4-pro is text-only)'),
|
|
204
|
+
native_search: unsupported("DeepSeek's Search and Fetch are separate per-request REST APIs, not a chat request field; the web_search sentinel is dropped (use web_extract)"),
|
|
205
|
+
},
|
|
196
206
|
ollama: {
|
|
197
207
|
delegation: ok(),
|
|
198
208
|
memory_tools: ok(),
|
|
@@ -239,6 +249,7 @@ export const REASONING_ABOVE_HIGH = {
|
|
|
239
249
|
openai: 'xhigh as asked on GPT-5.2 and later, and max goes as xhigh there, weakened; high, weakened, on the o-series, GPT-5 and GPT-5.1',
|
|
240
250
|
xai: 'xhigh as asked on grok-4.7, and max goes as xhigh there, weakened; high, weakened, on grok-4.5 and grok-4.6',
|
|
241
251
|
moonshot: 'max as asked on kimi-k3, and xhigh rounds up to max; the K2 generation takes a thinking switch, which these leave on',
|
|
252
|
+
deepseek: 'max as asked, and xhigh rounds up to max (DeepSeek has low | high | max), each beside thinking enabled',
|
|
242
253
|
ollama: 'reasoning_effort high, weakened',
|
|
243
254
|
gateway: 'reasoning_effort high, weakened, whatever the id: the words above high are not accepted across gateways',
|
|
244
255
|
};
|
|
@@ -36,7 +36,10 @@
|
|
|
36
36
|
* (replaysReasoningContent: Kimi) writes the response's reasoning_content
|
|
37
37
|
* as providerState on the part it preceded and sends it back on that
|
|
38
38
|
* assistant message within the current turn's tool loop, for the same
|
|
39
|
-
* provider and model only (ADR 0046).
|
|
39
|
+
* provider and model only (ADR 0046). An adapter whose provider asks for
|
|
40
|
+
* it on every past assistant message of a request with tools
|
|
41
|
+
* (replaysReasoningOnEveryTurn: DeepSeek) sends it on all of them, an
|
|
42
|
+
* empty string where none of its own is stored.
|
|
40
43
|
* - A turn that thought, or ran out of tokens, without a reply or a tool
|
|
41
44
|
* call is an error final (`<ID>_MAX_TOKENS`, `<ID>_EMPTY_RESPONSE`, ADR
|
|
42
45
|
* 0027), carrying its usage. An adapter that opts in
|
|
@@ -230,6 +233,24 @@ export declare abstract class ChatCompletionsAdapter implements ModelAdapter {
|
|
|
230
233
|
* provider asks for the field back on a tool loop turns it on (Kimi).
|
|
231
234
|
*/
|
|
232
235
|
protected replaysReasoningContent(_model: string): boolean;
|
|
236
|
+
/**
|
|
237
|
+
* Whether, on a request that carries `tools`, EVERY assistant message in
|
|
238
|
+
* the history (earlier turns' included, not only this turn's tool loop)
|
|
239
|
+
* carries `reasoning_content`: this adapter's own stored state for the
|
|
240
|
+
* same model where there is some, else an empty string, so the field is
|
|
241
|
+
* never missing. Default: never. Only meaningful beside
|
|
242
|
+
* replaysReasoningContent (DeepSeek, whose API returns 400 on a tool
|
|
243
|
+
* request missing a past turn's reasoning_content).
|
|
244
|
+
*/
|
|
245
|
+
protected replaysReasoningOnEveryTurn(_model: string): boolean;
|
|
246
|
+
/**
|
|
247
|
+
* Whether the provider enforces a strict `json_schema` response_format.
|
|
248
|
+
* Default: true. Where it takes JSON mode only (DeepSeek), an outputSchema
|
|
249
|
+
* goes as `response_format: json_object` and the schema rides in the
|
|
250
|
+
* system message instead; the engine validates the reply against it
|
|
251
|
+
* (lib/runtime/native/agentLoop.ts saveOutput).
|
|
252
|
+
*/
|
|
253
|
+
protected takesStrictJsonSchema(_model: string): boolean;
|
|
233
254
|
/** A precondition the call cannot be made without (e.g. no API key), or undefined when ready. */
|
|
234
255
|
protected missingRequirement(): ChatFailure | undefined;
|
|
235
256
|
/** The failure for a non-2xx HTTP status. */
|
|
@@ -263,4 +284,6 @@ export declare abstract class ChatCompletionsAdapter implements ModelAdapter {
|
|
|
263
284
|
/** The function tools this adapter sends for a request: none under `toolChoice: 'none'`. */
|
|
264
285
|
toolsFor(request: ModelRequest): Array<Record<string, unknown>>;
|
|
265
286
|
}
|
|
287
|
+
/** The system line asking for the schema where the provider takes JSON mode only (takesStrictJsonSchema). */
|
|
288
|
+
export declare function schemaInstruction(schema: unknown): string;
|
|
266
289
|
export {};
|
|
@@ -36,7 +36,10 @@
|
|
|
36
36
|
* (replaysReasoningContent: Kimi) writes the response's reasoning_content
|
|
37
37
|
* as providerState on the part it preceded and sends it back on that
|
|
38
38
|
* assistant message within the current turn's tool loop, for the same
|
|
39
|
-
* provider and model only (ADR 0046).
|
|
39
|
+
* provider and model only (ADR 0046). An adapter whose provider asks for
|
|
40
|
+
* it on every past assistant message of a request with tools
|
|
41
|
+
* (replaysReasoningOnEveryTurn: DeepSeek) sends it on all of them, an
|
|
42
|
+
* empty string where none of its own is stored.
|
|
40
43
|
* - A turn that thought, or ran out of tokens, without a reply or a tool
|
|
41
44
|
* call is an error final (`<ID>_MAX_TOKENS`, `<ID>_EMPTY_RESPONSE`, ADR
|
|
42
45
|
* 0027), carrying its usage. An adapter that opts in
|
|
@@ -382,6 +385,28 @@ export class ChatCompletionsAdapter {
|
|
|
382
385
|
replaysReasoningContent(_model) {
|
|
383
386
|
return false;
|
|
384
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Whether, on a request that carries `tools`, EVERY assistant message in
|
|
390
|
+
* the history (earlier turns' included, not only this turn's tool loop)
|
|
391
|
+
* carries `reasoning_content`: this adapter's own stored state for the
|
|
392
|
+
* same model where there is some, else an empty string, so the field is
|
|
393
|
+
* never missing. Default: never. Only meaningful beside
|
|
394
|
+
* replaysReasoningContent (DeepSeek, whose API returns 400 on a tool
|
|
395
|
+
* request missing a past turn's reasoning_content).
|
|
396
|
+
*/
|
|
397
|
+
replaysReasoningOnEveryTurn(_model) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Whether the provider enforces a strict `json_schema` response_format.
|
|
402
|
+
* Default: true. Where it takes JSON mode only (DeepSeek), an outputSchema
|
|
403
|
+
* goes as `response_format: json_object` and the schema rides in the
|
|
404
|
+
* system message instead; the engine validates the reply against it
|
|
405
|
+
* (lib/runtime/native/agentLoop.ts saveOutput).
|
|
406
|
+
*/
|
|
407
|
+
takesStrictJsonSchema(_model) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
385
410
|
/** A precondition the call cannot be made without (e.g. no API key), or undefined when ready. */
|
|
386
411
|
missingRequirement() {
|
|
387
412
|
return undefined;
|
|
@@ -684,7 +709,7 @@ export class ChatCompletionsAdapter {
|
|
|
684
709
|
...this.reasoningFields(model, request.reasoning),
|
|
685
710
|
...(tools.length > 0 ? { tools } : {}),
|
|
686
711
|
...(tools.length > 0 ? this.#toolChoice(choice, model, request.reasoning) : {}),
|
|
687
|
-
...this.#responseFormat(request),
|
|
712
|
+
...this.#responseFormat(request, model),
|
|
688
713
|
...this.extraBodyFields(request),
|
|
689
714
|
};
|
|
690
715
|
this.#nativeTools(request.nativeTools ?? [], body, model);
|
|
@@ -713,7 +738,9 @@ export class ChatCompletionsAdapter {
|
|
|
713
738
|
* `outputFormat: 'json'` without a schema is JSON mode (ADR 0061); each
|
|
714
739
|
* of Ollama, Kimi and the gateway takes it.
|
|
715
740
|
*/
|
|
716
|
-
#responseFormat(request) {
|
|
741
|
+
#responseFormat(request, model) {
|
|
742
|
+
if (request.outputSchema && !this.takesStrictJsonSchema(model))
|
|
743
|
+
return { response_format: { type: 'json_object' } };
|
|
717
744
|
if (request.outputSchema) {
|
|
718
745
|
return { response_format: { type: 'json_schema', json_schema: { name: 'response', strict: true, schema: toStrictJsonSchema(request.outputSchema) } } };
|
|
719
746
|
}
|
|
@@ -752,7 +779,13 @@ export class ChatCompletionsAdapter {
|
|
|
752
779
|
// span providers ask for it on. Earlier turns' state is left out: the
|
|
753
780
|
// stored history is not what the model saw (tool payloads are elided
|
|
754
781
|
// before storage), and replaying it bills it again as input (ADR 0046).
|
|
755
|
-
|
|
782
|
+
// An adapter whose provider wants it on every past assistant message of
|
|
783
|
+
// a tool request (DeepSeek) replays from the first, and fills the field.
|
|
784
|
+
const everyTurn = this.replaysReasoningContent(model) && this.replaysReasoningOnEveryTurn(model) && this.toolsFor(request).length > 0;
|
|
785
|
+
const turnStart = everyTurn ? -1 : this.replaysReasoningContent(model) ? currentTurnStart(request.messages) : Infinity;
|
|
786
|
+
// JSON mode only: the schema the provider cannot hold the reply to is asked for in the system message.
|
|
787
|
+
if (request.outputSchema && !this.takesStrictJsonSchema(model))
|
|
788
|
+
system.push(schemaInstruction(request.outputSchema));
|
|
756
789
|
request.messages.forEach((message, index) => {
|
|
757
790
|
switch (message.role) {
|
|
758
791
|
case 'system': {
|
|
@@ -817,7 +850,7 @@ export class ChatCompletionsAdapter {
|
|
|
817
850
|
messages.push({
|
|
818
851
|
role: 'assistant',
|
|
819
852
|
content: text || null,
|
|
820
|
-
...(reasoning.length > 0 ? { reasoning_content: reasoning.join('') } : {}),
|
|
853
|
+
...(reasoning.length > 0 ? { reasoning_content: reasoning.join('') } : everyTurn ? { reasoning_content: '' } : {}),
|
|
821
854
|
...(calls.length > 0 ? { tool_calls: calls } : {}),
|
|
822
855
|
});
|
|
823
856
|
}
|
|
@@ -840,6 +873,10 @@ export class ChatCompletionsAdapter {
|
|
|
840
873
|
console.warn(message);
|
|
841
874
|
}
|
|
842
875
|
}
|
|
876
|
+
/** The system line asking for the schema where the provider takes JSON mode only (takesStrictJsonSchema). */
|
|
877
|
+
export function schemaInstruction(schema) {
|
|
878
|
+
return `Reply with one JSON object, and nothing else, that matches this JSON Schema:\n${JSON.stringify(schema)}`;
|
|
879
|
+
}
|
|
843
880
|
/** A tool declaration as a function tool: the schema as written, or its strict form with `strict: true`. */
|
|
844
881
|
function functionTool(tool) {
|
|
845
882
|
return {
|
|
@@ -291,7 +291,7 @@ export interface Grounding {
|
|
|
291
291
|
/** Configuration and installation failures any adapter reports before it calls. */
|
|
292
292
|
type SetupErrorCode = 'MISSING_API_KEY' | 'ENDPOINT_MISCONFIGURED' | 'SDK_NOT_INSTALLED';
|
|
293
293
|
/** The chat-completions failures, under the provider's id (the gateway names the upstream provider). */
|
|
294
|
-
type ChatErrorCode = `${'MOONSHOT' | 'OLLAMA' | 'GEMINI' | 'ANTHROPIC' | 'OPENAI' | 'XAI'}_${'MAX_TOKENS' | 'EMPTY_RESPONSE' | 'UNREACHABLE'}` | 'MOONSHOT_MISSING_KEY' | 'MOONSHOT_HTTP_ERROR' | 'OLLAMA_HTTP_ERROR' | 'GATEWAY_NOT_CONFIGURED' | 'GATEWAY_KEY_MISSING' | 'GATEWAY_HTTP_ERROR';
|
|
294
|
+
type ChatErrorCode = `${'MOONSHOT' | 'DEEPSEEK' | 'OLLAMA' | 'GEMINI' | 'ANTHROPIC' | 'OPENAI' | 'XAI'}_${'MAX_TOKENS' | 'EMPTY_RESPONSE' | 'UNREACHABLE'}` | 'MOONSHOT_MISSING_KEY' | 'MOONSHOT_HTTP_ERROR' | 'DEEPSEEK_MISSING_KEY' | 'DEEPSEEK_HTTP_ERROR' | 'OLLAMA_HTTP_ERROR' | 'GATEWAY_NOT_CONFIGURED' | 'GATEWAY_KEY_MISSING' | 'GATEWAY_HTTP_ERROR';
|
|
295
295
|
/**
|
|
296
296
|
* Gemini's codes: a candidate's finish reason, or the prompt's block reason,
|
|
297
297
|
* is the code itself, as ADK's adapter reported it. GEMINI_ERROR is a failed
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/models/deepseekAdapter.ts — DeepSeek behind the engine's own model
|
|
3
|
+
* contract (lib/models/contract.ts, ADR 0048).
|
|
4
|
+
*
|
|
5
|
+
* WHY this file exists:
|
|
6
|
+
* Model optionality is a primary driver of this framework: the agent YAML
|
|
7
|
+
* declares `model`, and the registry routes it to the right provider.
|
|
8
|
+
* DeepSeek V4 is the cheapest capable model with tool calling and a 1M
|
|
9
|
+
* context on a first-party API (Flash at $0.15 / $0.60 per 1M off-peak),
|
|
10
|
+
* and its thinking mode needs request-side care the gateway cannot give
|
|
11
|
+
* it (reasoning_content back on every tool request, below), so it earns a
|
|
12
|
+
* direct adapter rather than only the gateway path; any `deepseek-*` id
|
|
13
|
+
* resolves to it (resolveAdapter, lib/models/adapterResolver.ts).
|
|
14
|
+
*
|
|
15
|
+
* WHY the chat-completions base (lib/models/chatCompletionsAdapter.ts):
|
|
16
|
+
* DeepSeek's API is OpenAI Chat Completions at https://api.deepseek.com
|
|
17
|
+
* (`/chat/completions`; it also serves a Responses API and an
|
|
18
|
+
* Anthropic-format API, neither adding a capability the chat dialect lacks
|
|
19
|
+
* here). The base provides tool calling, JSON mode, SSE streaming with
|
|
20
|
+
* usage, image parts as base64 data URIs, `reasoning_content` surfaced as
|
|
21
|
+
* thinking, and retries.
|
|
22
|
+
*
|
|
23
|
+
* HOW TO ENABLE:
|
|
24
|
+
* 1. Create a key at https://platform.deepseek.com/api_keys and put it in
|
|
25
|
+
* .env as DEEPSEEK_API_KEY.
|
|
26
|
+
* 2. Set model: "deepseek-flash" (or any deepseek-* id) in your YAML.
|
|
27
|
+
* The adapter is available when the key is set (logProviderStatuses()
|
|
28
|
+
* reports it).
|
|
29
|
+
* Optional: DEEPSEEK_BASE_URL for a proxy that speaks the same dialect.
|
|
30
|
+
*
|
|
31
|
+
* THE FAMILY (api-docs.deepseek.com › Models & Pricing, checked 2026-10-22;
|
|
32
|
+
* USD per 1M tokens, off-peak; peak is exactly double):
|
|
33
|
+
*
|
|
34
|
+
* model context max out in (miss) / cache hit / out use it for
|
|
35
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
* deepseek-flash 1M 384K $0.15 / $0.003 / $0.60 DEFAULT — DeepSeek-V4.1-Flash-1017:
|
|
37
|
+
* vision, tools, thinking
|
|
38
|
+
* deepseek-v4-pro 1M 384K $0.66 / $0.022 / $1.98 DeepSeek-V4-Pro-0813: the deeper
|
|
39
|
+
* tier; text only, no vision
|
|
40
|
+
*
|
|
41
|
+
* Peak hours are 01:00–04:00 and 06:00–10:00 UTC on weekdays. The price
|
|
42
|
+
* rows (db/migrations/0019_deepseek_kimi_pricing.sql) carry the PEAK
|
|
43
|
+
* rates, so a budget never underestimates a call.
|
|
44
|
+
* Retired but still accepted: `deepseek-v4-flash` and
|
|
45
|
+
* `deepseek-v4-flash-vision-exp`, both served by V4.1-Flash and billed at
|
|
46
|
+
* Flash's price. The pre-V4 `deepseek-chat` and `deepseek-reasoner` were
|
|
47
|
+
* retired after 2026-07-24 (third-party reports); the 404 hint names the
|
|
48
|
+
* current ids. With no direct key, MODEL_GATEWAY serves an id as
|
|
49
|
+
* `deepseek/<id>` (lib/models/gateway.ts), the gateways' own catalog
|
|
50
|
+
* permitting.
|
|
51
|
+
*
|
|
52
|
+
* REASONING (ADR 0047; api-docs.deepseek.com › Thinking Mode):
|
|
53
|
+
* Both models think by default, at effort `high`. The switch is
|
|
54
|
+
* `thinking: { type: "enabled" | "disabled" }` in the body, the depth
|
|
55
|
+
* `reasoning_effort` (low | high | max). A request with no `reasoning`
|
|
56
|
+
* sends neither: the vendor's default (`high`, the middle of three) is not
|
|
57
|
+
* its slowest, so no effort is pinned in lib/config.ts. `none` (or a
|
|
58
|
+
* budget of 0) sends `thinking: disabled` and no effort; any other level
|
|
59
|
+
* sends `thinking: enabled` and its DeepSeek word: `medium` → `high`,
|
|
60
|
+
* `xhigh` → `max` (a missing word rounds up, as on Kimi K3, ADR 0117;
|
|
61
|
+
* DeepSeek's own server-side mapping would take `xhigh` as `high`), a
|
|
62
|
+
* budget → the level that covers it. In thinking mode temperature,
|
|
63
|
+
* presence_penalty and frequency_penalty are ignored without an error.
|
|
64
|
+
*
|
|
65
|
+
* THINKING WITH TOOLS (the one rule this adapter exists for):
|
|
66
|
+
* When a request carries `tools`, the `reasoning_content` of ALL previous
|
|
67
|
+
* assistant messages must be sent back, or the API returns 400; without
|
|
68
|
+
* tools it is ignored. The adapter turns on the base's
|
|
69
|
+
* replaysReasoningContent and replaysReasoningOnEveryTurn: the field rides
|
|
70
|
+
* as providerState on the part that followed it (ADR 0046) and goes back
|
|
71
|
+
* on every assistant message of a tool request, earlier turns included,
|
|
72
|
+
* for the same model; where none of its own is stored (an earlier turn's
|
|
73
|
+
* state dropped by the transcript projection, another model's turn) it is
|
|
74
|
+
* sent as an empty string, so the field is never missing. Whether DeepSeek
|
|
75
|
+
* accepts an empty string there is not verified live. The replayed text is
|
|
76
|
+
* billed again as input, mostly at the cache-hit price.
|
|
77
|
+
*
|
|
78
|
+
* TOOL CHOICE: auto and none. Whether thinking mode honours a forced
|
|
79
|
+
* `tool_choice` is not documented on the thinking-mode page and not
|
|
80
|
+
* checked live, so `required` and a named tool are weakened to auto and
|
|
81
|
+
* marked on the span (llm.tool_choice.weakened).
|
|
82
|
+
*
|
|
83
|
+
* LIMITATIONS:
|
|
84
|
+
* - Structured output is JSON mode (`response_format: json_object`), not a
|
|
85
|
+
* strict schema: an outputSchema goes as JSON mode with the schema asked
|
|
86
|
+
* for in the system message (takesStrictJsonSchema), and the engine
|
|
87
|
+
* validates the reply. Beside tools the schema goes as set_model_response.
|
|
88
|
+
* - web_search: DeepSeek's Search and Fetch are separate per-request REST
|
|
89
|
+
* APIs, not a chat request field, so the sentinel is dropped with a
|
|
90
|
+
* warning, as on Kimi; `web_extract` works.
|
|
91
|
+
* - Vision on deepseek-flash only; an image sent to deepseek-v4-pro is
|
|
92
|
+
* refused by the API.
|
|
93
|
+
* - Not run live: no DeepSeek key was available when this adapter was
|
|
94
|
+
* written (2026-10-22). Every cell of its capability row is asserted
|
|
95
|
+
* against the request body the adapter sends
|
|
96
|
+
* (tests/capabilityMatrix.test.ts), and the reasoning replay in
|
|
97
|
+
* tests/models.test.ts, all offline.
|
|
98
|
+
*/
|
|
99
|
+
import type { ReasoningSetting } from './contract.ts';
|
|
100
|
+
import { ChatCompletionsAdapter } from './chatCompletionsAdapter.ts';
|
|
101
|
+
import type { ChatFailure } from './chatCompletionsAdapter.ts';
|
|
102
|
+
export declare const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
|
|
103
|
+
export interface DeepSeekAdapterOptions {
|
|
104
|
+
/** The model id as the YAML names it: "deepseek-*". */
|
|
105
|
+
model: string;
|
|
106
|
+
/** A caller's own key; default DEEPSEEK_API_KEY, read at call time. */
|
|
107
|
+
apiKey?: string;
|
|
108
|
+
/** Default: DEEPSEEK_BASE_URL in the environment, else https://api.deepseek.com. */
|
|
109
|
+
baseUrl?: string;
|
|
110
|
+
}
|
|
111
|
+
export declare class DeepSeekAdapter extends ChatCompletionsAdapter {
|
|
112
|
+
#private;
|
|
113
|
+
readonly provider = "deepseek";
|
|
114
|
+
constructor({ model, apiKey, baseUrl }: DeepSeekAdapterOptions);
|
|
115
|
+
protected endpointUrl(): string;
|
|
116
|
+
protected headers(): Record<string, string>;
|
|
117
|
+
/** reasoning_content goes back on the tool loop (see the header). */
|
|
118
|
+
protected replaysReasoningContent(_model: string): boolean;
|
|
119
|
+
/** ...and on every earlier assistant message of a request with tools, or DeepSeek returns 400. */
|
|
120
|
+
protected replaysReasoningOnEveryTurn(_model: string): boolean;
|
|
121
|
+
/** JSON mode only: no strict json_schema response_format. */
|
|
122
|
+
protected takesStrictJsonSchema(_model: string): boolean;
|
|
123
|
+
protected missingRequirement(): ChatFailure | undefined;
|
|
124
|
+
protected httpError(status: number, detail: string, model: string): ChatFailure;
|
|
125
|
+
/** The thinking switch and its effort (see the header). */
|
|
126
|
+
protected reasoningFields(model: string, setting: ReasoningSetting | undefined): Record<string, unknown>;
|
|
127
|
+
}
|