melchizedek-agents 0.9.6 → 0.16.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 +272 -0
- package/AGENT_SETUP.md +39 -23
- package/CHANGELOG.md +623 -0
- package/DOCUMENTATION.md +429 -109
- package/QUICKSTART.md +92 -18
- package/README.md +45 -5
- package/config/agents/README.md +46 -1
- package/config/agents/examples/ares.yaml +117 -40
- package/config/agents/examples/assistant.yaml +159 -0
- package/config/agents/examples/augustin.yaml +154 -51
- package/config/agents/examples/cartographers.yaml +91 -23
- package/config/agents/examples/claude.yaml +45 -6
- package/config/agents/examples/council.yaml +23 -8
- package/config/agents/examples/critic.yaml +57 -21
- package/config/agents/examples/delegation.yaml +76 -9
- package/config/agents/examples/hierarchical.yaml +55 -8
- package/config/agents/examples/image_production.yaml +54 -16
- package/config/agents/examples/librarian.yaml +70 -14
- package/config/agents/examples/model_zoo.yaml +105 -29
- package/config/agents/examples/patient_advocate.yaml +102 -34
- package/config/agents/examples/research.yaml +250 -0
- package/config/agents/examples/scribe.yaml +242 -0
- package/config/agents/examples/scriptorium.yaml +87 -24
- package/config/agents/examples/style_council.yaml +31 -9
- package/config/agents/examples/syndicate.yaml +29 -10
- package/config/agents/examples/tutor.yaml +10 -3
- package/config/agents/syndicateSchema.yaml +44 -9
- package/config/agents/templates/README.md +58 -0
- package/config/agents/templates/account_memory.yaml +153 -0
- package/config/agents/templates/case_desk.yaml +188 -0
- package/config/agents/templates/conversational.yaml +110 -0
- package/config/agents/templates/draft_review.yaml +130 -0
- package/config/agents/templates/intake_extractor.yaml +152 -0
- package/config/agents/templates/research_brief.yaml +169 -0
- package/config/agents/templates/research_desk.yaml +152 -0
- package/config/agents/templates/review_panel.yaml +188 -0
- package/config/agents/templates/support_triage.yaml +273 -0
- package/config/agents/templates/systems_operator.yaml +157 -0
- package/db/hardening.sql +202 -0
- package/db/memory_v2.sql +77 -0
- package/db/migrations/0001_base.sql +167 -0
- package/db/migrations/0002_erase_scope.sql +172 -0
- package/db/migrations/0003_postgres_storage.sql +58 -0
- package/db/migrations/0004_usage.sql +94 -0
- package/db/migrations/0005_agent_registry.sql +200 -0
- package/db/migrations/0006_task_leases.sql +25 -0
- package/db/migrations/0007_memory_commit.sql +134 -0
- package/db/migrations/0008_memory_retention.sql +37 -0
- package/db/migrations/0009_task_queue.sql +51 -0
- package/db/migrations/0010_erase_complete.sql +180 -0
- package/db/telemetry.sql +401 -0
- package/demo/README.md +77 -0
- package/demo/a2a_demo.mjs +95 -0
- package/dist/lib/a2a/app.d.ts +253 -0
- package/dist/lib/a2a/app.js +751 -0
- package/dist/lib/a2a/executor.d.ts +167 -0
- package/dist/lib/a2a/executor.js +548 -0
- package/dist/lib/a2a/identity.d.ts +122 -0
- package/dist/lib/a2a/identity.js +254 -0
- package/dist/lib/a2a/policy.d.ts +110 -0
- package/dist/lib/a2a/policy.js +178 -0
- package/dist/lib/a2a/redisLimits.d.ts +26 -0
- package/dist/lib/a2a/redisLimits.js +31 -0
- package/dist/lib/a2a/remoteAgent.d.ts +91 -0
- package/dist/lib/a2a/remoteAgent.js +268 -0
- package/dist/lib/a2a/turnLock.d.ts +28 -0
- package/dist/lib/a2a/turnLock.js +76 -0
- package/dist/lib/compile.d.ts +11 -1
- package/dist/lib/compile.js +109 -8
- package/dist/lib/config.d.ts +7 -7
- package/dist/lib/config.js +7 -7
- package/dist/lib/doctor.d.ts +145 -0
- package/dist/lib/doctor.js +517 -0
- package/dist/lib/grounding.d.ts +2 -2
- package/dist/lib/grounding.js +2 -2
- package/dist/lib/guards/index.d.ts +37 -0
- package/dist/lib/guards/index.js +66 -0
- package/dist/lib/guards/science.d.ts +117 -0
- package/dist/lib/guards/science.js +444 -0
- package/dist/lib/index.d.ts +37 -2
- package/dist/lib/index.js +35 -4
- package/dist/lib/loadEnv.d.ts +23 -11
- package/dist/lib/loadEnv.js +77 -29
- package/dist/lib/loadSyndicate.d.ts +97 -5
- package/dist/lib/loadSyndicate.js +176 -76
- package/dist/lib/memory/erase.d.ts +24 -0
- package/dist/lib/memory/erase.js +47 -0
- package/dist/lib/memory/namespace.d.ts +37 -0
- package/dist/lib/memory/namespace.js +74 -0
- package/dist/lib/memory/providers.d.ts +99 -0
- package/dist/lib/memory/providers.js +240 -0
- package/dist/lib/memory/store.d.ts +74 -0
- package/dist/lib/memory/store.js +108 -0
- package/dist/lib/memory/supabaseMemoryService.d.ts +69 -9
- package/dist/lib/memory/supabaseMemoryService.js +239 -125
- package/dist/lib/models/capabilities.d.ts +103 -0
- package/dist/lib/models/capabilities.js +291 -0
- package/dist/lib/models/claudeLlm.d.ts +10 -1
- package/dist/lib/models/claudeLlm.js +57 -27
- package/dist/lib/models/endpoints.d.ts +100 -0
- package/dist/lib/models/endpoints.js +282 -0
- package/dist/lib/models/gateway.d.ts +107 -0
- package/dist/lib/models/gateway.js +157 -0
- package/dist/lib/models/gatewayLlm.d.ts +57 -0
- package/dist/lib/models/gatewayLlm.js +105 -0
- package/dist/lib/models/gptLlm.d.ts +51 -2
- package/dist/lib/models/gptLlm.js +169 -26
- package/dist/lib/models/grokLlm.d.ts +19 -10
- package/dist/lib/models/grokLlm.js +29 -14
- package/dist/lib/models/ollamaLlm.d.ts +26 -0
- package/dist/lib/models/ollamaLlm.js +43 -1
- package/dist/lib/models/openAiCompatibleLlm.d.ts +67 -2
- package/dist/lib/models/openAiCompatibleLlm.js +200 -37
- package/dist/lib/models/providerMap.js +9 -2
- package/dist/lib/models/registry.d.ts +42 -5
- package/dist/lib/models/registry.js +140 -29
- package/dist/lib/models/retry.d.ts +146 -0
- package/dist/lib/models/retry.js +305 -0
- package/dist/lib/models/schemaNormalize.d.ts +20 -0
- package/dist/lib/models/schemaNormalize.js +43 -0
- package/dist/lib/net/addressGuard.d.ts +60 -0
- package/dist/lib/net/addressGuard.js +216 -0
- package/dist/lib/observability/embeddings.d.ts +11 -2
- package/dist/lib/observability/embeddings.js +18 -14
- package/dist/lib/observability/metrics.d.ts +36 -0
- package/dist/lib/observability/metrics.js +86 -0
- package/dist/lib/observability/redact.d.ts +29 -0
- package/dist/lib/observability/redact.js +102 -0
- package/dist/lib/observability/supabaseSpanExporter.d.ts +2 -0
- package/dist/lib/observability/supabaseSpanExporter.js +22 -6
- package/dist/lib/observability/tracer.d.ts +10 -0
- package/dist/lib/observability/tracer.js +70 -3
- package/dist/lib/persistence/supabaseProvider.d.ts +11 -0
- package/dist/lib/persistence/supabaseProvider.js +32 -6
- package/dist/lib/registry.d.ts +87 -0
- package/dist/lib/registry.js +146 -0
- package/dist/lib/runtime/approvals.d.ts +44 -0
- package/dist/lib/runtime/approvals.js +67 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +233 -0
- package/dist/lib/runtime/syndicateTurn.js +597 -0
- package/dist/lib/runtime/turnControl.d.ts +90 -0
- package/dist/lib/runtime/turnControl.js +131 -0
- package/dist/lib/session/supabaseSessionService.js +1 -1
- package/dist/lib/session/transcript.d.ts +9 -1
- package/dist/lib/session/transcript.js +15 -7
- package/dist/lib/skills.d.ts +50 -0
- package/dist/lib/skills.js +138 -0
- package/dist/lib/storage/postgres/index.d.ts +98 -0
- package/dist/lib/storage/postgres/index.js +146 -0
- package/dist/lib/storage/postgres/memoryStore.d.ts +11 -0
- package/dist/lib/storage/postgres/memoryStore.js +82 -0
- package/dist/lib/storage/postgres/sessionService.d.ts +51 -0
- package/dist/lib/storage/postgres/sessionService.js +225 -0
- package/dist/lib/storage/postgres/taskQueue.d.ts +22 -0
- package/dist/lib/storage/postgres/taskQueue.js +101 -0
- package/dist/lib/storage/postgres/taskStore.d.ts +55 -0
- package/dist/lib/storage/postgres/taskStore.js +168 -0
- package/dist/lib/storage/schema.d.ts +27 -0
- package/dist/lib/storage/schema.js +50 -0
- package/dist/lib/storage/schemaVersion.d.ts +25 -0
- package/dist/lib/storage/schemaVersion.js +46 -0
- package/dist/lib/syndicateSchema.d.ts +275 -0
- package/dist/lib/syndicateSchema.js +534 -0
- package/dist/lib/toolRegistry.d.ts +15 -0
- package/dist/lib/toolRegistry.js +59 -10
- package/dist/lib/tools/collectionsSearchTool.js +1 -1
- package/dist/lib/tools/mcpServe.d.ts +45 -0
- package/dist/lib/tools/mcpServe.js +88 -0
- package/dist/lib/tools/mcpToolFactory.d.ts +2 -0
- package/dist/lib/tools/mcpToolFactory.js +41 -21
- package/dist/lib/tools/science/corrections.d.ts +42 -0
- package/dist/lib/tools/science/corrections.js +115 -0
- package/dist/lib/tools/science/europepmc.d.ts +51 -0
- package/dist/lib/tools/science/europepmc.js +127 -0
- package/dist/lib/tools/science/http.d.ts +31 -0
- package/dist/lib/tools/science/http.js +104 -0
- package/dist/lib/tools/science/openalex.d.ts +30 -0
- package/dist/lib/tools/science/openalex.js +81 -0
- package/dist/lib/tools/science/trials.d.ts +58 -0
- package/dist/lib/tools/science/trials.js +203 -0
- package/dist/lib/tools/science/types.d.ts +69 -0
- package/dist/lib/tools/science/types.js +48 -0
- package/dist/lib/tools/scienceTools.d.ts +100 -0
- package/dist/lib/tools/scienceTools.js +277 -0
- package/dist/lib/tools/taskTools.d.ts +204 -0
- package/dist/lib/tools/taskTools.js +374 -0
- package/dist/lib/tools/toolContract.d.ts +17 -4
- package/dist/lib/tools/toolContract.js +13 -5
- package/dist/lib/tools/webExtractTool.d.ts +13 -10
- package/dist/lib/tools/webExtractTool.js +23 -55
- package/dist/lib/tools/webSearchTool.d.ts +2 -1
- package/dist/lib/tools/webSearchTool.js +4 -3
- package/dist/lib/tools/wikiTools.d.ts +2 -2
- package/dist/lib/tools/wikiTools.js +12 -12
- package/dist/lib/tools/xApiSearchTool.d.ts +194 -0
- package/dist/lib/tools/xApiSearchTool.js +583 -0
- package/dist/lib/tools/xSearchTool.js +1 -1
- package/dist/lib/wiki/agentRun.js +1 -1
- package/dist/lib/wiki/builder.js +21 -6
- package/dist/lib/wiki/dive.js +2 -2
- package/dist/lib/wiki/entities.d.ts +2 -2
- package/dist/lib/wiki/entities.js +7 -7
- package/dist/lib/wiki/extract.js +5 -1
- package/dist/lib/wiki/fill.js +5 -5
- package/dist/lib/wiki/format.d.ts +7 -5
- package/dist/lib/wiki/format.js +2 -2
- package/dist/lib/wiki/graph.js +2 -2
- package/dist/lib/wiki/lint.js +2 -2
- package/dist/lib/wiki/markdown.d.ts +1 -1
- package/dist/lib/wiki/markdown.js +6 -1
- package/dist/lib/wiki/pipeline.js +2 -2
- package/dist/lib/wiki/vault.js +2 -2
- package/dist/scripts/a2a_server.d.ts +17 -11
- package/dist/scripts/a2a_server.js +405 -1028
- package/dist/scripts/assistant_worker.d.ts +33 -0
- package/dist/scripts/assistant_worker.js +193 -0
- package/dist/scripts/db.d.ts +26 -0
- package/dist/scripts/db.js +179 -0
- package/dist/scripts/doctor.d.ts +23 -0
- package/dist/scripts/doctor.js +90 -0
- package/dist/scripts/init.d.ts +43 -0
- package/dist/scripts/init.js +152 -0
- package/dist/scripts/registry.d.ts +25 -0
- package/dist/scripts/registry.js +182 -0
- package/dist/scripts/skills_install.d.ts +7 -0
- package/dist/scripts/skills_install.js +137 -0
- package/dist/scripts/syndicate_chat.js +159 -276
- package/package.json +90 -16
- package/skills/README.md +74 -0
- package/skills/briefs/README.md +16 -0
- package/skills/briefs/_shared.md +16 -0
- package/skills/briefs/melchizedek-author.md +60 -0
- package/skills/briefs/melchizedek-memory.md +47 -0
- package/skills/briefs/melchizedek-models.md +47 -0
- package/skills/briefs/melchizedek-scribe.md +37 -0
- package/skills/briefs/melchizedek-serve.md +56 -0
- package/skills/briefs/melchizedek.md +69 -0
- package/skills/briefs/skills-readme.md +33 -0
- package/skills/melchizedek/SKILL.md +159 -0
- package/skills/melchizedek-author/SKILL.md +99 -0
- package/skills/melchizedek-author/templates/minimal.yaml +60 -0
- package/skills/melchizedek-memory/SKILL.md +138 -0
- package/skills/melchizedek-models/SKILL.md +137 -0
- package/skills/melchizedek-scribe/SKILL.md +72 -0
- package/skills/melchizedek-scribe/templates/brief.md +27 -0
- package/skills/melchizedek-serve/SKILL.md +119 -0
|
@@ -23,45 +23,119 @@
|
|
|
23
23
|
* resolution (LRU), so late registration can be masked by stale cache hits.
|
|
24
24
|
*/
|
|
25
25
|
import { Gemini, LLMRegistry } from '@google/adk';
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
26
|
+
import { endpointFromEnv, endpointProblems, mergeEndpoint, platformModel, providerReady } from './endpoints.js';
|
|
27
|
+
import { currentTurnSignal } from '../runtime/turnControl.js';
|
|
28
|
+
import { errorStatus, retryUntilFirstYield } from './retry.js';
|
|
29
|
+
import { DEFAULT_CLAUDE_MODEL, DEFAULT_GEMINI_MODEL, DEFAULT_GPT_MODEL, DEFAULT_GROK_MODEL, DEFAULT_OLLAMA_MODEL, } from '../config.js';
|
|
30
|
+
import { traceLlmGeneration, setLlmSpanAttribute, } from '../observability/tracer.js';
|
|
31
|
+
import { ClaudeLlm, registerClaudeLlm } from './claudeLlm.js';
|
|
32
|
+
import { GptLlm, registerGptLlm } from './gptLlm.js';
|
|
33
|
+
import { GrokLlm, registerGrokLlm } from './grokLlm.js';
|
|
34
|
+
import { OllamaLlm, registerOllamaLlm } from './ollamaLlm.js';
|
|
35
|
+
import { GatewayLlm, registerGatewayLlm } from './gatewayLlm.js';
|
|
36
|
+
import { gatewayConfig, gatewayProblem, gatewayUsable, planTransport, } from './gateway.js';
|
|
37
|
+
import { PROVIDERS, providerForModel, providerKeyPresent, } from './providerMap.js';
|
|
33
38
|
export { providerForModel, providerKeyPresent, PROVIDERS };
|
|
39
|
+
export { endpointFromEnv, endpointLabel, endpointProblems, PLATFORMS_FOR, platformFromEnv, providerReady, } from './endpoints.js';
|
|
40
|
+
export { gatewayConfig, gatewayProblem, gatewayUsable, planTransport, GATEWAYS } from './gateway.js';
|
|
41
|
+
export { describeCapabilities, capabilitySummary } from './capabilities.js';
|
|
42
|
+
export { GatewayLlm };
|
|
34
43
|
// ── TracedGemini ─────────────────────────────────────────────────────────────
|
|
35
44
|
// ADK's built-in Gemini adapter, wrapped so Gemini calls emit the same
|
|
36
|
-
// per-request llm.request spans (tokens, latency) as every other provider
|
|
45
|
+
// per-request llm.request spans (tokens, latency) as every other provider,
|
|
46
|
+
// and get the shared transient-failure retries (lib/models/retry.ts).
|
|
37
47
|
export class TracedGemini extends Gemini {
|
|
38
48
|
// CRITICAL: reuse Gemini's exact regex instances. The LLMRegistry dict is
|
|
39
49
|
// keyed by the regex OBJECT — registering the same instances REPLACES the
|
|
40
50
|
// built-in Gemini entries instead of adding shadowed duplicates.
|
|
41
51
|
static supportedModels = Gemini.supportedModels;
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
/**
|
|
53
|
+
* The Gemini platform (ADR 0023) applies to every construction, the
|
|
54
|
+
* LLMRegistry's included: on Vertex AI the client authenticates with
|
|
55
|
+
* Google Application Default Credentials for the configured project and
|
|
56
|
+
* location, and an AI Studio key (the environment's or a caller's) is not
|
|
57
|
+
* sent. `endpoint` overrides the environment's (the credentials plug point).
|
|
58
|
+
*/
|
|
59
|
+
constructor(params = {}) {
|
|
60
|
+
const { endpoint: given, ...rest } = params;
|
|
61
|
+
const e = given ?? endpointFromEnv('gemini');
|
|
62
|
+
const model = rest.model ? platformModel(e, rest.model) : rest.model;
|
|
63
|
+
super(e.platform === 'vertex'
|
|
64
|
+
? { ...rest, model, apiKey: e.apiKey, vertexai: true, project: e.project, location: e.location }
|
|
65
|
+
: { ...rest, model, ...(e.apiKey && !rest.apiKey ? { apiKey: e.apiKey } : {}) });
|
|
66
|
+
}
|
|
67
|
+
async *generateContentAsync(llmRequest, stream, abortSignal) {
|
|
68
|
+
yield* traceLlmGeneration({ provider: 'gemini', model: this.model, llmRequest }, this.tagAndGenerate(llmRequest, stream, abortSignal ?? currentTurnSignal()));
|
|
44
69
|
}
|
|
45
70
|
/** Tags the llm.request span (web_search = Gemini grounding), then
|
|
46
|
-
* delegates to ADK's Gemini. Runs inside the span context.
|
|
47
|
-
|
|
71
|
+
* delegates to ADK's Gemini. Runs inside the span context. The abort
|
|
72
|
+
* signal is forwarded so a canceled turn stops the request in flight
|
|
73
|
+
* (ADK's Gemini puts it on the genai request config). */
|
|
74
|
+
async *tagAndGenerate(llmRequest, stream, abortSignal) {
|
|
48
75
|
const hasGrounding = (llmRequest.config?.tools ?? []).some((t) => t && (t.googleSearch || t.googleSearchRetrieval));
|
|
49
76
|
if (hasGrounding)
|
|
50
77
|
setLlmSpanAttribute('llm.web_search.native', true);
|
|
51
|
-
|
|
78
|
+
// Retries live here, not in genai's own httpOptions.retryOptions: that
|
|
79
|
+
// hook (p-retry 4, client-level only) ignores the abort signal — it
|
|
80
|
+
// sleeps and re-sends after a canceled turn — honours no Retry-After,
|
|
81
|
+
// does not retry Node's "fetch failed" resets, and replaces a 4xx's
|
|
82
|
+
// error body (e.g. "API key not valid") with a bare statusText. Wrapping
|
|
83
|
+
// the call keeps genai's ApiError intact and applies the same policy as
|
|
84
|
+
// every other adapter. A retry is only made while nothing has been
|
|
85
|
+
// yielded, so a stream that fails mid-reply is surfaced, not replayed.
|
|
86
|
+
// ADK's request preprocessing is idempotent, so re-sending the same
|
|
87
|
+
// llmRequest is safe. Both the registry class and resolveModel()'s
|
|
88
|
+
// per-request instances are TracedGemini, so both get this.
|
|
89
|
+
try {
|
|
90
|
+
yield* retryUntilFirstYield(() => super.generateContentAsync(llmRequest, stream, abortSignal), {
|
|
91
|
+
signal: abortSignal,
|
|
92
|
+
onRetry: ({ retries }) => setLlmSpanAttribute('llm.retries', retries),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
// genai's ApiError carries the status; put it where the ledger reads.
|
|
97
|
+
const status = errorStatus(err);
|
|
98
|
+
if (status !== undefined)
|
|
99
|
+
setLlmSpanAttribute('llm.http_status', status);
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
52
102
|
}
|
|
53
103
|
}
|
|
54
104
|
/** Availability report without side effects (used by demos and key gates). */
|
|
55
105
|
export function providerStatuses() {
|
|
106
|
+
const gw = gatewayUsable() ? gatewayConfig() : null;
|
|
56
107
|
return Object.keys(PROVIDERS).map((provider) => {
|
|
57
|
-
const
|
|
108
|
+
const label = PROVIDERS[provider].label;
|
|
109
|
+
if (providerReady(provider)) {
|
|
110
|
+
return { provider, label, available: true, transport: 'direct' };
|
|
111
|
+
}
|
|
112
|
+
// A cloud platform that is not fully configured is not covered by the
|
|
113
|
+
// gateway: the deployment chose that platform on purpose.
|
|
114
|
+
let cloudProblem;
|
|
115
|
+
try {
|
|
116
|
+
const e = endpointFromEnv(provider);
|
|
117
|
+
if (e.platform !== 'direct')
|
|
118
|
+
cloudProblem = endpointProblems(provider, e).join('; ');
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
cloudProblem = err instanceof Error ? err.message : String(err);
|
|
122
|
+
}
|
|
123
|
+
if (cloudProblem)
|
|
124
|
+
return { provider, label, available: false, reason: cloudProblem };
|
|
125
|
+
if (gw && provider !== 'ollama') {
|
|
126
|
+
return {
|
|
127
|
+
provider,
|
|
128
|
+
label,
|
|
129
|
+
available: true,
|
|
130
|
+
transport: 'gateway',
|
|
131
|
+
gateway: gw.gateway.id,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
58
134
|
return {
|
|
59
135
|
provider,
|
|
60
|
-
label
|
|
61
|
-
available,
|
|
62
|
-
|
|
63
|
-
? {}
|
|
64
|
-
: { reason: `${PROVIDERS[provider].keyEnv} not set` }),
|
|
136
|
+
label,
|
|
137
|
+
available: false,
|
|
138
|
+
reason: `${PROVIDERS[provider].keyEnv} not set`,
|
|
65
139
|
};
|
|
66
140
|
});
|
|
67
141
|
}
|
|
@@ -72,29 +146,57 @@ const REGISTRARS = {
|
|
|
72
146
|
xai: registerGrokLlm,
|
|
73
147
|
ollama: registerOllamaLlm,
|
|
74
148
|
};
|
|
149
|
+
/**
|
|
150
|
+
* The direct adapter's OWN supportedModels per provider. A gateway stand-in
|
|
151
|
+
* must register under these exact regex instances so it REPLACES the
|
|
152
|
+
* direct entry (the LLMRegistry dict is keyed by regex object) instead of
|
|
153
|
+
* sitting behind it unmatched. Gemini's are ADK's built-in patterns, which
|
|
154
|
+
* are registered at import time — replacing them is how TracedGemini works
|
|
155
|
+
* too.
|
|
156
|
+
*/
|
|
157
|
+
const PATTERNS = {
|
|
158
|
+
gemini: Gemini.supportedModels,
|
|
159
|
+
anthropic: ClaudeLlm.supportedModels,
|
|
160
|
+
openai: GptLlm.supportedModels,
|
|
161
|
+
xai: GrokLlm.supportedModels,
|
|
162
|
+
};
|
|
75
163
|
let providersRegistered = false;
|
|
76
164
|
/**
|
|
77
165
|
* Registers every adapter whose credentials exist into the ADK LLMRegistry
|
|
78
166
|
* (Ollama unconditionally — local needs no key; Gemini always, since the
|
|
79
167
|
* registry needs a fallback class, and a missing Gemini key surfaces as a
|
|
80
|
-
* clear API error at call time).
|
|
81
|
-
*
|
|
168
|
+
* clear API error at call time). When MODEL_GATEWAY is configured, a
|
|
169
|
+
* provider whose direct key is ABSENT is served by the gateway stand-in
|
|
170
|
+
* instead (lib/models/gateway.ts — the fallback rule). Returns the
|
|
171
|
+
* per-provider statuses so entrypoints can log which models are routable.
|
|
82
172
|
*/
|
|
83
173
|
export function registerAvailableProviders(log) {
|
|
84
174
|
const statuses = providerStatuses();
|
|
85
175
|
if (!providersRegistered) {
|
|
86
176
|
providersRegistered = true;
|
|
87
177
|
for (const status of statuses) {
|
|
88
|
-
if (status.
|
|
178
|
+
if (status.transport === 'gateway' && status.provider !== 'ollama') {
|
|
179
|
+
registerGatewayLlm(PATTERNS[status.provider]);
|
|
180
|
+
}
|
|
181
|
+
else if (status.available || status.provider === 'gemini') {
|
|
89
182
|
REGISTRARS[status.provider]();
|
|
90
183
|
}
|
|
91
184
|
}
|
|
92
185
|
}
|
|
93
186
|
if (log) {
|
|
187
|
+
const problem = gatewayProblem();
|
|
188
|
+
if (problem)
|
|
189
|
+
log(`⚠ ${problem} — gateway ignored`);
|
|
94
190
|
for (const s of statuses) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
191
|
+
if (!s.available) {
|
|
192
|
+
log(`⚠ ${s.label} disabled (${s.reason}) — ${modelHint(s.provider)} models unavailable`);
|
|
193
|
+
}
|
|
194
|
+
else if (s.transport === 'gateway') {
|
|
195
|
+
log(`◇ ${s.label} — via gateway:${s.gateway} (${modelHint(s.provider)} served without native search)`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
log(`✓ ${s.label} — active`);
|
|
199
|
+
}
|
|
98
200
|
}
|
|
99
201
|
}
|
|
100
202
|
return statuses;
|
|
@@ -137,17 +239,26 @@ export function resolveModel(model, options = {}) {
|
|
|
137
239
|
const apiKey = providerForModel(model ?? DEFAULT_MODEL_FOR[normalizeProvider(options.defaultProvider)]) === normalizeProvider(options.defaultProvider)
|
|
138
240
|
? options.apiKey
|
|
139
241
|
: undefined;
|
|
140
|
-
|
|
242
|
+
// The fallback rule (lib/models/gateway.ts): the direct adapter whenever
|
|
243
|
+
// the provider's key — from env, or the caller's own BYOK key — is
|
|
244
|
+
// present; the gateway stand-in only when it is absent and a gateway is
|
|
245
|
+
// configured. The gateway key is server env only, never a request header.
|
|
246
|
+
const provider = providerForModel(resolved);
|
|
247
|
+
const endpoint = options.endpoint ? mergeEndpoint(provider, options.endpoint) : undefined;
|
|
248
|
+
if (planTransport(resolved, { callerKey: !!apiKey || !!endpoint }).transport === 'gateway') {
|
|
249
|
+
return new GatewayLlm({ model: resolved });
|
|
250
|
+
}
|
|
251
|
+
switch (provider) {
|
|
141
252
|
case 'ollama':
|
|
142
253
|
return new OllamaLlm({ model: resolved });
|
|
143
254
|
case 'anthropic':
|
|
144
|
-
return new ClaudeLlm({ model: resolved, apiKey });
|
|
255
|
+
return new ClaudeLlm({ model: resolved, apiKey, endpoint });
|
|
145
256
|
case 'openai':
|
|
146
|
-
return new GptLlm({ model: resolved, apiKey });
|
|
257
|
+
return new GptLlm({ model: resolved, apiKey, endpoint });
|
|
147
258
|
case 'xai':
|
|
148
259
|
return new GrokLlm({ model: resolved, apiKey });
|
|
149
260
|
case 'gemini':
|
|
150
|
-
return new TracedGemini({ model: resolved, apiKey });
|
|
261
|
+
return new TracedGemini({ model: resolved, apiKey, endpoint });
|
|
151
262
|
}
|
|
152
263
|
}
|
|
153
264
|
function normalizeProvider(provider) {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/models/retry.ts — the one retry policy for transient provider failures.
|
|
3
|
+
*
|
|
4
|
+
* WHY this file exists:
|
|
5
|
+
* Before it, resilience was an accident of which SDK an adapter used.
|
|
6
|
+
* Claude and GPT/Grok inherited their vendor SDKs' two retries; Gemini —
|
|
7
|
+
* the default provider — got none (`@google/genai` does a single fetch
|
|
8
|
+
* unless retryOptions is set, and ADK's Gemini never sets it); the
|
|
9
|
+
* chat-completions path (Ollama, gateways) was one bare `fetch`. A 503
|
|
10
|
+
* "high demand" from Google failed the whole turn. One policy here, used by
|
|
11
|
+
* every adapter that does not get it from its SDK, keeps the behaviour the
|
|
12
|
+
* same whichever provider an agent's YAML names.
|
|
13
|
+
*
|
|
14
|
+
* THE POLICY:
|
|
15
|
+
* - Bounded: MODEL_RETRY_MAX_ATTEMPTS total attempts (default 3, clamped
|
|
16
|
+
* 1..10; 1 disables retries). Matches the vendor SDKs' 1 + 2 retries.
|
|
17
|
+
* - Retried: HTTP 408/409/425/429/500/502/503/504, and connection-level
|
|
18
|
+
* failures that say nothing about the request (ECONNRESET, ETIMEDOUT,
|
|
19
|
+
* EPIPE, ECONNABORTED, EAI_AGAIN, undici socket/connect-timeout). Every
|
|
20
|
+
* other 4xx is the request's own fault and is never repeated.
|
|
21
|
+
* - NOT retried: ECONNREFUSED / ENOTFOUND. Against Ollama on localhost a
|
|
22
|
+
* refused connection means the server is not running, and the adapter's
|
|
23
|
+
* own message already says how to start it; retrying only delays that
|
|
24
|
+
* diagnosis by seconds, and no daemon restarts inside our backoff window.
|
|
25
|
+
* Against a remote gateway it means a wrong host or port — configuration,
|
|
26
|
+
* not load. Response-wait timeouts (undici headers/body timeout) are not
|
|
27
|
+
* retried either: a request that already ran for minutes is better
|
|
28
|
+
* reported than repeated at the same cost.
|
|
29
|
+
* - Backoff: full jitter, uniform in [0, min(8 s, 500 ms * 2^(n-1))], so
|
|
30
|
+
* callers that failed together do not come back together.
|
|
31
|
+
* - Retry-After (seconds, HTTP date, or OpenAI's retry-after-ms; Google's
|
|
32
|
+
* RetryInfo `retryDelay` in an error body) replaces the jittered delay
|
|
33
|
+
* when present. Above 60 s the call is NOT retried at all: per-minute
|
|
34
|
+
* quota windows reset inside a minute, so a longer wait signals a daily
|
|
35
|
+
* or hard quota that waiting will not fix — surface it now.
|
|
36
|
+
* - Cancellation: the turn's AbortSignal (lib/runtime/turnControl.ts) is
|
|
37
|
+
* checked before every retry and wakes the backoff sleep, so a canceled
|
|
38
|
+
* or timed-out turn never sleeps past its abort and never re-sends.
|
|
39
|
+
*
|
|
40
|
+
* THE STREAMING RULE:
|
|
41
|
+
* A retry is only ever made BEFORE the caller has received any response
|
|
42
|
+
* bytes. A failure after the first partial has been yielded would, if
|
|
43
|
+
* retried, replay text the user has already seen (and that ADK may have
|
|
44
|
+
* already turned into events). `fetchWithRetry` retries the request and
|
|
45
|
+
* hands back the Response before its body is read; `retryUntilFirstYield`
|
|
46
|
+
* retries a generator only while it has yielded nothing.
|
|
47
|
+
*
|
|
48
|
+
* Retries run inside one `llm.request` span and one step-budget charge: a
|
|
49
|
+
* retried call is still one logical model call. The count lands on the span
|
|
50
|
+
* as `llm.retries` (via the onRetry hook each adapter wires to
|
|
51
|
+
* setLlmSpanAttribute).
|
|
52
|
+
*/
|
|
53
|
+
export declare const RETRYABLE_STATUSES: ReadonlySet<number>;
|
|
54
|
+
export declare const RETRYABLE_NETWORK_CODES: ReadonlySet<string>;
|
|
55
|
+
export interface RetryPolicy {
|
|
56
|
+
/** Total attempts including the first; 1 = no retries. */
|
|
57
|
+
maxAttempts: number;
|
|
58
|
+
/** Backoff ceiling for attempt 1; doubles per attempt. */
|
|
59
|
+
baseDelayMs: number;
|
|
60
|
+
/** Backoff ceiling never grows past this. */
|
|
61
|
+
maxDelayMs: number;
|
|
62
|
+
/** A Retry-After longer than this is not waited out — the call fails. */
|
|
63
|
+
maxRetryAfterMs: number;
|
|
64
|
+
}
|
|
65
|
+
export declare const DEFAULT_RETRY_POLICY: Readonly<RetryPolicy>;
|
|
66
|
+
export declare const MAX_ATTEMPTS_ENV = "MODEL_RETRY_MAX_ATTEMPTS";
|
|
67
|
+
/**
|
|
68
|
+
* Process-wide overrides for the policy (tests shrink the delays; an
|
|
69
|
+
* embedding app can tune them). Returns a function restoring the previous
|
|
70
|
+
* overrides. The env var still sets maxAttempts unless overridden here.
|
|
71
|
+
*/
|
|
72
|
+
export declare function setRetryPolicyOverrides(next: Partial<RetryPolicy>): () => void;
|
|
73
|
+
/** The effective policy: defaults ← MODEL_RETRY_MAX_ATTEMPTS ← overrides. */
|
|
74
|
+
export declare function retryPolicy(env?: NodeJS.ProcessEnv): RetryPolicy;
|
|
75
|
+
/** What one failed attempt says about trying again. */
|
|
76
|
+
export interface RetryDecision {
|
|
77
|
+
retryable: boolean;
|
|
78
|
+
/** HTTP status, when the failure had one. */
|
|
79
|
+
status?: number;
|
|
80
|
+
/** Server-requested wait, when it sent one. */
|
|
81
|
+
retryAfterMs?: number;
|
|
82
|
+
}
|
|
83
|
+
export declare function isRetryableStatus(status: number): boolean;
|
|
84
|
+
/** The system/undici error code anywhere on an error's cause chain. */
|
|
85
|
+
export declare function networkErrorCode(err: unknown): string | undefined;
|
|
86
|
+
/** A numeric HTTP status carried on an error (genai ApiError, SDK errors). */
|
|
87
|
+
export declare function errorStatus(err: unknown): number | undefined;
|
|
88
|
+
/** Classifies a non-2xx HTTP response. */
|
|
89
|
+
export declare function classifyResponse(res: Pick<Response, 'status' | 'headers'>): RetryDecision;
|
|
90
|
+
/** Classifies a thrown error (an HTTP error from an SDK, or a network one). */
|
|
91
|
+
export declare function classifyError(err: unknown): RetryDecision;
|
|
92
|
+
/**
|
|
93
|
+
* Parses a Retry-After value: delta-seconds ("30", "1.5") or an HTTP date.
|
|
94
|
+
* Returns milliseconds (never negative), or undefined when unparseable.
|
|
95
|
+
*/
|
|
96
|
+
export declare function parseRetryAfter(value: string | null | undefined, now?: number): number | undefined;
|
|
97
|
+
/** Retry-After from response headers, preferring OpenAI's retry-after-ms. */
|
|
98
|
+
export declare function retryAfterFromHeaders(headers: Pick<Headers, 'get'> | undefined, now?: number): number | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Google RPC's RetryInfo, which genai only exposes inside the error message
|
|
101
|
+
* (its ApiError drops the response headers): `"retryDelay": "37s"`.
|
|
102
|
+
*/
|
|
103
|
+
export declare function googleRetryDelayMs(message: string): number | undefined;
|
|
104
|
+
/** Full-jitter backoff for the given (1-based) failed attempt. */
|
|
105
|
+
export declare function backoffDelayMs(attempt: number, policy?: RetryPolicy, random?: () => number): number;
|
|
106
|
+
/**
|
|
107
|
+
* How long to wait before attempt `attempt + 1`, or undefined to stop:
|
|
108
|
+
* not retryable, attempts exhausted, the turn aborted, or the server asked
|
|
109
|
+
* for a wait beyond the cap.
|
|
110
|
+
*/
|
|
111
|
+
export declare function nextRetryDelay(attempt: number, decision: RetryDecision, policy?: RetryPolicy, signal?: AbortSignal, random?: () => number): number | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* Sleeps `ms`, waking early if `signal` aborts. Resolves true when the full
|
|
114
|
+
* wait elapsed, false when it was cut short by an abort.
|
|
115
|
+
*/
|
|
116
|
+
export declare function sleepUnlessAborted(ms: number, signal?: AbortSignal): Promise<boolean>;
|
|
117
|
+
export interface RetryOptions {
|
|
118
|
+
signal?: AbortSignal;
|
|
119
|
+
policy?: RetryPolicy;
|
|
120
|
+
/** Called before each wait; `retries` counts retries made so far + 1. */
|
|
121
|
+
onRetry?: (info: {
|
|
122
|
+
retries: number;
|
|
123
|
+
delayMs: number;
|
|
124
|
+
decision: RetryDecision;
|
|
125
|
+
}) => void;
|
|
126
|
+
/** Injectable for tests. */
|
|
127
|
+
random?: () => number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* `fetch` with the policy applied to the REQUEST. Returns the first 2xx, or
|
|
131
|
+
* the last non-2xx Response once retrying stops (body unread, so the caller
|
|
132
|
+
* builds its own error from it). Throws the last error when the final
|
|
133
|
+
* attempt failed at the network level. The body of a successful response is
|
|
134
|
+
* never touched here — reading (and streaming) it is the caller's, and a
|
|
135
|
+
* failure there is not retried (see THE STREAMING RULE above).
|
|
136
|
+
*/
|
|
137
|
+
export declare function fetchWithRetry(url: string, init: RequestInit, opts?: RetryOptions): Promise<{
|
|
138
|
+
response: Response;
|
|
139
|
+
retries: number;
|
|
140
|
+
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Re-runs a generator factory while it fails BEFORE its first yield. Once
|
|
143
|
+
* anything has been yielded a failure is rethrown untouched: retrying then
|
|
144
|
+
* would duplicate output the caller has already passed on.
|
|
145
|
+
*/
|
|
146
|
+
export declare function retryUntilFirstYield<T>(start: (attempt: number) => AsyncGenerator<T, void>, opts?: RetryOptions): AsyncGenerator<T, void>;
|