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
|
@@ -0,0 +1,157 @@
|
|
|
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 { ChatCompletionsAdapter, reasonsNotAtAll } from './chatCompletionsAdapter.js';
|
|
100
|
+
import { trimTrailingSlashes } from './urls.js';
|
|
101
|
+
import { credentialValue } from './credentialSource.js';
|
|
102
|
+
export const DEEPSEEK_BASE_URL = 'https://api.deepseek.com';
|
|
103
|
+
export class DeepSeekAdapter extends ChatCompletionsAdapter {
|
|
104
|
+
provider = 'deepseek';
|
|
105
|
+
#apiKey;
|
|
106
|
+
#baseUrl;
|
|
107
|
+
constructor({ model, apiKey, baseUrl }) {
|
|
108
|
+
super({ model });
|
|
109
|
+
this.#apiKey = apiKey;
|
|
110
|
+
this.#baseUrl = trimTrailingSlashes(baseUrl || process.env.DEEPSEEK_BASE_URL || DEEPSEEK_BASE_URL);
|
|
111
|
+
}
|
|
112
|
+
endpointUrl() {
|
|
113
|
+
return `${this.#baseUrl}/chat/completions`;
|
|
114
|
+
}
|
|
115
|
+
#key() {
|
|
116
|
+
return this.#apiKey ?? credentialValue('DEEPSEEK_API_KEY');
|
|
117
|
+
}
|
|
118
|
+
headers() {
|
|
119
|
+
return { Authorization: `Bearer ${this.#key() ?? ''}` };
|
|
120
|
+
}
|
|
121
|
+
/** reasoning_content goes back on the tool loop (see the header). */
|
|
122
|
+
replaysReasoningContent(_model) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
/** ...and on every earlier assistant message of a request with tools, or DeepSeek returns 400. */
|
|
126
|
+
replaysReasoningOnEveryTurn(_model) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
/** JSON mode only: no strict json_schema response_format. */
|
|
130
|
+
takesStrictJsonSchema(_model) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
missingRequirement() {
|
|
134
|
+
if (this.#key())
|
|
135
|
+
return undefined;
|
|
136
|
+
return { code: 'DEEPSEEK_MISSING_KEY', message: 'DEEPSEEK_API_KEY is not set in environment.' };
|
|
137
|
+
}
|
|
138
|
+
httpError(status, detail, model) {
|
|
139
|
+
const hint = status === 401
|
|
140
|
+
? ' Check DEEPSEEK_API_KEY (platform.deepseek.com/api_keys).'
|
|
141
|
+
: status === 404
|
|
142
|
+
? ` Is "${model}" a current DeepSeek id? The current ids are deepseek-flash and deepseek-v4-pro; deepseek-chat and deepseek-reasoner were retired after 2026-07-24.`
|
|
143
|
+
: '';
|
|
144
|
+
return {
|
|
145
|
+
code: 'DEEPSEEK_HTTP_ERROR',
|
|
146
|
+
message: `DeepSeek returned ${status}: ${detail.slice(0, 4000)}.${hint}`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/** The thinking switch and its effort (see the header). */
|
|
150
|
+
reasoningFields(model, setting) {
|
|
151
|
+
if (setting === undefined)
|
|
152
|
+
return {}; // DeepSeek's default: thinking on, effort high
|
|
153
|
+
if (reasonsNotAtAll(setting))
|
|
154
|
+
return { thinking: { type: 'disabled' } };
|
|
155
|
+
return { thinking: { type: 'enabled' }, reasoning_effort: this.effortFor(model, setting) };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* gemini direct | vertex Google Application Default Credentials
|
|
12
12
|
* anthropic direct | bedrock | vertex AWS credential chain / Google ADC
|
|
13
13
|
* openai direct | azure AZURE_OPENAI_API_KEY, else Entra ID
|
|
14
|
-
* xai, moonshot, ollama direct only (moonshot:
|
|
14
|
+
* xai, moonshot, deepseek, ollama direct only (moonshot, deepseek:
|
|
15
|
+
* MOONSHOT_BASE_URL, DEEPSEEK_BASE_URL for a proxy)
|
|
15
16
|
*
|
|
16
17
|
* Configuration comes from the environment (`endpointFromEnv`) or, per
|
|
17
18
|
* request, from the server's `credentials` plug point, which may return an
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* gemini direct | vertex Google Application Default Credentials
|
|
12
12
|
* anthropic direct | bedrock | vertex AWS credential chain / Google ADC
|
|
13
13
|
* openai direct | azure AZURE_OPENAI_API_KEY, else Entra ID
|
|
14
|
-
* xai, moonshot, ollama direct only (moonshot:
|
|
14
|
+
* xai, moonshot, deepseek, ollama direct only (moonshot, deepseek:
|
|
15
|
+
* MOONSHOT_BASE_URL, DEEPSEEK_BASE_URL for a proxy)
|
|
15
16
|
*
|
|
16
17
|
* Configuration comes from the environment (`endpointFromEnv`) or, per
|
|
17
18
|
* request, from the server's `credentials` plug point, which may return an
|
|
@@ -38,6 +39,7 @@ export const PLATFORMS_FOR = {
|
|
|
38
39
|
openai: ['direct', 'azure'],
|
|
39
40
|
xai: ['direct'],
|
|
40
41
|
moonshot: ['direct'],
|
|
42
|
+
deepseek: ['direct'],
|
|
41
43
|
ollama: ['direct'],
|
|
42
44
|
};
|
|
43
45
|
const PLATFORM_ENV = {
|
|
@@ -104,6 +106,8 @@ export function endpointFromEnv(provider, env = credentialEnv()) {
|
|
|
104
106
|
return { ...base, ...(trimmed(env.OPENAI_BASE_URL) ? { baseURL: trimmed(env.OPENAI_BASE_URL) } : {}) };
|
|
105
107
|
case 'moonshot/direct':
|
|
106
108
|
return { ...base, ...(trimmed(env.MOONSHOT_BASE_URL) ? { baseURL: trimmed(env.MOONSHOT_BASE_URL) } : {}) };
|
|
109
|
+
case 'deepseek/direct':
|
|
110
|
+
return { ...base, ...(trimmed(env.DEEPSEEK_BASE_URL) ? { baseURL: trimmed(env.DEEPSEEK_BASE_URL) } : {}) };
|
|
107
111
|
case 'openai/azure': {
|
|
108
112
|
const endpoint = trimmed(env.AZURE_OPENAI_ENDPOINT);
|
|
109
113
|
const key = trimmed(env.AZURE_OPENAI_API_KEY);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* WHY this file exists:
|
|
5
5
|
* A newcomer should be able to run every starter-pack syndicate with ONE
|
|
6
6
|
* credential when they choose to. A hosted gateway (Vercel AI Gateway,
|
|
7
|
-
* OpenRouter) fronts OpenAI, Anthropic, Google, xAI and
|
|
7
|
+
* OpenRouter) fronts OpenAI, Anthropic, Google, xAI, Moonshot and DeepSeek behind a single
|
|
8
8
|
* OpenAI-compatible endpoint, so a model id whose direct key is absent can
|
|
9
9
|
* still be served. This module owns that decision and nothing else.
|
|
10
10
|
*
|
|
@@ -81,7 +81,8 @@ export declare function gatewayUsable(): boolean;
|
|
|
81
81
|
* Gateways prefix every id with the upstream provider's slug and spell
|
|
82
82
|
* Anthropic's version segment with a dot ("claude-sonnet-4-6" is
|
|
83
83
|
* "anthropic/claude-sonnet-4.6" on both Vercel and OpenRouter). Gemini,
|
|
84
|
-
* GPT, Grok and
|
|
84
|
+
* GPT, Grok, Kimi and DeepSeek ids already carry dots (or need none), so only the
|
|
85
|
+
* prefix is added. An id
|
|
85
86
|
* the mapper gets wrong is fixed in MODEL_GATEWAY_MODEL_MAP, not in code.
|
|
86
87
|
*/
|
|
87
88
|
export declare function gatewayWireModel(model: string, gateway: GatewayInfo): string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* WHY this file exists:
|
|
5
5
|
* A newcomer should be able to run every starter-pack syndicate with ONE
|
|
6
6
|
* credential when they choose to. A hosted gateway (Vercel AI Gateway,
|
|
7
|
-
* OpenRouter) fronts OpenAI, Anthropic, Google, xAI and
|
|
7
|
+
* OpenRouter) fronts OpenAI, Anthropic, Google, xAI, Moonshot and DeepSeek behind a single
|
|
8
8
|
* OpenAI-compatible endpoint, so a model id whose direct key is absent can
|
|
9
9
|
* still be served. This module owns that decision and nothing else.
|
|
10
10
|
*
|
|
@@ -52,14 +52,14 @@ export const GATEWAYS = {
|
|
|
52
52
|
label: 'Vercel AI Gateway',
|
|
53
53
|
baseUrl: 'https://ai-gateway.vercel.sh/v1',
|
|
54
54
|
consoleUrl: 'https://vercel.com/ai-gateway',
|
|
55
|
-
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'xai', moonshot: 'moonshotai' },
|
|
55
|
+
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'xai', moonshot: 'moonshotai', deepseek: 'deepseek' },
|
|
56
56
|
},
|
|
57
57
|
openrouter: {
|
|
58
58
|
id: 'openrouter',
|
|
59
59
|
label: 'OpenRouter',
|
|
60
60
|
baseUrl: 'https://openrouter.ai/api/v1',
|
|
61
61
|
consoleUrl: 'https://openrouter.ai/keys',
|
|
62
|
-
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'x-ai', moonshot: 'moonshotai' },
|
|
62
|
+
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'x-ai', moonshot: 'moonshotai', deepseek: 'deepseek' },
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
/**
|
|
@@ -124,7 +124,8 @@ function modelMapFromEnv() {
|
|
|
124
124
|
* Gateways prefix every id with the upstream provider's slug and spell
|
|
125
125
|
* Anthropic's version segment with a dot ("claude-sonnet-4-6" is
|
|
126
126
|
* "anthropic/claude-sonnet-4.6" on both Vercel and OpenRouter). Gemini,
|
|
127
|
-
* GPT, Grok and
|
|
127
|
+
* GPT, Grok, Kimi and DeepSeek ids already carry dots (or need none), so only the
|
|
128
|
+
* prefix is added. An id
|
|
128
129
|
* the mapper gets wrong is fixed in MODEL_GATEWAY_MODEL_MAP, not in code.
|
|
129
130
|
*/
|
|
130
131
|
export function gatewayWireModel(model, gateway) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* WHAT STAYS TRUE THROUGH THE GATEWAY:
|
|
14
14
|
* - Attribution. `provider` is the YAML id's provider (anthropic, openai,
|
|
15
|
-
* gemini, xai, moonshot), never "gateway", so the ledger and any
|
|
15
|
+
* gemini, xai, moonshot, deepseek), never "gateway", so the ledger and any
|
|
16
16
|
* per-agent cost view keep the same provider column (ADR 0009). The
|
|
17
17
|
* transport is a separate span attribute: llm.transport = gateway:<id>.
|
|
18
18
|
* - Tool calling, structured output, reasoning (as `reasoning_effort`, the
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* WHAT STAYS TRUE THROUGH THE GATEWAY:
|
|
14
14
|
* - Attribution. `provider` is the YAML id's provider (anthropic, openai,
|
|
15
|
-
* gemini, xai, moonshot), never "gateway", so the ledger and any
|
|
15
|
+
* gemini, xai, moonshot, deepseek), never "gateway", so the ledger and any
|
|
16
16
|
* per-agent cost view keep the same provider column (ADR 0009). The
|
|
17
17
|
* transport is a separate span attribute: llm.transport = gateway:<id>.
|
|
18
18
|
* - Tool calling, structured output, reasoning (as `reasoning_effort`, the
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
* Model-name conventions (see DOCUMENTATION.md §7):
|
|
12
12
|
* claude-* → anthropic gpt-* / o<digit>* → openai
|
|
13
13
|
* grok-* → xai ollama/<model> → ollama (local, keyless)
|
|
14
|
-
* kimi-* → moonshot
|
|
14
|
+
* kimi-* → moonshot deepseek-* → deepseek
|
|
15
|
+
* everything else → gemini (the default)
|
|
15
16
|
*/
|
|
16
|
-
export type ProviderId = 'gemini' | 'anthropic' | 'openai' | 'xai' | 'moonshot' | 'ollama';
|
|
17
|
+
export type ProviderId = 'gemini' | 'anthropic' | 'openai' | 'xai' | 'moonshot' | 'deepseek' | 'ollama';
|
|
17
18
|
interface ProviderInfo {
|
|
18
19
|
/** Env var holding the API key; null = keyless (local). */
|
|
19
20
|
keyEnv: string | null;
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* Model-name conventions (see DOCUMENTATION.md §7):
|
|
12
12
|
* claude-* → anthropic gpt-* / o<digit>* → openai
|
|
13
13
|
* grok-* → xai ollama/<model> → ollama (local, keyless)
|
|
14
|
-
* kimi-* → moonshot
|
|
14
|
+
* kimi-* → moonshot deepseek-* → deepseek
|
|
15
|
+
* everything else → gemini (the default)
|
|
15
16
|
*/
|
|
16
17
|
import { credentialValue } from './credentialSource.js';
|
|
17
18
|
export const PROVIDERS = {
|
|
@@ -20,6 +21,7 @@ export const PROVIDERS = {
|
|
|
20
21
|
openai: { keyEnv: 'OPENAI_API_KEY', label: 'OpenAI GPT' },
|
|
21
22
|
xai: { keyEnv: 'XAI_API_KEY', label: 'xAI Grok' },
|
|
22
23
|
moonshot: { keyEnv: 'MOONSHOT_API_KEY', label: 'Moonshot Kimi' },
|
|
24
|
+
deepseek: { keyEnv: 'DEEPSEEK_API_KEY', label: 'DeepSeek' },
|
|
23
25
|
ollama: { keyEnv: null, label: 'Ollama (local)' },
|
|
24
26
|
};
|
|
25
27
|
/** Maps a model id to its provider. Unknown ids default to gemini, as
|
|
@@ -35,6 +37,8 @@ export function providerForModel(model) {
|
|
|
35
37
|
return 'xai';
|
|
36
38
|
if (/^kimi-/.test(model))
|
|
37
39
|
return 'moonshot';
|
|
40
|
+
if (/^deepseek-/.test(model))
|
|
41
|
+
return 'deepseek';
|
|
38
42
|
return 'gemini';
|
|
39
43
|
}
|
|
40
44
|
/** True when the provider's key is present (always true for local Ollama). */
|
|
@@ -26,7 +26,7 @@ export declare const isAboveHigh: (setting: ReasoningSetting | undefined) => set
|
|
|
26
26
|
* The highest level the direct adapter for `model` sends as asked (ADR
|
|
27
27
|
* 0117); a level above it goes as this one. `max` where every level
|
|
28
28
|
* passes: Claude's adaptive generations (the Claude adapter sends its budget
|
|
29
|
-
* generations `high`'s budget)
|
|
29
|
+
* generations `high`'s budget), Kimi K3 and DeepSeek. OpenAI's GPT-5.2 and later and
|
|
30
30
|
* grok-4.7 stop at `xhigh`; other OpenAI and Grok ids, Gemini and Ollama at
|
|
31
31
|
* `high`. A gateway stops at `high` whatever the id (gatewayAdapter.ts).
|
|
32
32
|
*/
|
|
@@ -44,7 +44,8 @@ function reasoningEffort(model, level) {
|
|
|
44
44
|
case 'xai':
|
|
45
45
|
return level === 'none' ? 'low' : level; // Grok 4.5/4.7 cannot stop reasoning
|
|
46
46
|
case 'moonshot':
|
|
47
|
-
|
|
47
|
+
case 'deepseek':
|
|
48
|
+
// K3 and DeepSeek have low | high | max: a missing word rounds up.
|
|
48
49
|
return level === 'medium' ? 'high' : level === 'xhigh' ? 'max' : level;
|
|
49
50
|
default:
|
|
50
51
|
return level;
|
|
@@ -54,7 +55,7 @@ function reasoningEffort(model, level) {
|
|
|
54
55
|
* The highest level the direct adapter for `model` sends as asked (ADR
|
|
55
56
|
* 0117); a level above it goes as this one. `max` where every level
|
|
56
57
|
* passes: Claude's adaptive generations (the Claude adapter sends its budget
|
|
57
|
-
* generations `high`'s budget)
|
|
58
|
+
* generations `high`'s budget), Kimi K3 and DeepSeek. OpenAI's GPT-5.2 and later and
|
|
58
59
|
* grok-4.7 stop at `xhigh`; other OpenAI and Grok ids, Gemini and Ollama at
|
|
59
60
|
* `high`. A gateway stops at `high` whatever the id (gatewayAdapter.ts).
|
|
60
61
|
*/
|
|
@@ -62,6 +63,7 @@ export function effortCeiling(model) {
|
|
|
62
63
|
switch (providerForModel(model)) {
|
|
63
64
|
case 'anthropic':
|
|
64
65
|
case 'moonshot':
|
|
66
|
+
case 'deepseek':
|
|
65
67
|
return 'max';
|
|
66
68
|
case 'openai':
|
|
67
69
|
return openAiTakesXhigh(model) ? 'xhigh' : 'high';
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* resolves when an agent first calls it.
|
|
24
24
|
*/
|
|
25
25
|
import { endpointFromEnv, endpointProblems, providerReady } from './endpoints.js';
|
|
26
|
-
import { DEFAULT_CLAUDE_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_GPT_MODEL, DEFAULT_GROK_MODEL, DEFAULT_KIMI_MODEL, DEFAULT_OLLAMA_MODEL, } from '../config.js';
|
|
26
|
+
import { DEFAULT_CLAUDE_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_GPT_MODEL, DEFAULT_GROK_MODEL, DEFAULT_KIMI_MODEL, DEFAULT_DEEPSEEK_MODEL, DEFAULT_OLLAMA_MODEL, } from '../config.js';
|
|
27
27
|
import { geminiAdapterSetting, normalizeProvider, resolveAdapter } from './adapterResolver.js';
|
|
28
28
|
import { gatewayConfig, gatewayProblem, gatewayUsable } from './gateway.js';
|
|
29
29
|
import { PROVIDERS, providerForModel, providerKeyPresent, } from './providerMap.js';
|
|
@@ -105,6 +105,8 @@ function modelHint(provider) {
|
|
|
105
105
|
return 'grok-*';
|
|
106
106
|
case 'moonshot':
|
|
107
107
|
return 'kimi-*';
|
|
108
|
+
case 'deepseek':
|
|
109
|
+
return 'deepseek-*';
|
|
108
110
|
case 'ollama':
|
|
109
111
|
return 'ollama/*';
|
|
110
112
|
case 'gemini':
|
|
@@ -117,6 +119,7 @@ const DEFAULT_MODEL_FOR = {
|
|
|
117
119
|
openai: DEFAULT_GPT_MODEL,
|
|
118
120
|
xai: DEFAULT_GROK_MODEL,
|
|
119
121
|
moonshot: DEFAULT_KIMI_MODEL,
|
|
122
|
+
deepseek: DEFAULT_DEEPSEEK_MODEL,
|
|
120
123
|
ollama: DEFAULT_OLLAMA_MODEL,
|
|
121
124
|
};
|
|
122
125
|
/**
|
|
@@ -155,7 +155,7 @@ export declare function validTraceparent(value: unknown): string | undefined;
|
|
|
155
155
|
export declare function callerSpanContext(traceparent: string | undefined): SpanContext | undefined;
|
|
156
156
|
export declare function traceAgentRun(stream: AsyncIterableIterator<Event>, metadata: TraceMetadata): AsyncGenerator<Event, void, void>;
|
|
157
157
|
export interface LlmCallMeta {
|
|
158
|
-
/** Provider id, e.g. 'anthropic', 'openai', 'xai', 'moonshot', 'ollama', 'gemini'. */
|
|
158
|
+
/** Provider id, e.g. 'anthropic', 'openai', 'xai', 'moonshot', 'deepseek', 'ollama', 'gemini'. */
|
|
159
159
|
provider: string;
|
|
160
160
|
/** The model id as declared in the agent YAML (e.g. 'ollama/qwen3:8b'). */
|
|
161
161
|
model: string;
|
package/dist/lib/onboarding.js
CHANGED
|
@@ -106,6 +106,7 @@ const PROVIDER_CONSOLE = {
|
|
|
106
106
|
openai: 'https://platform.openai.com/api-keys',
|
|
107
107
|
xai: 'https://console.x.ai',
|
|
108
108
|
moonshot: 'https://platform.moonshot.ai',
|
|
109
|
+
deepseek: 'https://platform.deepseek.com/api_keys',
|
|
109
110
|
};
|
|
110
111
|
const PREFIX = {
|
|
111
112
|
gemini: 'gemini-* (and any id without another prefix)',
|
|
@@ -113,6 +114,7 @@ const PREFIX = {
|
|
|
113
114
|
openai: 'gpt-*, o<digit>*',
|
|
114
115
|
xai: 'grok-*',
|
|
115
116
|
moonshot: 'kimi-*',
|
|
117
|
+
deepseek: 'deepseek-*',
|
|
116
118
|
};
|
|
117
119
|
const CLOUD_PROVIDERS = Object.keys(PROVIDER_CONSOLE);
|
|
118
120
|
function providerKeyLines() {
|
|
@@ -154,7 +156,7 @@ export const LEVELS = [
|
|
|
154
156
|
},
|
|
155
157
|
{
|
|
156
158
|
id: 'one-provider',
|
|
157
|
-
menu: "One provider's API key (Gemini, Anthropic, OpenAI, xAI or
|
|
159
|
+
menu: "One provider's API key (Gemini, Anthropic, OpenAI, xAI, Moonshot or DeepSeek)",
|
|
158
160
|
title: "One provider's API key",
|
|
159
161
|
summary: "The model id's prefix picks the provider, and that provider's key funds it. One key runs every file whose agents all use that provider. A Gemini key runs the most shipped files and also powers long-term memory's embeddings.",
|
|
160
162
|
env: providerKeyLines(),
|
|
@@ -120,6 +120,10 @@ export declare const agentSchema: z.ZodObject<{
|
|
|
120
120
|
instruction: z.ZodOptional<z.ZodString>;
|
|
121
121
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
122
122
|
}, z.core.$strict>>;
|
|
123
|
+
deepseek: z.ZodOptional<z.ZodObject<{
|
|
124
|
+
instruction: z.ZodOptional<z.ZodString>;
|
|
125
|
+
instruction_append: z.ZodOptional<z.ZodString>;
|
|
126
|
+
}, z.core.$strict>>;
|
|
123
127
|
ollama: z.ZodOptional<z.ZodObject<{
|
|
124
128
|
instruction: z.ZodOptional<z.ZodString>;
|
|
125
129
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
@@ -338,6 +342,10 @@ export declare const subagentSchema: z.ZodObject<{
|
|
|
338
342
|
instruction: z.ZodOptional<z.ZodString>;
|
|
339
343
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
340
344
|
}, z.core.$strict>>;
|
|
345
|
+
deepseek: z.ZodOptional<z.ZodObject<{
|
|
346
|
+
instruction: z.ZodOptional<z.ZodString>;
|
|
347
|
+
instruction_append: z.ZodOptional<z.ZodString>;
|
|
348
|
+
}, z.core.$strict>>;
|
|
341
349
|
ollama: z.ZodOptional<z.ZodObject<{
|
|
342
350
|
instruction: z.ZodOptional<z.ZodString>;
|
|
343
351
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
@@ -562,6 +570,10 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
562
570
|
instruction: z.ZodOptional<z.ZodString>;
|
|
563
571
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
564
572
|
}, z.core.$strict>>;
|
|
573
|
+
deepseek: z.ZodOptional<z.ZodObject<{
|
|
574
|
+
instruction: z.ZodOptional<z.ZodString>;
|
|
575
|
+
instruction_append: z.ZodOptional<z.ZodString>;
|
|
576
|
+
}, z.core.$strict>>;
|
|
565
577
|
ollama: z.ZodOptional<z.ZodObject<{
|
|
566
578
|
instruction: z.ZodOptional<z.ZodString>;
|
|
567
579
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
@@ -774,6 +786,10 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
774
786
|
instruction: z.ZodOptional<z.ZodString>;
|
|
775
787
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
776
788
|
}, z.core.$strict>>;
|
|
789
|
+
deepseek: z.ZodOptional<z.ZodObject<{
|
|
790
|
+
instruction: z.ZodOptional<z.ZodString>;
|
|
791
|
+
instruction_append: z.ZodOptional<z.ZodString>;
|
|
792
|
+
}, z.core.$strict>>;
|
|
777
793
|
ollama: z.ZodOptional<z.ZodObject<{
|
|
778
794
|
instruction: z.ZodOptional<z.ZodString>;
|
|
779
795
|
instruction_append: z.ZodOptional<z.ZodString>;
|
|
@@ -146,6 +146,7 @@ const modelOverridesSchema = z
|
|
|
146
146
|
openai: modelOverrideEntry.optional(),
|
|
147
147
|
xai: modelOverrideEntry.optional(),
|
|
148
148
|
moonshot: modelOverrideEntry.optional(),
|
|
149
|
+
deepseek: modelOverrideEntry.optional(),
|
|
149
150
|
ollama: modelOverrideEntry.optional(),
|
|
150
151
|
})
|
|
151
152
|
.describe('Per-provider instruction, keyed by the provider of the agent\'s model id (lib/models/providerMap.ts). Applied at compile time before the skills index; not to fallback_model, and never to globalInstruction (ADR 0115).');
|
package/dist/lib/toolRegistry.js
CHANGED
|
@@ -56,7 +56,7 @@ const BUILTIN_TOOLS = {
|
|
|
56
56
|
web_search: WEB_SEARCH_MARKER,
|
|
57
57
|
// Deterministic complement to web_search: client-side URL → clean-text
|
|
58
58
|
// reading (keyless — works on every provider, including local Ollama).
|
|
59
|
-
//
|
|
59
|
+
// current_events.yaml and librarian-style research agents declare it.
|
|
60
60
|
web_extract: webExtractTool,
|
|
61
61
|
// Gemini reads URLs in the conversation server-side; a no-op (reported as
|
|
62
62
|
// dropped by the doctor) on other providers. lib/tools/nativeTools.ts.
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
/** Prefixes and names of variables the framework itself reads: never an API credential. */
|
|
18
18
|
const FRAMEWORK_ENV_PREFIXES = [
|
|
19
19
|
'A2A_', 'SUPABASE_', 'DATABASE_', 'MCP_', 'MODEL_', 'MEMORY_', 'OTEL_', 'TELEMETRY_', 'MELCHIZEDEK_',
|
|
20
|
-
'GOOGLE_', 'GEMINI_', 'ANTHROPIC_', 'OPENAI_', 'AZURE_', 'AWS_', 'XAI_', 'MOONSHOT_', 'OLLAMA_', 'WIKI_',
|
|
20
|
+
'GOOGLE_', 'GEMINI_', 'ANTHROPIC_', 'OPENAI_', 'AZURE_', 'AWS_', 'XAI_', 'MOONSHOT_', 'DEEPSEEK_', 'OLLAMA_', 'WIKI_',
|
|
21
21
|
'ALLOW_', 'OPENAPI_',
|
|
22
22
|
];
|
|
23
23
|
const FRAMEWORK_ENV_NAMES = new Set(['PUBLIC_URL', 'HOST', 'PORT', 'PATH', 'HOME', 'NODE_OPTIONS', 'WEB_EXTRACT_CHAR_LIMIT', 'OAUTH_REDIRECT_URI', 'OAUTH_CALLBACK_IDENTITY']);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "melchizedek-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "A multi-model, multi-agent orchestration framework — YAML-defined syndicates on the engine's own agent loop, Supabase or Postgres session persistence, pgvector long-term memory. Companion repo to the lyceumagents.com curriculum.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"syndicate:librarian": "node --disable-warning=DEP0040 --experimental-strip-types scripts/syndicate_chat.ts --syndicate librarian",
|
|
105
105
|
"syndicate:scriptorium": "node --disable-warning=DEP0040 --experimental-strip-types scripts/syndicate_chat.ts --syndicate scriptorium",
|
|
106
106
|
"syndicate:cartographers": "node --disable-warning=DEP0040 --experimental-strip-types scripts/syndicate_chat.ts --syndicate cartographers",
|
|
107
|
-
"syndicate:
|
|
107
|
+
"syndicate:current-events": "node --disable-warning=DEP0040 --experimental-strip-types scripts/syndicate_chat.ts --syndicate current_events",
|
|
108
108
|
"syndicate:scribe": "node --disable-warning=DEP0040 --experimental-strip-types scripts/syndicate_chat.ts --syndicate scribe",
|
|
109
109
|
"syndicate:assistant": "node --disable-warning=DEP0040 --experimental-strip-types scripts/syndicate_chat.ts --syndicate assistant",
|
|
110
110
|
"syndicate:harness": "node --disable-warning=DEP0040 --experimental-strip-types scripts/syndicate_chat.ts --syndicate harness",
|
|
@@ -41,7 +41,7 @@ Agent block keys (orchestrator and each subagent):
|
|
|
41
41
|
- `reasoning:` how hard the agent reasons, on any provider: `none`, `low`, `medium`, `high`, or `{ budget_tokens: 4096 }`. The compiler sends each provider the field it reads, so the setting survives a change to the `model:` line; unset keeps the provider's default. The templates in `config/agents/templates/` give a gathering or routing agent `none` and a synthesizing agent `low` or `medium`. What each level becomes per provider and per Claude model generation belongs to the melchizedek-models skill.
|
|
42
42
|
- `sampling:` per agent: `temperature`, `top_p`, `max_output_tokens`, `stop`; each adapter sends the fields its provider takes. Thinking tokens count against `max_output_tokens`, so a reasoning agent with long output needs a high ceiling (the Scribe uses 24576).
|
|
43
43
|
- `output:` what the agent answers in: `schema`, a JSON schema (types in upper case: `OBJECT`, `STRING`, `INTEGER`, `BOOLEAN`, `ARRAY`) the agent's reply must satisfy, and `mime`, `"application/json"` for JSON mode or `"text/plain"` (the default).
|
|
44
|
-
- `model_overrides:` a prompt nuance for one provider, keyed `gemini`, `anthropic`, `openai`, `xai`, `moonshot` or `ollama`; each entry holds `instruction` (replaces the agent's instruction on that provider) or `instruction_append` (added after it). The entry for the provider of the agent's `model:` applies.
|
|
44
|
+
- `model_overrides:` a prompt nuance for one provider, keyed `gemini`, `anthropic`, `openai`, `xai`, `moonshot`, `deepseek` or `ollama`; each entry holds `instruction` (replaces the agent's instruction on that provider) or `instruction_append` (added after it). The entry for the provider of the agent's `model:` applies.
|
|
45
45
|
- `generateContentConfig:` and `outputSchema:` are the deprecated v1 spelling of `sampling:`, `output:` and `reasoning:`: they still load and behave the same, with one deprecation line per file, and an agent that sets a v2 key beside its v1 spelling fails to load. `npx melchizedek-codemod <file|dir>` rewrites a v1 file to v2, comments kept (`--check` only reports). New agents use the v2 keys.
|
|
46
46
|
- Subagents receive one string argument, `query`, and see nothing else of the conversation. The orchestrator's instruction must say what to pass them, in full, every time.
|
|
47
47
|
|
|
@@ -6,19 +6,19 @@ STRUCTURE (exact):
|
|
|
6
6
|
Frontmatter, verbatim:
|
|
7
7
|
---
|
|
8
8
|
name: melchizedek-models
|
|
9
|
-
description: "Choose and wire a model for a Melchizedek agent: how a model id routes to Gemini, Claude, GPT, Grok, Kimi, or local Ollama, which environment variable each needs, the gateway fallback, the doctor, and per-agent settings such as how hard an agent reasons. Use when the user changes a model line, adds a provider key, wants an agent to think more or less, sees a missing-key error, Model not found or a gateway error, or asks which keys a syndicate needs."
|
|
9
|
+
description: "Choose and wire a model for a Melchizedek agent: how a model id routes to Gemini, Claude, GPT, Grok, Kimi, DeepSeek, or local Ollama, which environment variable each needs, the gateway fallback, the doctor, and per-agent settings such as how hard an agent reasons. Use when the user changes a model line, adds a provider key, wants an agent to think more or less, sees a missing-key error, Model not found or a gateway error, or asks which keys a syndicate needs."
|
|
10
10
|
---
|
|
11
11
|
Then `##` sections in this order: "How a model id routes", "Which key unlocks what", "Run with no key at all", "One key for every cloud provider", "Settings per agent", "Mixing providers in one syndicate", "Reading the errors".
|
|
12
12
|
|
|
13
13
|
FACTS:
|
|
14
14
|
How a model id routes:
|
|
15
|
-
- The `model:` string's prefix names the provider: `claude-*` to Anthropic, `gpt-*` and `o<digit>*` to OpenAI, `grok-*` to xAI, `kimi-*` to Moonshot AI (Kimi), `ollama/<model>` to a local Ollama, and everything else to Gemini (the default, served by the engine's own Gemini adapter).
|
|
15
|
+
- The `model:` string's prefix names the provider: `claude-*` to Anthropic, `gpt-*` and `o<digit>*` to OpenAI, `grok-*` to xAI, `kimi-*` to Moonshot AI (Kimi), `deepseek-*` to DeepSeek, `ollama/<model>` to a local Ollama, and everything else to Gemini (the default, served by the engine's own Gemini adapter).
|
|
16
16
|
- There is no allowlist in the engine: any id a provider currently serves works as written. A new model is a one-line YAML change.
|
|
17
17
|
- Ids verified in this deployment: `gemini-3.8-flash` (production), `gemini-3.5-flash-lite` (subagents, cost), `claude-sonnet-4-6`, `claude-opus-4-6`, `claude-haiku-4-5-20251001`, `gpt-5-mini`, `gpt-5`, `grok-4.7`, `kimi-k3`, `ollama/qwen3:8b`.
|
|
18
18
|
- `gemini-2.5-flash` returns a 400 about tool call context circulation with the server-side tool flag the framework sets; use `gemini-3.8-flash` or newer.
|
|
19
19
|
- Subagents inherit the orchestrator's `model` when they set none.
|
|
20
20
|
Which key unlocks what:
|
|
21
|
-
- `GOOGLE_GENAI_API_KEY` for Gemini (also the embedding model behind long-term memory); `ANTHROPIC_API_KEY`; `OPENAI_API_KEY`; `XAI_API_KEY`; `MOONSHOT_API_KEY` for Kimi; no key for `ollama/*`.
|
|
21
|
+
- `GOOGLE_GENAI_API_KEY` for Gemini (also the embedding model behind long-term memory); `ANTHROPIC_API_KEY`; `OPENAI_API_KEY`; `XAI_API_KEY`; `MOONSHOT_API_KEY` for Kimi; `DEEPSEEK_API_KEY` for DeepSeek (`deepseek-flash`, the default, and `deepseek-v4-pro`, the deeper text-only tier; billed from a balance topped up first); no key for `ollama/*`.
|
|
22
22
|
- A provider is available only when its key is present; a missing key shows the provider as disabled in the doctor and the startup log, and an agent on that provider fails its turn with an error naming the key (`ANTHROPIC_API_KEY is not set in environment.`).
|
|
23
23
|
- `npx melchizedek-doctor` (clone: `npm run doctor`) reads every syndicate, resolves each agent's model under the current `.env`, and prints per agent: model, provider, which server-side search tools the path keeps or drops, and whether it is funded; per syndicate a verdict; and the variables that would unlock the most. `--json`, `--check` (exit 1 when any syndicate is blocked). Read-only, no key value printed.
|
|
24
24
|
- The `# tier:` first line of every starter-pack file states its cost class (`keyless`, one provider name, or `multi-provider`); the doctor checks the claim against the models.
|
|
@@ -34,21 +34,22 @@ One key for every cloud provider (the gateway fallback):
|
|
|
34
34
|
Settings per agent:
|
|
35
35
|
- `reasoning:` on the agent block, beside `model:`, sets how hard the agent reasons: `none`, `low`, `medium`, `high`, `xhigh`, `max`, or `{ budget_tokens: 4096 }`. It reads the same on every provider; unset keeps the provider's default. The compiler sends each provider the field it reads: Gemini 3 and later get a thinking level (`none` is `MINIMAL`, then `LOW`, `MEDIUM`, `HIGH`); Gemini 2.x, and Claude 4.6 or older, get a thinking budget of 0, 2048, 8192 or 16384 tokens; every later Claude model gets adaptive thinking at an effort of `low`, `medium` or `high`, and `none` is that model's own off switch, sent at `low` effort; every other provider, and the gateway, gets an effort word, and where the provider lacks that word, its nearest setting above.
|
|
36
36
|
- `xhigh` and `max` ask for more thought than `high`. They reach the models that take them: both on every later Claude model, `max` on Kimi K3 (where `xhigh` rounds up to it), and `xhigh` on GPT-5.2 and later and on grok-4.7. Every other model gets its highest setting instead, and the request's span carries `llm.reasoning.weakened`; the gateway gets `high` for any id. `npm run doctor -- --matrix` lists each path. Both cost more: `max` is Kimi K3's costliest effort.
|
|
37
|
+
- DeepSeek thinks by default: `reasoning: none` turns thinking off, and the other levels go as DeepSeek's low, high or max (`xhigh` rounds up to max). While it thinks, DeepSeek ignores `temperature`. Structured output on DeepSeek is JSON mode (the engine asks for the schema in the system message and validates the answer), and `web_search` is dropped with a warning.
|
|
37
38
|
- A `budget_tokens` value goes as written to Gemini and to Claude 4.6 or older, and as the smallest level that covers it everywhere else, later Claude models included. The effort word is always sent too, because the gateway can serve any cloud id; a direct provider ignores the field it does not read. Changing the `model:` line keeps the setting. Unset, a later Claude model keeps its own default: it thinks, except Opus 4.7 and 4.8, which think only when asked.
|
|
38
39
|
- Four provider limits: on Claude 4.6 or older, the adapter raises a budget under 1024 to 1024, the least Anthropic takes; on every Claude model, the Anthropic SDK refuses a non-streaming request whose output ceiling passes about 21000 tokens, and the adapter sets that ceiling to at least the thinking budget plus 2048, so a `budget_tokens` above about 19000 fails a non-streaming turn, while the levels stay under it; on Claude Opus 5 and 5.5, Fable and Mythos, `none` is adaptive thinking at `low` effort, so a little thinking remains, because Opus 5.5, Fable and Mythos cannot turn thinking off and Opus 5's off switch can write a tool call as text that never runs; Gemini 2.5 Pro rejects a budget of 0, so `none` fails on it.
|
|
39
40
|
- `sampling:` on any agent: `temperature` (where the provider still exposes it; never sent to Claude), `top_p`, `stop`, `max_output_tokens`; each adapter sends the fields its provider takes. Thinking tokens count against `max_output_tokens`, so a reasoning agent with long output needs room; the Claude adapter raises its own ceiling to the thinking budget plus 2048 tokens, on every Claude model. A level counts as its budget (2048, 8192 or 16384), so `high`, or a later Claude model that thinks by default, gets at least 18432.
|
|
40
41
|
- `tool_choice:` says which tools the model may call: `auto` (the default), `none`, `required` (some tool), or `{ name: lookup }` (that one tool). It reads the same on every provider; where a provider rejects forcing a tool, the adapter sends a weaker choice and the span carries `llm.tool_choice.weakened`.
|
|
41
|
-
- `model_overrides:` gives one provider its own prompt nuance, keyed `gemini`, `anthropic`, `openai`, `xai`, `moonshot` or `ollama`; each entry holds `instruction` (replaces the agent's instruction on that provider) or `instruction_append` (added after it). The entry for the provider of the agent's `model:` applies, and a `fallback_model` gets the same instruction.
|
|
42
|
+
- `model_overrides:` gives one provider its own prompt nuance, keyed `gemini`, `anthropic`, `openai`, `xai`, `moonshot`, `deepseek` or `ollama`; each entry holds `instruction` (replaces the agent's instruction on that provider) or `instruction_append` (added after it). The entry for the provider of the agent's `model:` applies, and a `fallback_model` gets the same instruction.
|
|
42
43
|
- `generateContentConfig:` is the deprecated, Gemini-shaped spelling of `sampling:`, `tool_choice:`, `output.mime` and `reasoning:` (as `thinkingConfig` and `reasoningEffort`). It still loads and behaves the same, with one deprecation line per file, but a `thinkingLevel` written for Gemini does nothing on Claude. An agent that sets a v2 key beside its v1 spelling fails to load. `npx melchizedek-codemod <file|dir>` rewrites a file to the v2 keys. Only `toolConfig.functionCallingConfig.mode: VALIDATED` and Gemini's dynamic budget (`thinkingBudget: -1`) still need `generateContentConfig:`; the codemod drops `includeThoughts`. `topK`, `seed`, the penalties, `candidateCount`, `safetySettings` and `includeThoughts` reach no provider; the Gemini adapter asks for the thought trace itself whenever `reasoning:` is not `none`.
|
|
43
44
|
- The framework's own pattern: data-gathering subagents on a lite model with `reasoning: none` and a tight output cap; synthesis on the stronger model with `reasoning: low` or higher and room to reason. The templates in `config/agents/templates/` follow it.
|
|
44
45
|
Mixing providers in one syndicate:
|
|
45
46
|
- Any agent in a graph can run on a different provider; `model_zoo.yaml` declares one lightweight agent per provider, and in a clone `npm run demo:models` sends one prompt through each.
|
|
46
47
|
- In code, `logProviderStatuses()` from `melchizedek-agents` reports which providers have a key (and, given a log function, logs a line per provider; it registers nothing); `resolveAdapter(modelId)` from `melchizedek-agents/model` returns the provider's adapter for any of these ids; in a clone `npm run demo:direct -- --model ollama/qwen3:8b hello` makes one model call with no YAML.
|
|
47
48
|
Reading the errors:
|
|
48
|
-
- `<KEY> is not set in environment.` for a `claude-*`, `gpt-*`, `grok-*` or `
|
|
49
|
+
- `<KEY> is not set in environment.` for a `claude-*`, `gpt-*`, `grok-*`, `kimi-*` or `deepseek-*` id: the provider's key is unset. Set it, or set the gateway pair.
|
|
49
50
|
- `GATEWAY_HTTP_ERROR ... 404/400`: the gateway rejected the mapped id; fix the name with `MODEL_GATEWAY_MODEL_MAP`.
|
|
50
51
|
- `GATEWAY_KEY_MISSING`: `MODEL_GATEWAY` is set without `MODEL_GATEWAY_API_KEY`.
|
|
51
52
|
- `OLLAMA_UNREACHABLE`: Ollama is not running (`ollama serve`) or the model is not pulled (`ollama list`).
|
|
52
53
|
|
|
53
|
-
IDENTIFIERS (verbatim): model:, claude-*, gpt-*, grok-*, kimi-*, ollama/, gemini-3.8-flash, gemini-3.5-flash-lite, gemini-2.5-flash, kimi-k3, ollama/qwen3:8b, GOOGLE_GENAI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, MOONSHOT_API_KEY, npx melchizedek-doctor, --json, --check, # tier:, ollama pull qwen3:8b, http://localhost:11434/v1, MODEL_GATEWAY, MODEL_GATEWAY_API_KEY, MODEL_GATEWAY_MODEL_MAP, MODEL_GATEWAY_BASE_URL, X-API-Key, reasoning:, budget_tokens, sampling:, max_output_tokens, model_overrides:, instruction_append, generateContentConfig:, thinkingConfig, reasoningEffort, thinkingLevel, includeThoughts, toolConfig, npx melchizedek-codemod, config/agents/templates/, model_zoo.yaml, npm run demo:models, logProviderStatuses, GATEWAY_HTTP_ERROR, GATEWAY_KEY_MISSING, OLLAMA_UNREACHABLE
|
|
54
|
+
IDENTIFIERS (verbatim): model:, claude-*, gpt-*, grok-*, kimi-*, deepseek-*, ollama/, gemini-3.8-flash, gemini-3.5-flash-lite, gemini-2.5-flash, kimi-k3, ollama/qwen3:8b, GOOGLE_GENAI_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, XAI_API_KEY, MOONSHOT_API_KEY, DEEPSEEK_API_KEY, deepseek-flash, deepseek-v4-pro, npx melchizedek-doctor, --json, --check, # tier:, ollama pull qwen3:8b, http://localhost:11434/v1, MODEL_GATEWAY, MODEL_GATEWAY_API_KEY, MODEL_GATEWAY_MODEL_MAP, MODEL_GATEWAY_BASE_URL, X-API-Key, reasoning:, budget_tokens, sampling:, max_output_tokens, model_overrides:, instruction_append, generateContentConfig:, thinkingConfig, reasoningEffort, thinkingLevel, includeThoughts, toolConfig, npx melchizedek-codemod, config/agents/templates/, model_zoo.yaml, npm run demo:models, logProviderStatuses, GATEWAY_HTTP_ERROR, GATEWAY_KEY_MISSING, OLLAMA_UNREACHABLE
|
|
54
55
|
LIMITS: the routing prefixes and what `reasoning:` becomes per provider may each be a short table. Body under 160 lines.
|