melchizedek-agents 0.16.2 → 0.18.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 +40 -3
- package/AGENT_SETUP.md +4 -2
- package/CHANGELOG.md +246 -0
- package/DOCUMENTATION.md +311 -15
- package/QUICKSTART.md +2 -2
- package/README.md +125 -149
- package/config/agents/examples/harness.yaml +135 -0
- package/config/agents/examples/model_zoo.yaml +23 -9
- package/config/agents/examples/pipeline.yaml +120 -0
- package/config/agents/examples/specs/open-meteo-forecast.json +77 -0
- package/config/agents/examples/specs/open-meteo-geocoding.json +53 -0
- package/config/agents/examples/weather.yaml +59 -0
- package/config/agents/syndicateSchema.yaml +125 -1
- package/db/hardening.sql +4 -3
- package/db/migrations/0011_erase_expired.sql +198 -0
- package/db/migrations/0012_audit_log.sql +98 -0
- package/dist/lib/a2a/app.d.ts +27 -0
- package/dist/lib/a2a/app.js +101 -5
- package/dist/lib/a2a/executor.d.ts +34 -4
- package/dist/lib/a2a/executor.js +109 -9
- package/dist/lib/compile.d.ts +41 -3
- package/dist/lib/compile.js +162 -17
- package/dist/lib/config.d.ts +24 -0
- package/dist/lib/config.js +24 -0
- package/dist/lib/doctor.js +17 -4
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +1 -0
- package/dist/lib/loadEnv.d.ts +5 -0
- package/dist/lib/loadEnv.js +7 -0
- package/dist/lib/loadSyndicate.d.ts +50 -0
- package/dist/lib/loadSyndicate.js +19 -3
- package/dist/lib/memory/erase.d.ts +5 -0
- package/dist/lib/memory/erase.js +5 -0
- package/dist/lib/memory/providers.d.ts +27 -0
- package/dist/lib/memory/providers.js +28 -0
- package/dist/lib/models/capabilities.js +10 -0
- package/dist/lib/models/endpoints.d.ts +1 -1
- package/dist/lib/models/endpoints.js +4 -1
- package/dist/lib/models/fallback.d.ts +41 -0
- package/dist/lib/models/fallback.js +105 -0
- package/dist/lib/models/gateway.d.ts +2 -2
- package/dist/lib/models/gateway.js +4 -4
- package/dist/lib/models/kimiLlm.d.ts +123 -0
- package/dist/lib/models/kimiLlm.js +171 -0
- package/dist/lib/models/providerMap.d.ts +2 -2
- package/dist/lib/models/providerMap.js +4 -1
- package/dist/lib/models/registry.d.ts +1 -1
- package/dist/lib/models/registry.js +11 -3
- package/dist/lib/net/redirects.d.ts +14 -0
- package/dist/lib/net/redirects.js +94 -0
- package/dist/lib/observability/audit.d.ts +21 -0
- package/dist/lib/observability/audit.js +39 -0
- package/dist/lib/observability/metrics.d.ts +6 -0
- package/dist/lib/observability/tracer.d.ts +14 -1
- package/dist/lib/observability/tracer.js +16 -2
- package/dist/lib/persistence/supabaseProvider.d.ts +2 -6
- package/dist/lib/persistence/supabaseProvider.js +2 -31
- package/dist/lib/runtime/questions.d.ts +55 -0
- package/dist/lib/runtime/questions.js +107 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +45 -5
- package/dist/lib/runtime/syndicateTurn.js +165 -17
- package/dist/lib/storage/postgres/index.d.ts +9 -0
- package/dist/lib/storage/postgres/index.js +13 -0
- package/dist/lib/storage/rlsStatus.d.ts +45 -0
- package/dist/lib/storage/rlsStatus.js +69 -0
- package/dist/lib/syndicateSchema.d.ts +177 -0
- package/dist/lib/syndicateSchema.js +343 -3
- package/dist/lib/toolRegistry.js +9 -0
- package/dist/lib/tools/mcpToolFactory.d.ts +26 -0
- package/dist/lib/tools/mcpToolFactory.js +61 -4
- package/dist/lib/tools/openapiTools.d.ts +52 -0
- package/dist/lib/tools/openapiTools.js +225 -0
- package/dist/lib/tools/skillToolset.d.ts +135 -0
- package/dist/lib/tools/skillToolset.js +247 -0
- package/dist/lib/tools/urlContextTool.d.ts +29 -0
- package/dist/lib/tools/urlContextTool.js +39 -0
- package/dist/lib/workflow.d.ts +72 -0
- package/dist/lib/workflow.js +180 -0
- package/dist/lib/workflowConfig.d.ts +95 -0
- package/dist/lib/workflowConfig.js +93 -0
- package/dist/scripts/a2a_server.js +51 -8
- package/dist/scripts/db.d.ts +9 -0
- package/dist/scripts/db.js +30 -15
- package/dist/scripts/syndicate_chat.js +39 -5
- package/package.json +6 -3
- package/skills/briefs/_shared.md +1 -1
- package/skills/briefs/melchizedek-author.md +8 -4
- package/skills/briefs/melchizedek-memory.md +1 -1
- package/skills/briefs/melchizedek-models.md +1 -1
- package/skills/briefs/melchizedek-scribe.md +4 -4
- package/skills/briefs/melchizedek-serve.md +1 -1
- package/skills/briefs/melchizedek.md +4 -1
- package/skills/melchizedek/SKILL.md +4 -1
- package/skills/melchizedek-author/SKILL.md +7 -3
- package/skills/melchizedek-memory/SKILL.md +1 -1
- package/skills/melchizedek-models/SKILL.md +1 -1
- package/skills/melchizedek-scribe/SKILL.md +3 -3
- package/skills/melchizedek-serve/SKILL.md +1 -1
- /package/skills/melchizedek-author/{templates → assets}/minimal.yaml +0 -0
- /package/skills/melchizedek-scribe/{templates → assets}/brief.md +0 -0
package/dist/lib/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { loadSyndicate, loadSyndicateFromRegistry, parseCliBindings, validateReg
|
|
|
17
17
|
export { runSyndicateTurn, ingestTurnMemory } from './runtime/syndicateTurn.js';
|
|
18
18
|
export { approvalResponsePart, pendingApproval, APPROVAL_REQUEST } from './runtime/approvals.js';
|
|
19
19
|
export { compileGraph, compileSubagent } from './compile.js';
|
|
20
|
+
export { compileWorkflow, isWorkflowSyndicate, describeInput } from './workflow.js';
|
|
20
21
|
// ── Serve it over A2A, or call a remote A2A agent ───────────────────────────
|
|
21
22
|
export { createA2AApp, compileAgentCard, currentRequestContext } from './a2a/app.js';
|
|
22
23
|
export { callerTokens, firstOf, hashCallerToken, jwtIdentity, parseCallers, scopeSegment, sharedSecret, trustedHeader, } from './a2a/identity.js';
|
package/dist/lib/loadEnv.d.ts
CHANGED
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
* as a credential. Before this, `cp .env.example .env` crashed the default
|
|
20
20
|
* quickstart (`Invalid supabaseUrl`), made the doctor report every provider
|
|
21
21
|
* funded, and turned the example bearer secret into a live one.
|
|
22
|
+
*
|
|
23
|
+
* `MELCHIZEDEK_DOTENV=off` in the environment skips every file: the process
|
|
24
|
+
* runs on its ambient environment alone. A container whose secrets arrive as
|
|
25
|
+
* real variables wants that, and so does a test that starts a bin from a
|
|
26
|
+
* clone holding a real `.env`, which must never reach live services.
|
|
22
27
|
*/
|
|
23
28
|
/** True for template placeholders that must never act as real values. */
|
|
24
29
|
export declare function isPlaceholderValue(value: string | undefined): boolean;
|
package/dist/lib/loadEnv.js
CHANGED
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
* as a credential. Before this, `cp .env.example .env` crashed the default
|
|
20
20
|
* quickstart (`Invalid supabaseUrl`), made the doctor report every provider
|
|
21
21
|
* funded, and turned the example bearer secret into a live one.
|
|
22
|
+
*
|
|
23
|
+
* `MELCHIZEDEK_DOTENV=off` in the environment skips every file: the process
|
|
24
|
+
* runs on its ambient environment alone. A container whose secrets arrive as
|
|
25
|
+
* real variables wants that, and so does a test that starts a bin from a
|
|
26
|
+
* clone holding a real `.env`, which must never reach live services.
|
|
22
27
|
*/
|
|
23
28
|
import { existsSync, readFileSync } from 'node:fs';
|
|
24
29
|
import { dirname, join, resolve } from 'node:path';
|
|
@@ -69,6 +74,8 @@ export function parseEnvFile(raw) {
|
|
|
69
74
|
* clone run from another directory still finds its own `.env`.
|
|
70
75
|
*/
|
|
71
76
|
export function loadEnv(moduleUrl) {
|
|
77
|
+
if (process.env.MELCHIZEDEK_DOTENV?.trim().toLowerCase() === 'off')
|
|
78
|
+
return;
|
|
72
79
|
const files = [join(process.cwd(), '.env')];
|
|
73
80
|
if (moduleUrl) {
|
|
74
81
|
const own = join(findRepoRoot(dirname(fileURLToPath(moduleUrl))), '.env');
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { DispatchConfig } from './dispatch.ts';
|
|
2
2
|
export type { DispatchConfig } from './dispatch.ts';
|
|
3
|
+
import type { SkillsConfig } from './tools/skillToolset.ts';
|
|
4
|
+
import type { OpenApiConfig } from './tools/openapiTools.ts';
|
|
5
|
+
import type { ContextConfig, ExampleConfig } from './compile.ts';
|
|
6
|
+
export type { OpenApiConfig } from './tools/openapiTools.ts';
|
|
7
|
+
export type { SkillsConfig } from './tools/skillToolset.ts';
|
|
8
|
+
import type { WorkflowConfig } from './workflowConfig.ts';
|
|
9
|
+
export type { WorkflowConfig } from './workflowConfig.ts';
|
|
3
10
|
export interface ThinkingConfig {
|
|
4
11
|
/** Token budget for internal reasoning. 0 = off, -1 = dynamic. */
|
|
5
12
|
thinkingBudget?: number;
|
|
@@ -81,6 +88,30 @@ export interface AgentYamlConfig {
|
|
|
81
88
|
*/
|
|
82
89
|
outputSchema?: Record<string, unknown>;
|
|
83
90
|
mcp_server_url?: string;
|
|
91
|
+
/** The MCP server's tools this agent may use; the rest are not exposed. */
|
|
92
|
+
mcp_tools?: string[];
|
|
93
|
+
/** Answers when `model` fails provider-side, or its provider's circuit is open (ADR 0044). */
|
|
94
|
+
fallback_model?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Agent Skills (SKILL.md directories) this agent holds the way a coding
|
|
97
|
+
* harness does: the index in its instruction, a skill read on demand, its
|
|
98
|
+
* scripts run only after approval. lib/tools/skillToolset.ts.
|
|
99
|
+
*/
|
|
100
|
+
skills?: SkillsConfig;
|
|
101
|
+
/**
|
|
102
|
+
* HTTP APIs this agent may call, each from an OpenAPI 3 spec file: one tool
|
|
103
|
+
* per operation, the GET operations unless `operations` names others, auth
|
|
104
|
+
* from environment variables. lib/tools/openapiTools.ts.
|
|
105
|
+
*/
|
|
106
|
+
openapi?: OpenApiConfig[];
|
|
107
|
+
/** `gemini`: the model's Python runs in Gemini's server-side sandbox (ADR 0033). Gemini models only. */
|
|
108
|
+
code_execution?: 'gemini';
|
|
109
|
+
/** Compact a long conversation into a summary past a token threshold (ADR 0033). Delegate orchestrator only. */
|
|
110
|
+
context?: ContextConfig;
|
|
111
|
+
/** `task`: the agent works until it calls finish_task; its arguments are the node's output. Workflow nodes only. */
|
|
112
|
+
mode?: 'task';
|
|
113
|
+
/** Few-shot exchanges added to every request's instruction (ADK's ExampleTool). */
|
|
114
|
+
examples?: ExampleConfig[];
|
|
84
115
|
orchestration?: {
|
|
85
116
|
role?: 'primary' | 'sub-agent';
|
|
86
117
|
delegates?: string[];
|
|
@@ -121,6 +152,24 @@ export interface SyndicateYamlConfig {
|
|
|
121
152
|
* See lib/dispatch.ts for the full contract.
|
|
122
153
|
*/
|
|
123
154
|
dispatch?: DispatchConfig;
|
|
155
|
+
/**
|
|
156
|
+
* Self-correction (ADK's reflect-and-retry plugins), on by default:
|
|
157
|
+
* `model_errors` retries a model reply ADK marks malformed (default 2),
|
|
158
|
+
* `tool_errors` caps a tool's retries after it throws, with structured
|
|
159
|
+
* guidance to the model (default 3). 0 turns either off. ADR 0034.
|
|
160
|
+
*/
|
|
161
|
+
retries?: {
|
|
162
|
+
model_errors?: number;
|
|
163
|
+
tool_errors?: number;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Opts this syndicate into a WORKFLOW: a graph whose nodes are its agents
|
|
167
|
+
* (plus join, map, tool and ask_user nodes) and whose edges say what runs
|
|
168
|
+
* after what and on which route. The orchestrator is a node like any
|
|
169
|
+
* other; nothing delegates. Cannot be combined with `dispatch`.
|
|
170
|
+
* Contract: lib/workflow.ts.
|
|
171
|
+
*/
|
|
172
|
+
workflow?: WorkflowConfig;
|
|
124
173
|
/** Optional default variable definitions for {{token}} interpolation. */
|
|
125
174
|
variables?: VariableMap;
|
|
126
175
|
/** Defines the persistence and semantic memory layer for the syndicate. */
|
|
@@ -237,6 +286,7 @@ export declare function loadSyndicate(filename: string, options?: LoadSyndicateO
|
|
|
237
286
|
export declare function readSyndicateFile(filename: string, options?: Pick<LoadSyndicateOptions, 'agentsDir' | 'shippedFallback'>): {
|
|
238
287
|
raw: unknown;
|
|
239
288
|
label: string;
|
|
289
|
+
filePath: string;
|
|
240
290
|
};
|
|
241
291
|
/**
|
|
242
292
|
* Every nested `yaml_reference:` a syndicate reaches, read raw from the agents
|
|
@@ -228,8 +228,24 @@ export function collectGuards(config, loadNested = nestedLoader(config), seen =
|
|
|
228
228
|
}
|
|
229
229
|
export function loadSyndicate(filename, options = {}) {
|
|
230
230
|
const { bindings = {}, overrides } = options;
|
|
231
|
-
const { raw, label } = readSyndicateFile(filename, options);
|
|
232
|
-
return resolveAndValidate(raw, label, { bindings, overrides });
|
|
231
|
+
const { raw, label, filePath } = readSyndicateFile(filename, options);
|
|
232
|
+
return anchorSpecPaths(resolveAndValidate(raw, label, { bindings, overrides }), path.dirname(filePath));
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* An `openapi:` spec path is written relative to the syndicate file, so a
|
|
236
|
+
* file copied with its specs keeps working from any working directory. A
|
|
237
|
+
* definition from the registry has no file and resolves against the cwd.
|
|
238
|
+
*/
|
|
239
|
+
function anchorSpecPaths(config, dir) {
|
|
240
|
+
const anchor = (agent) => {
|
|
241
|
+
for (const entry of agent?.openapi ?? [])
|
|
242
|
+
if (!path.isAbsolute(entry.spec))
|
|
243
|
+
entry.spec = path.resolve(dir, entry.spec);
|
|
244
|
+
};
|
|
245
|
+
anchor(config.orchestrator);
|
|
246
|
+
for (const sub of config.subagents ?? [])
|
|
247
|
+
anchor(sub);
|
|
248
|
+
return config;
|
|
233
249
|
}
|
|
234
250
|
/**
|
|
235
251
|
* Read one syndicate file, unparsed values and all, from the agents root (the
|
|
@@ -266,7 +282,7 @@ export function readSyndicateFile(filename, options = {}) {
|
|
|
266
282
|
// is under it), so a multi-file nested load says WHICH file is wrong.
|
|
267
283
|
const rel = path.relative(process.cwd(), filePath);
|
|
268
284
|
const label = rel && !rel.startsWith('..') && !path.isAbsolute(rel) ? rel : filePath;
|
|
269
|
-
return { raw: parse(fileContent), label };
|
|
285
|
+
return { raw: parse(fileContent), label, filePath };
|
|
270
286
|
}
|
|
271
287
|
/**
|
|
272
288
|
* Every nested `yaml_reference:` a syndicate reaches, read raw from the agents
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
* markers, and (without a namespace) its task tools' list and jobs
|
|
9
9
|
* (migration 0010), in one transaction. Returns what each store lost.
|
|
10
10
|
*
|
|
11
|
+
* With a namespace, ledger turns and A2A tasks are kept only when their
|
|
12
|
+
* conversation is still live in another namespace (migration 0011): they do
|
|
13
|
+
* not record the namespace, and a conversation whose session expired must
|
|
14
|
+
* not survive an erase.
|
|
15
|
+
*
|
|
11
16
|
* Throws on any failure: a deletion request that silently did nothing is
|
|
12
17
|
* worse than an error the caller can surface and retry.
|
|
13
18
|
*/
|
package/dist/lib/memory/erase.js
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
* markers, and (without a namespace) its task tools' list and jobs
|
|
9
9
|
* (migration 0010), in one transaction. Returns what each store lost.
|
|
10
10
|
*
|
|
11
|
+
* With a namespace, ledger turns and A2A tasks are kept only when their
|
|
12
|
+
* conversation is still live in another namespace (migration 0011): they do
|
|
13
|
+
* not record the namespace, and a conversation whose session expired must
|
|
14
|
+
* not survive an erase.
|
|
15
|
+
*
|
|
11
16
|
* Throws on any failure: a deletion request that silently did nothing is
|
|
12
17
|
* worse than an error the caller can surface and retry.
|
|
13
18
|
*/
|
|
@@ -97,3 +97,30 @@ export interface MemoryProviders {
|
|
|
97
97
|
* model or embedder is Gemini.
|
|
98
98
|
*/
|
|
99
99
|
export declare function memoryProvidersFromEnv(env?: NodeJS.ProcessEnv, geminiKey?: string): MemoryProviders;
|
|
100
|
+
/**
|
|
101
|
+
* Where long-term memory sends a syndicate's transcripts: the provider that
|
|
102
|
+
* extracts facts and the one that embeds them. By default both are Gemini,
|
|
103
|
+
* whatever the agents run on, so a deployment approved for one provider can
|
|
104
|
+
* send transcripts to another without noticing; the server warns when they
|
|
105
|
+
* differ (memoryCrossesProviders).
|
|
106
|
+
*/
|
|
107
|
+
export declare function memoryDestinations(env?: NodeJS.ProcessEnv, override?: {
|
|
108
|
+
extractor?: {
|
|
109
|
+
model: string;
|
|
110
|
+
};
|
|
111
|
+
embedder?: {
|
|
112
|
+
provider: string;
|
|
113
|
+
};
|
|
114
|
+
}): {
|
|
115
|
+
extraction: string;
|
|
116
|
+
embeddings: string;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* The memory destinations a syndicate's own agents do not already use, or []
|
|
120
|
+
* when memory stays with the agents' providers. An embedding endpoint the
|
|
121
|
+
* operator configured themselves (`openai-compatible`) is not counted.
|
|
122
|
+
*/
|
|
123
|
+
export declare function memoryCrossesProviders(models: Array<string | undefined>, dest: {
|
|
124
|
+
extraction: string;
|
|
125
|
+
embeddings: string;
|
|
126
|
+
}): string[];
|
|
@@ -238,3 +238,31 @@ export function memoryProvidersFromEnv(env = process.env, geminiKey) {
|
|
|
238
238
|
}
|
|
239
239
|
return { extractor, embedder };
|
|
240
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Where long-term memory sends a syndicate's transcripts: the provider that
|
|
243
|
+
* extracts facts and the one that embeds them. By default both are Gemini,
|
|
244
|
+
* whatever the agents run on, so a deployment approved for one provider can
|
|
245
|
+
* send transcripts to another without noticing; the server warns when they
|
|
246
|
+
* differ (memoryCrossesProviders).
|
|
247
|
+
*/
|
|
248
|
+
export function memoryDestinations(env = process.env, override = {}) {
|
|
249
|
+
const model = override.extractor?.model ?? (env.MEMORY_EXTRACTION_MODEL?.trim() || MEMORY_EXTRACTION_MODEL);
|
|
250
|
+
return {
|
|
251
|
+
extraction: providerForModel(model),
|
|
252
|
+
embeddings: (override.embedder?.provider ?? (env.MEMORY_EMBEDDING_PROVIDER?.trim() || 'gemini')).toLowerCase(),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* The memory destinations a syndicate's own agents do not already use, or []
|
|
257
|
+
* when memory stays with the agents' providers. An embedding endpoint the
|
|
258
|
+
* operator configured themselves (`openai-compatible`) is not counted.
|
|
259
|
+
*/
|
|
260
|
+
export function memoryCrossesProviders(models, dest) {
|
|
261
|
+
const agents = new Set(models.filter((m) => !!m).map((m) => providerForModel(m)));
|
|
262
|
+
const out = new Set();
|
|
263
|
+
if (!agents.has(dest.extraction))
|
|
264
|
+
out.add(dest.extraction);
|
|
265
|
+
if (dest.embeddings !== 'openai-compatible' && !agents.has(dest.embeddings))
|
|
266
|
+
out.add(dest.embeddings);
|
|
267
|
+
return [...out];
|
|
268
|
+
}
|
|
@@ -37,6 +37,7 @@ function directPlatform(provider) {
|
|
|
37
37
|
export const SERVER_SIDE_TOOLS = {
|
|
38
38
|
web_search: ['gemini', 'anthropic', 'openai', 'xai'],
|
|
39
39
|
google_search: ['gemini'],
|
|
40
|
+
url_context: ['gemini'],
|
|
40
41
|
x_search: ['xai'],
|
|
41
42
|
collections_search: ['xai'],
|
|
42
43
|
};
|
|
@@ -171,6 +172,15 @@ export const CAPABILITY_MATRIX = {
|
|
|
171
172
|
vision: ok('test', 'user-turn images only'),
|
|
172
173
|
native_search: nativeSearch('xai'),
|
|
173
174
|
},
|
|
175
|
+
moonshot: {
|
|
176
|
+
delegation: ok(),
|
|
177
|
+
memory_tools: ok(),
|
|
178
|
+
structured_output: ok('test', 'strict json_schema; kimi-k2.6 is documented as unstable on complex schemas ($ref, oneOf)'),
|
|
179
|
+
thinking_with_tools: degraded('reasoning_content is not replayed across tool calls, which Moonshot asks for on kimi-k3, so the model re-reasons each step; effort travels as reasoning_effort (K3) or a thinking switch (K2.x)'),
|
|
180
|
+
streaming: ok(),
|
|
181
|
+
vision: ok('test', 'user-turn images only, sent as base64 (Moonshot takes no public image URLs)'),
|
|
182
|
+
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)"),
|
|
183
|
+
},
|
|
174
184
|
ollama: {
|
|
175
185
|
delegation: ok(),
|
|
176
186
|
memory_tools: ok(),
|
|
@@ -11,7 +11,7 @@
|
|
|
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, ollama
|
|
14
|
+
* xai, moonshot, ollama direct only (moonshot: MOONSHOT_BASE_URL for a proxy)
|
|
15
15
|
*
|
|
16
16
|
* Configuration comes from the environment (`endpointFromEnv`) or, per
|
|
17
17
|
* request, from the server's `credentials` plug point, which may return an
|
|
@@ -11,7 +11,7 @@
|
|
|
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, ollama
|
|
14
|
+
* xai, moonshot, ollama direct only (moonshot: MOONSHOT_BASE_URL for a proxy)
|
|
15
15
|
*
|
|
16
16
|
* Configuration comes from the environment (`endpointFromEnv`) or, per
|
|
17
17
|
* request, from the server's `credentials` plug point, which may return an
|
|
@@ -35,6 +35,7 @@ export const PLATFORMS_FOR = {
|
|
|
35
35
|
anthropic: ['direct', 'bedrock', 'vertex'],
|
|
36
36
|
openai: ['direct', 'azure'],
|
|
37
37
|
xai: ['direct'],
|
|
38
|
+
moonshot: ['direct'],
|
|
38
39
|
ollama: ['direct'],
|
|
39
40
|
};
|
|
40
41
|
const PLATFORM_ENV = {
|
|
@@ -99,6 +100,8 @@ export function endpointFromEnv(provider, env = process.env) {
|
|
|
99
100
|
return { ...base, ...(trimmed(env.ANTHROPIC_BASE_URL) ? { baseURL: trimmed(env.ANTHROPIC_BASE_URL) } : {}) };
|
|
100
101
|
case 'openai/direct':
|
|
101
102
|
return { ...base, ...(trimmed(env.OPENAI_BASE_URL) ? { baseURL: trimmed(env.OPENAI_BASE_URL) } : {}) };
|
|
103
|
+
case 'moonshot/direct':
|
|
104
|
+
return { ...base, ...(trimmed(env.MOONSHOT_BASE_URL) ? { baseURL: trimmed(env.MOONSHOT_BASE_URL) } : {}) };
|
|
102
105
|
case 'openai/azure': {
|
|
103
106
|
const endpoint = trimmed(env.AZURE_OPENAI_ENDPOINT);
|
|
104
107
|
const key = trimmed(env.AZURE_OPENAI_API_KEY);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/models/fallback.ts — a fallback model and a per-provider circuit
|
|
3
|
+
* breaker for an agent that declares `fallback_model:` (ADR 0044).
|
|
4
|
+
*
|
|
5
|
+
* WHY: lib/models/retry.ts makes one call survive a blip. It does nothing for
|
|
6
|
+
* an outage: every turn on a provider that is down spends its retries and
|
|
7
|
+
* fails. An agent that names a fallback model now answers from it instead,
|
|
8
|
+
* and a provider that keeps failing is skipped for a cooldown rather than
|
|
9
|
+
* tried on every turn.
|
|
10
|
+
*
|
|
11
|
+
* THE RULES:
|
|
12
|
+
* - Only provider-side failures count: what retry.ts classifies retryable
|
|
13
|
+
* (408/409/425/429/5xx, connection resets) once the adapter's own retries
|
|
14
|
+
* are spent. A 4xx is the request's fault and is thrown as it is; a
|
|
15
|
+
* canceled turn is never counted or redirected.
|
|
16
|
+
* - The fallback runs only if the primary produced nothing yet: a stream
|
|
17
|
+
* that failed midway is thrown, never replayed on another model.
|
|
18
|
+
* - The breaker is per provider, shared by every agent in the process:
|
|
19
|
+
* MODEL_BREAKER_THRESHOLD consecutive failures (default 5; 0 disables)
|
|
20
|
+
* open it for MODEL_BREAKER_COOLDOWN_MS (default 30 s). While open, a
|
|
21
|
+
* wrapped agent goes straight to its fallback; after the cooldown one
|
|
22
|
+
* call is let through, and a success closes the circuit.
|
|
23
|
+
*/
|
|
24
|
+
import { BaseLlm } from '@google/adk';
|
|
25
|
+
import type { BaseLlmConnection, LlmRequest, LlmResponse } from '@google/adk';
|
|
26
|
+
/** True while `provider`'s circuit is open (calls skip it). */
|
|
27
|
+
export declare function circuitOpen(provider: string, now?: number): boolean;
|
|
28
|
+
/** For tests: close every circuit. */
|
|
29
|
+
export declare function resetCircuits(): void;
|
|
30
|
+
/** A provider-side failure: worth counting against the provider and worth a fallback. */
|
|
31
|
+
export declare function isProviderFailure(err: unknown): boolean;
|
|
32
|
+
export declare class FallbackLlm extends BaseLlm {
|
|
33
|
+
readonly primary: BaseLlm;
|
|
34
|
+
readonly fallback: BaseLlm;
|
|
35
|
+
private readonly log;
|
|
36
|
+
private readonly primaryProvider;
|
|
37
|
+
constructor(primary: BaseLlm, fallback: BaseLlm, log?: (m: string) => void);
|
|
38
|
+
generateContentAsync(llmRequest: LlmRequest, stream?: boolean, abortSignal?: AbortSignal): AsyncGenerator<LlmResponse, void>;
|
|
39
|
+
private onFallback;
|
|
40
|
+
connect(llmRequest: LlmRequest): Promise<BaseLlmConnection>;
|
|
41
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/models/fallback.ts — a fallback model and a per-provider circuit
|
|
3
|
+
* breaker for an agent that declares `fallback_model:` (ADR 0044).
|
|
4
|
+
*
|
|
5
|
+
* WHY: lib/models/retry.ts makes one call survive a blip. It does nothing for
|
|
6
|
+
* an outage: every turn on a provider that is down spends its retries and
|
|
7
|
+
* fails. An agent that names a fallback model now answers from it instead,
|
|
8
|
+
* and a provider that keeps failing is skipped for a cooldown rather than
|
|
9
|
+
* tried on every turn.
|
|
10
|
+
*
|
|
11
|
+
* THE RULES:
|
|
12
|
+
* - Only provider-side failures count: what retry.ts classifies retryable
|
|
13
|
+
* (408/409/425/429/5xx, connection resets) once the adapter's own retries
|
|
14
|
+
* are spent. A 4xx is the request's fault and is thrown as it is; a
|
|
15
|
+
* canceled turn is never counted or redirected.
|
|
16
|
+
* - The fallback runs only if the primary produced nothing yet: a stream
|
|
17
|
+
* that failed midway is thrown, never replayed on another model.
|
|
18
|
+
* - The breaker is per provider, shared by every agent in the process:
|
|
19
|
+
* MODEL_BREAKER_THRESHOLD consecutive failures (default 5; 0 disables)
|
|
20
|
+
* open it for MODEL_BREAKER_COOLDOWN_MS (default 30 s). While open, a
|
|
21
|
+
* wrapped agent goes straight to its fallback; after the cooldown one
|
|
22
|
+
* call is let through, and a success closes the circuit.
|
|
23
|
+
*/
|
|
24
|
+
import { BaseLlm } from '@google/adk';
|
|
25
|
+
import { classifyError } from './retry.js';
|
|
26
|
+
import { providerForModel } from './providerMap.js';
|
|
27
|
+
const circuits = new Map();
|
|
28
|
+
function breakerSettings(env = process.env) {
|
|
29
|
+
const n = (name, fallback) => {
|
|
30
|
+
const v = Number(env[name]);
|
|
31
|
+
return Number.isFinite(v) && v >= 0 ? Math.floor(v) : fallback;
|
|
32
|
+
};
|
|
33
|
+
return { threshold: n('MODEL_BREAKER_THRESHOLD', 5), cooldownMs: n('MODEL_BREAKER_COOLDOWN_MS', 30_000) };
|
|
34
|
+
}
|
|
35
|
+
/** True while `provider`'s circuit is open (calls skip it). */
|
|
36
|
+
export function circuitOpen(provider, now = Date.now()) {
|
|
37
|
+
const c = circuits.get(provider);
|
|
38
|
+
return !!c && c.openUntil > now;
|
|
39
|
+
}
|
|
40
|
+
function recordFailure(provider, now = Date.now()) {
|
|
41
|
+
const { threshold, cooldownMs } = breakerSettings();
|
|
42
|
+
if (threshold === 0)
|
|
43
|
+
return;
|
|
44
|
+
const c = circuits.get(provider) ?? { failures: 0, openUntil: 0 };
|
|
45
|
+
c.failures += 1;
|
|
46
|
+
if (c.failures >= threshold)
|
|
47
|
+
c.openUntil = now + cooldownMs;
|
|
48
|
+
circuits.set(provider, c);
|
|
49
|
+
}
|
|
50
|
+
function recordSuccess(provider) {
|
|
51
|
+
circuits.delete(provider);
|
|
52
|
+
}
|
|
53
|
+
/** For tests: close every circuit. */
|
|
54
|
+
export function resetCircuits() {
|
|
55
|
+
circuits.clear();
|
|
56
|
+
}
|
|
57
|
+
/** A provider-side failure: worth counting against the provider and worth a fallback. */
|
|
58
|
+
export function isProviderFailure(err) {
|
|
59
|
+
if (err?.name === 'AbortError')
|
|
60
|
+
return false;
|
|
61
|
+
return classifyError(err).retryable;
|
|
62
|
+
}
|
|
63
|
+
export class FallbackLlm extends BaseLlm {
|
|
64
|
+
primary;
|
|
65
|
+
fallback;
|
|
66
|
+
log;
|
|
67
|
+
primaryProvider;
|
|
68
|
+
constructor(primary, fallback, log = (m) => console.warn(m)) {
|
|
69
|
+
super({ model: primary.model });
|
|
70
|
+
this.primary = primary;
|
|
71
|
+
this.fallback = fallback;
|
|
72
|
+
this.log = log;
|
|
73
|
+
this.primaryProvider = providerForModel(primary.model);
|
|
74
|
+
}
|
|
75
|
+
async *generateContentAsync(llmRequest, stream, abortSignal) {
|
|
76
|
+
if (circuitOpen(this.primaryProvider)) {
|
|
77
|
+
yield* this.onFallback(llmRequest, stream, abortSignal, `${this.primaryProvider} circuit is open`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
let yielded = false;
|
|
81
|
+
try {
|
|
82
|
+
for await (const r of this.primary.generateContentAsync(llmRequest, stream, abortSignal)) {
|
|
83
|
+
yielded = true;
|
|
84
|
+
yield r;
|
|
85
|
+
}
|
|
86
|
+
recordSuccess(this.primaryProvider);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (abortSignal?.aborted || !isProviderFailure(err))
|
|
90
|
+
throw err;
|
|
91
|
+
recordFailure(this.primaryProvider);
|
|
92
|
+
if (yielded)
|
|
93
|
+
throw err;
|
|
94
|
+
yield* this.onFallback(llmRequest, stream, abortSignal, `${this.primary.model} failed (${err instanceof Error ? err.message.slice(0, 120) : String(err)})`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async *onFallback(llmRequest, stream, abortSignal, why) {
|
|
98
|
+
this.log(`[models] ${why}; answering from ${this.fallback.model}.`);
|
|
99
|
+
const request = { ...llmRequest, model: this.fallback.model };
|
|
100
|
+
yield* this.fallback.generateContentAsync(request, stream, abortSignal);
|
|
101
|
+
}
|
|
102
|
+
connect(llmRequest) {
|
|
103
|
+
return this.primary.connect(llmRequest);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -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 and
|
|
7
|
+
* OpenRouter) fronts OpenAI, Anthropic, Google, xAI and Moonshot 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
|
*
|
|
@@ -80,7 +80,7 @@ export declare function gatewayUsable(): boolean;
|
|
|
80
80
|
* Gateways prefix every id with the upstream provider's slug and spell
|
|
81
81
|
* Anthropic's version segment with a dot ("claude-sonnet-4-6" is
|
|
82
82
|
* "anthropic/claude-sonnet-4.6" on both Vercel and OpenRouter). Gemini,
|
|
83
|
-
* GPT and
|
|
83
|
+
* GPT, Grok and Kimi ids already carry dots, so only the prefix is added. An id
|
|
84
84
|
* the mapper gets wrong is fixed in MODEL_GATEWAY_MODEL_MAP, not in code.
|
|
85
85
|
*/
|
|
86
86
|
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 and
|
|
7
|
+
* OpenRouter) fronts OpenAI, Anthropic, Google, xAI and Moonshot 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
|
*
|
|
@@ -49,14 +49,14 @@ export const GATEWAYS = {
|
|
|
49
49
|
label: 'Vercel AI Gateway',
|
|
50
50
|
baseUrl: 'https://ai-gateway.vercel.sh/v1',
|
|
51
51
|
consoleUrl: 'https://vercel.com/ai-gateway',
|
|
52
|
-
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'xai' },
|
|
52
|
+
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'xai', moonshot: 'moonshotai' },
|
|
53
53
|
},
|
|
54
54
|
openrouter: {
|
|
55
55
|
id: 'openrouter',
|
|
56
56
|
label: 'OpenRouter',
|
|
57
57
|
baseUrl: 'https://openrouter.ai/api/v1',
|
|
58
58
|
consoleUrl: 'https://openrouter.ai/keys',
|
|
59
|
-
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'x-ai' },
|
|
59
|
+
slugs: { gemini: 'google', anthropic: 'anthropic', openai: 'openai', xai: 'x-ai', moonshot: 'moonshotai' },
|
|
60
60
|
},
|
|
61
61
|
};
|
|
62
62
|
/**
|
|
@@ -119,7 +119,7 @@ function modelMapFromEnv() {
|
|
|
119
119
|
* Gateways prefix every id with the upstream provider's slug and spell
|
|
120
120
|
* Anthropic's version segment with a dot ("claude-sonnet-4-6" is
|
|
121
121
|
* "anthropic/claude-sonnet-4.6" on both Vercel and OpenRouter). Gemini,
|
|
122
|
-
* GPT and
|
|
122
|
+
* GPT, Grok and Kimi ids already carry dots, so only the prefix is added. An id
|
|
123
123
|
* the mapper gets wrong is fixed in MODEL_GATEWAY_MODEL_MAP, not in code.
|
|
124
124
|
*/
|
|
125
125
|
export function gatewayWireModel(model, gateway) {
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/models/kimiLlm.ts — Moonshot AI (Kimi) provider for the ADK LLMRegistry.
|
|
3
|
+
*
|
|
4
|
+
* WHY this file exists:
|
|
5
|
+
* Model optionality is a primary driver of this framework: the agent YAML
|
|
6
|
+
* declares `model`, and the registry routes it to the right provider.
|
|
7
|
+
* Kimi K3 (July 2026) is the strongest open-weight model on the public
|
|
8
|
+
* coding and research boards, and Moonshot serves it first-party with
|
|
9
|
+
* tool calling, strict structured output and vision, so it earns a
|
|
10
|
+
* direct adapter rather than only the gateway path.
|
|
11
|
+
*
|
|
12
|
+
* WHY it subclasses the chat-completions base (lib/models/openAiCompatibleLlm.ts):
|
|
13
|
+
* Moonshot's API is OpenAI Chat Completions at https://api.moonshot.ai/v1
|
|
14
|
+
* (it also serves Responses on the same base and Anthropic Messages at
|
|
15
|
+
* /anthropic; neither adds a capability the chat dialect lacks here).
|
|
16
|
+
* The base provides tool calling, strict `json_schema` structured output
|
|
17
|
+
* (Moonshot documents `strict: true`), SSE streaming with usage, image
|
|
18
|
+
* parts as base64 data URIs (Moonshot rejects public image URLs, which is
|
|
19
|
+
* what the base never sends), `reasoning_content` surfaced as THINKING
|
|
20
|
+
* output, retries, and the llm.request span.
|
|
21
|
+
*
|
|
22
|
+
* HOW TO ENABLE:
|
|
23
|
+
* 1. Create a key at https://platform.moonshot.ai (international; the
|
|
24
|
+
* .cn console serves mainland China) and put it in .env as
|
|
25
|
+
* MOONSHOT_API_KEY.
|
|
26
|
+
* 2. Set model: "kimi-k3" (or any kimi-* id) in your YAML.
|
|
27
|
+
* registerAvailableProviders() registers this adapter when the key is set.
|
|
28
|
+
* Optional: MOONSHOT_BASE_URL for a proxy that speaks the same dialect.
|
|
29
|
+
*
|
|
30
|
+
* THE FAMILY (platform.moonshot.ai › Model list, October 2026; USD per 1M tokens):
|
|
31
|
+
*
|
|
32
|
+
* model context in / cache hit / out use it for
|
|
33
|
+
* ──────────────────────────────────────────────────────────────────────
|
|
34
|
+
* kimi-k3 1M $3.00 / $0.30 / $15 DEFAULT — the flagship: 2.8T MoE, vision,
|
|
35
|
+
* always-on thinking, effort low|high|max
|
|
36
|
+
* kimi-k2.6 256K $0.95 / $0.16 / $4 the cheaper general tier: vision, thinking
|
|
37
|
+
* switchable; fragile on complex schemas
|
|
38
|
+
* kimi-k2.7-code 256K $0.95 / $0.19 / $4 coding; thinking cannot be turned off
|
|
39
|
+
* kimi-k2.7-code-highspeed 256K $1.90 / $0.38 / $8 the same model at ~180 tokens/s
|
|
40
|
+
*
|
|
41
|
+
* CHOOSING A MODEL (cost is a design property; see the model-routing skill):
|
|
42
|
+
* K3 is priced like a mid-tier closed model, not like an open-weight
|
|
43
|
+
* bargain: $3 / $15 is Claude Sonnet 4.6's price, and Claude Sonnet 5.5
|
|
44
|
+
* ($2 / $10, cache reads $0.20) undercuts it on every token while sitting
|
|
45
|
+
* above it on the coding boards. Two things make K3 dearer than its list
|
|
46
|
+
* price: thinking is always on and billed as output, and Moonshot's
|
|
47
|
+
* default effort is max — hence the pinned DEFAULT_KIMI_REASONING_EFFORT
|
|
48
|
+
* and the zoo agent at "low". What K3 buys is the strongest open-weight
|
|
49
|
+
* model (a self-host option) and a 1M context at a flat rate. For cost,
|
|
50
|
+
* reach for kimi-k2.6 (a quarter of K3's price, vision and tools) on
|
|
51
|
+
* subagent and bulk work, or serve K3 through the gateway: OpenRouter's
|
|
52
|
+
* endpoints range from about $0.88 to $3.45 per 1M input, the cheap ones
|
|
53
|
+
* quantized, with fidelity as the trade.
|
|
54
|
+
*
|
|
55
|
+
* There is no small or "flash" K3; K2.6 is the light tier. K3's weights
|
|
56
|
+
* are open (moonshotai/Kimi-K3 on Hugging Face), so the id is also served
|
|
57
|
+
* by OpenRouter, Together, Fireworks, Vercel AI Gateway and others; with
|
|
58
|
+
* no direct key, MODEL_GATEWAY serves it as `moonshotai/kimi-k3`
|
|
59
|
+
* (lib/models/gateway.ts). kimi-k2.5 and the moonshot-v1 ids were retired
|
|
60
|
+
* on 2026-08-31 and are not routed here on purpose.
|
|
61
|
+
*
|
|
62
|
+
* REASONING:
|
|
63
|
+
* K3 always thinks; the lever is the top-level `reasoning_effort`
|
|
64
|
+
* (low | high | max, Moonshot's default max). Requests pin
|
|
65
|
+
* DEFAULT_KIMI_REASONING_EFFORT (lib/config.ts) unless the agent sets
|
|
66
|
+
* generateContentConfig.reasoningEffort; "none" is mapped to "low" on
|
|
67
|
+
* K3, which cannot switch thinking off. K2.x models take no
|
|
68
|
+
* reasoning_effort: a `thinking: { type }` switch instead, so on those
|
|
69
|
+
* ids reasoningEffort "none" or thinkingBudget 0 sends `disabled` and
|
|
70
|
+
* any effort value is dropped from the body (K2.7 Code rejects
|
|
71
|
+
* disabled thinking; Moonshot's own error says so).
|
|
72
|
+
*
|
|
73
|
+
* LIMITATIONS:
|
|
74
|
+
* - web_search: Moonshot's model-side `$web_search` built-in retires on
|
|
75
|
+
* 2026-10-20 and its replacement is a separate REST API
|
|
76
|
+
* (POST /v1/tools/search, billed per call), not a tool the model
|
|
77
|
+
* enables in the request. The sentinel is therefore dropped with a
|
|
78
|
+
* warning, like on Ollama; `web_extract` works, and `MOONSHOT_API_KEY`
|
|
79
|
+
* would fund a client-side search tool over that REST API if one is
|
|
80
|
+
* added (lib/tools/).
|
|
81
|
+
* - Thinking with tools: Moonshot asks that K3's `reasoning_content` be
|
|
82
|
+
* passed back with the assistant message on a tool loop. This base
|
|
83
|
+
* keeps scratchpads out of history, so K3 re-reasons each step
|
|
84
|
+
* (degraded, stated in lib/models/capabilities.ts); if Moonshot ever
|
|
85
|
+
* enforces it, the turn ends with MOONSHOT_HTTP_ERROR naming the field.
|
|
86
|
+
* - Every cell of its capability row is asserted against the request body
|
|
87
|
+
* the adapter sends (tests/capabilityMatrix.test.ts), as for every other
|
|
88
|
+
* provider. Verified live on 2026-10-03 through `npm run demo:models`:
|
|
89
|
+
* kimi-k3 at low effort answered the zoo's explainer prompt in 17.9 s,
|
|
90
|
+
* 440 tokens in, 486 out, 262 of them thinking, the scratchpad surfaced
|
|
91
|
+
* from reasoning_content; tool loops and search were not exercised.
|
|
92
|
+
*/
|
|
93
|
+
import type { LlmRequest, LlmResponse } from '@google/adk';
|
|
94
|
+
import { OpenAiCompatibleLlm } from './openAiCompatibleLlm.ts';
|
|
95
|
+
export declare const MOONSHOT_BASE_URL = "https://api.moonshot.ai/v1";
|
|
96
|
+
/** The flagship takes `reasoning_effort`; the K2 generation takes a `thinking` switch. */
|
|
97
|
+
export declare function isKimiK3(model: string): boolean;
|
|
98
|
+
export declare class KimiLlm extends OpenAiCompatibleLlm {
|
|
99
|
+
/** Any model: "kimi-*" in a YAML config routes here after registration. */
|
|
100
|
+
static readonly supportedModels: Array<string | RegExp>;
|
|
101
|
+
private readonly apiKey?;
|
|
102
|
+
private readonly baseUrl;
|
|
103
|
+
constructor({ model, apiKey, baseUrl }: {
|
|
104
|
+
model: string;
|
|
105
|
+
apiKey?: string;
|
|
106
|
+
baseUrl?: string;
|
|
107
|
+
});
|
|
108
|
+
protected providerId(): string;
|
|
109
|
+
protected endpointUrl(): string;
|
|
110
|
+
private key;
|
|
111
|
+
protected headers(): Record<string, string>;
|
|
112
|
+
protected missingRequirement(): LlmResponse | undefined;
|
|
113
|
+
protected httpError(status: number, detail: string): LlmResponse;
|
|
114
|
+
/**
|
|
115
|
+
* The reasoning controls, per generation (see the header). The base has
|
|
116
|
+
* already put `reasoning_effort` in the body when the agent set one;
|
|
117
|
+
* these fields are merged after it, so an `undefined` here removes it
|
|
118
|
+
* (JSON.stringify drops undefined keys).
|
|
119
|
+
*/
|
|
120
|
+
protected extraBodyFields(llmRequest: LlmRequest): Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
/** Registers KimiLlm with the ADK LLMRegistry (called when MOONSHOT_API_KEY is set). */
|
|
123
|
+
export declare function registerKimiLlm(): void;
|