melchizedek-agents 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/DOCUMENTATION.md +11 -9
  3. package/config/agents/examples/critic.yaml +12 -7
  4. package/config/agents/examples/image_production.yaml +2 -2
  5. package/config/agents/examples/librarian.yaml +1 -1
  6. package/config/agents/examples/pipeline.yaml +1 -1
  7. package/config/agents/examples/research.yaml +3 -2
  8. package/config/agents/examples/scribe.yaml +5 -4
  9. package/config/agents/examples/weather.yaml +2 -1
  10. package/config/agents/syndicateSchema.yaml +18 -15
  11. package/dist/lib/dispatch.d.ts +4 -4
  12. package/dist/lib/dispatch.js +4 -4
  13. package/dist/lib/syndicateSchema.js +6 -6
  14. package/package.json +2 -2
  15. package/skills/briefs/melchizedek-author.md +1 -1
  16. package/skills/melchizedek-author/SKILL.md +1 -1
  17. package/skills/melchizedek-author/assets/minimal.yaml +1 -1
  18. package/dist/lib/adkPeer.d.ts +0 -63
  19. package/dist/lib/adkPeer.js +0 -240
  20. package/dist/lib/compileAdk.d.ts +0 -32
  21. package/dist/lib/compileAdk.js +0 -142
  22. package/dist/lib/models/adkGeminiAdapter.d.ts +0 -90
  23. package/dist/lib/models/adkGeminiAdapter.js +0 -369
  24. package/dist/lib/models/adkShim.d.ts +0 -111
  25. package/dist/lib/models/adkShim.js +0 -136
  26. package/dist/lib/models/claudeLlm.d.ts +0 -79
  27. package/dist/lib/models/claudeLlm.js +0 -87
  28. package/dist/lib/models/fallback.d.ts +0 -48
  29. package/dist/lib/models/fallback.js +0 -116
  30. package/dist/lib/models/gatewayLlm.d.ts +0 -36
  31. package/dist/lib/models/gatewayLlm.js +0 -44
  32. package/dist/lib/models/gptLlm.d.ts +0 -99
  33. package/dist/lib/models/gptLlm.js +0 -129
  34. package/dist/lib/models/grokLlm.d.ts +0 -50
  35. package/dist/lib/models/grokLlm.js +0 -56
  36. package/dist/lib/models/kimiLlm.d.ts +0 -31
  37. package/dist/lib/models/kimiLlm.js +0 -35
  38. package/dist/lib/models/ollamaLlm.d.ts +0 -37
  39. package/dist/lib/models/ollamaLlm.js +0 -42
  40. package/dist/lib/models/openAiCompatibleLlm.d.ts +0 -96
  41. package/dist/lib/models/openAiCompatibleLlm.js +0 -156
  42. package/dist/lib/models/tracedGemini.d.ts +0 -41
  43. package/dist/lib/models/tracedGemini.js +0 -98
  44. package/dist/lib/runtime/adkMemoryBridge.d.ts +0 -46
  45. package/dist/lib/runtime/adkMemoryBridge.js +0 -68
  46. package/dist/lib/runtime/adkSessionBridge.d.ts +0 -78
  47. package/dist/lib/runtime/adkSessionBridge.js +0 -176
  48. package/dist/lib/tools/adkTool.d.ts +0 -81
  49. package/dist/lib/tools/adkTool.js +0 -268
  50. package/dist/lib/tools/collectionsSearchTool.d.ts +0 -56
  51. package/dist/lib/tools/collectionsSearchTool.js +0 -71
  52. package/dist/lib/tools/urlContextTool.d.ts +0 -32
  53. package/dist/lib/tools/urlContextTool.js +0 -43
  54. package/dist/lib/tools/webSearchTool.d.ts +0 -51
  55. package/dist/lib/tools/webSearchTool.js +0 -82
  56. package/dist/lib/tools/xSearchTool.d.ts +0 -59
  57. package/dist/lib/tools/xSearchTool.js +0 -74
@@ -1,369 +0,0 @@
1
- /**
2
- * lib/models/adkGeminiAdapter.ts — TEMPORARY: Gemini as a contract
3
- * ModelAdapter (ADR 0048) over ADK's own Gemini, the TracedGemini path of
4
- * lib/models/tracedGemini.ts, through the genai mapping (lib/models/genaiMapping.ts).
5
- *
6
- * WHY this file exists:
7
- * The native loop (ADR 0045) calls ModelAdapters only. The engine's own
8
- * Gemini adapter (lib/models/geminiAdapter.ts) calls @google/genai with no
9
- * ADK, and waits for its live parity run at gate G3. Until it passes,
10
- * Gemini on the native runtime goes through ADK's proven Gemini, wrapped
11
- * here, so every provider has a contract adapter before gate G1. Delete
12
- * this file once G3 is signed and GeminiAdapter serves every Gemini id
13
- * (the plan's G3 risk: "The WS1-8 wrapper over ADK's Gemini stays until
14
- * the cells pass"). resolveAdapter (lib/models/registry.ts) returns it for Gemini ids.
15
- *
16
- * THE CALL:
17
- * 1. The ModelRequest becomes an LlmRequest (modelRequestToLlmRequest),
18
- * and then what ADK's path does to one before its Gemini sees it:
19
- * - system messages joined onto the system prompt, in order, since
20
- * Gemini takes no `system` content;
21
- * - `adk-` call ids taken off calls and results, as ADK's flow does
22
- * before every model call (ids the mapping minted are already off);
23
- * - `includeServerSideToolInvocations`, which the compiler sends on
24
- * every Gemini agent (lib/compile.ts);
25
- * - `includeThoughts` under any reasoning but `none`, the contract's
26
- * Gemini table, as GeminiAdapter sends it;
27
- * - the platform's wire model (GEMINI_MODEL_MAP), as TracedGemini's own.
28
- * The request is copied where ADK writes to it (a blob's display name),
29
- * so the caller's history is never changed.
30
- * 2. TracedGemini.generateWithRetries runs it: ADK's Gemini with the
31
- * shared retries, tagging the span that is open around this adapter
32
- * (llm.web_search.native, llm.retries, llm.http_status), as it tags its
33
- * own. `llm.capability.dropped` marks native tools Gemini has none for.
34
- * 3. Each LlmResponse comes back through llmResponseToModelResponse,
35
- * folded into the contract's stream. A thought signature becomes
36
- * providerState naming the request's model. A call Gemini returns
37
- * without an id gets the mapping's `genai-noid-<position>-<part>`, or,
38
- * streamed, ADK's own `adk-` id; neither goes back on the wire.
39
- *
40
- * REPLAY: every Gemini signature in the history goes back on its part, as
41
- * ADK sends a stored session's, whichever turn or model wrote it.
42
- * GeminiAdapter replays only the current turn's, for its own model.
43
- *
44
- * THE SPAN AND THE CHARGE are the caller's (ADR 0053): this adapter opens no
45
- * llm.request span and never charges the turn. Behind the ADK shim
46
- * (lib/models/adkShim.ts) it records the span TracedGemini records today for
47
- * the same exchange, but for a failed call's code (tests/telemetryLedger.test.ts).
48
- *
49
- * THE ADAPTER RULES (contract.ts header) as this adapter keeps them:
50
- * - Partials: a streamed call's text and thinking deltas, as ADK's stream
51
- * aggregator yields them. A call that does not stream yields its
52
- * thinking as one partial before the final.
53
- * - One final: every non-partial response ADK yields for the call (its
54
- * aggregator flushes the text before each tool call) folds into it, with
55
- * the last usage, grounding and finish reason.
56
- * - Errors are finals, never throws. ADK's Gemini throws on a failed call:
57
- * that is GEMINI_ERROR, `retryable` from lib/models/retry.ts's
58
- * classifyError, `status` when the error had one. An error code ADK
59
- * yields (a finish or block reason) is the code, not retryable; ADK's
60
- * `STOP` is no error. Setup failures are MISSING_API_KEY and
61
- * ENDPOINT_MISCONFIGURED, as GeminiAdapter's.
62
- * - The abort: `request.signal` (the shim aborts it when the turn stops)
63
- * goes on the request config. An aborted call is GEMINI_ERROR, never
64
- * retryable, and the iteration ends at once whatever the transport does;
65
- * a signal aborted before the call sends nothing.
66
- * - Retries: TracedGemini's, only before the first chunk.
67
- * - Messages never carry the key in use or anything shaped like a key.
68
- */
69
- import { endpointFromEnv, endpointProblems, platformModel } from './endpoints.js';
70
- import { errorText } from './errorResponse.js';
71
- import { GEMINI_PROVIDER } from './geminiState.js';
72
- import { llmResponseToModelResponse, modelRequestToLlmRequest, nativeToolsWithoutGeminiTool } from './genaiMapping.js';
73
- import { TracedGemini } from './tracedGemini.js';
74
- import { requireAdk } from '../adkPeer.js';
75
- import { classifyError, errorStatus } from './retry.js';
76
- import { setLlmSpanAttribute } from '../observability/tracer.js';
77
- /** The prefix of a call id ADK (or the engine after it) made: such an id never goes on the wire. */
78
- const ADK_CALL_ID_PREFIX = 'adk-';
79
- // ── The adapter ──────────────────────────────────────────────────────────────
80
- /** Temporary: ADK's Gemini behind the model contract, until GeminiAdapter passes gate G3. */
81
- export class AdkGeminiAdapter {
82
- provider = GEMINI_PROVIDER;
83
- model;
84
- #apiKey;
85
- #endpoint;
86
- #connection;
87
- #warned = new Set();
88
- constructor(options) {
89
- requireAdk('GEMINI_ADAPTER=adk (AdkGeminiAdapter, which runs ADK\'s Gemini)');
90
- this.model = options.model;
91
- this.#apiKey = nonEmpty(options.apiKey);
92
- this.#endpoint = options.endpoint;
93
- }
94
- async *generate(request) {
95
- const { signal } = request;
96
- const model = request.model || this.model;
97
- const stream = request.stream === true;
98
- const named = request.nativeTools ?? [];
99
- const searchTool = named.includes('web_search') || !named.includes('google_search') ? 'web_search' : 'google_search';
100
- const reply = new Reply({ model, position: request.messages.length, searchTool, stream });
101
- if (signal?.aborted) {
102
- yield reply.aborted();
103
- return;
104
- }
105
- const connection = this.#connect();
106
- if ('code' in connection) {
107
- yield { partial: false, parts: [], finishReason: 'error', error: connection };
108
- return;
109
- }
110
- const { gemini, endpoint, secret } = connection;
111
- try {
112
- const llmRequest = adkRequest(request, model, platformModel(endpoint, model));
113
- this.#report(nativeToolsWithoutGeminiTool(named));
114
- for await (const response of untilAborted(gemini.generateWithRetries(llmRequest, stream, signal), signal)) {
115
- const delta = reply.add(response);
116
- if (delta)
117
- yield delta;
118
- }
119
- const thinking = reply.thinking();
120
- if (thinking)
121
- yield thinking;
122
- yield reply.final();
123
- }
124
- catch (err) {
125
- if (signal?.aborted) {
126
- yield reply.aborted();
127
- return;
128
- }
129
- yield reply.failed(err, secret);
130
- }
131
- }
132
- /** Marks the open span with the native tools Gemini has none for, and warns once per tool. */
133
- #report(dropped) {
134
- if (dropped.length === 0)
135
- return;
136
- setLlmSpanAttribute('llm.capability.dropped', dropped.join(','));
137
- for (const tool of dropped) {
138
- if (this.#warned.has(tool))
139
- continue;
140
- this.#warned.add(tool);
141
- console.warn(`⚠ ${tool} is not a Gemini tool; ${this.model} runs without it.`);
142
- }
143
- }
144
- /** ADK's Gemini for this adapter's endpoint, built once; or why it cannot be built. */
145
- #connect() {
146
- if (this.#connection)
147
- return this.#connection;
148
- let endpoint;
149
- try {
150
- endpoint = this.#endpoint ?? endpointFromEnv('gemini');
151
- }
152
- catch (err) {
153
- return setupError('ENDPOINT_MISCONFIGURED', errorText(err));
154
- }
155
- let secret;
156
- if (endpoint.platform === 'vertex') {
157
- const problems = endpointProblems('gemini', endpoint);
158
- if (problems.length > 0)
159
- return setupError('ENDPOINT_MISCONFIGURED', `Gemini on Vertex AI: ${problems.join('; ')}.`);
160
- }
161
- else if (endpoint.platform === 'direct') {
162
- secret = this.#apiKey ?? nonEmpty(endpoint.apiKey) ?? envApiKey();
163
- if (!secret) {
164
- return setupError('MISSING_API_KEY', 'No Gemini API key: set GOOGLE_GENAI_API_KEY (or GEMINI_API_KEY), or GEMINI_PLATFORM=vertex for Vertex AI.');
165
- }
166
- }
167
- else {
168
- return setupError('ENDPOINT_MISCONFIGURED', `Gemini has no ${endpoint.platform} platform; use direct or vertex.`);
169
- }
170
- try {
171
- // TracedGemini applies the platform (ADR 0023): on Vertex AI, Google
172
- // Application Default Credentials and no AI Studio key.
173
- const gemini = new TracedGemini({ model: this.model, ...(secret ? { apiKey: secret } : {}), endpoint });
174
- this.#connection = { gemini, endpoint, secret };
175
- }
176
- catch (err) {
177
- return setupError('ENDPOINT_MISCONFIGURED', `ADK's Gemini failed to build: ${scrub(err, secret)}`);
178
- }
179
- return this.#connection;
180
- }
181
- }
182
- // ── The request ──────────────────────────────────────────────────────────────
183
- /** The LlmRequest ADK's Gemini is handed for this ModelRequest (see the header, THE CALL). */
184
- function adkRequest(request, model, wireModel) {
185
- const extra = [];
186
- for (const message of request.messages) {
187
- if (message.role === 'system')
188
- for (const part of message.parts)
189
- if (part.text)
190
- extra.push(part.text);
191
- }
192
- const llmRequest = modelRequestToLlmRequest({ ...request, model, messages: request.messages.filter((m) => m.role !== 'system') });
193
- const config = llmRequest.config ?? {};
194
- if (extra.length > 0) {
195
- config.systemInstruction = { parts: [...(request.system ? [request.system] : []), ...extra].map((text) => ({ text })) };
196
- }
197
- config.toolConfig = { ...config.toolConfig, includeServerSideToolInvocations: true };
198
- const thinking = config.thinkingConfig;
199
- const none = request.reasoning === 'none' || (typeof request.reasoning === 'object' && request.reasoning.budget_tokens <= 0);
200
- if (thinking && !none)
201
- config.thinkingConfig = { ...thinking, includeThoughts: true };
202
- return { ...llmRequest, model: wireModel, contents: llmRequest.contents.map(forAdk), config };
203
- }
204
- /**
205
- * A content as ADK's Gemini may take it: `adk-` ids off calls and results,
206
- * and every part copied where ADK writes (it clears a blob's display name in
207
- * place), so a part the history shares (a carried genai part) is never changed.
208
- */
209
- function forAdk(content) {
210
- return {
211
- ...content,
212
- parts: (content.parts ?? []).map((part) => {
213
- const out = { ...part };
214
- if (part.inlineData)
215
- out.inlineData = { ...part.inlineData };
216
- if (part.fileData)
217
- out.fileData = { ...part.fileData };
218
- if (part.functionCall)
219
- out.functionCall = withoutAdkId(part.functionCall);
220
- if (part.functionResponse)
221
- out.functionResponse = withoutAdkId(part.functionResponse);
222
- return out;
223
- }),
224
- };
225
- }
226
- function withoutAdkId(value) {
227
- if (typeof value.id !== 'string' || !value.id.startsWith(ADK_CALL_ID_PREFIX))
228
- return { ...value };
229
- const { id: _id, ...rest } = value;
230
- return rest;
231
- }
232
- // ── The response ─────────────────────────────────────────────────────────────
233
- /** Folds the LlmResponses ADK yields for one call into the contract's deltas and its one final. */
234
- class Reply {
235
- #model;
236
- #position;
237
- #searchTool;
238
- #stream;
239
- #parts = [];
240
- #thinking = [];
241
- #usage;
242
- #grounding;
243
- #finishReason;
244
- #error;
245
- constructor(options) {
246
- this.#model = options.model;
247
- this.#position = options.position;
248
- this.#searchTool = options.searchTool;
249
- this.#stream = options.stream;
250
- }
251
- /** Takes one LlmResponse. A streamed partial's deltas come back to be yielded; everything else folds into the final. */
252
- add(response) {
253
- if (response.usageMetadata)
254
- this.#usage = response.usageMetadata;
255
- if (response.groundingMetadata)
256
- this.#grounding = response.groundingMetadata;
257
- if (response.finishReason)
258
- this.#finishReason = response.finishReason;
259
- if (response.partial === true) {
260
- if (!this.#stream)
261
- return undefined;
262
- const delta = llmResponseToModelResponse(response);
263
- return delta.parts.length > 0 ? delta : undefined;
264
- }
265
- if (response.errorCode) {
266
- this.#error = { errorCode: response.errorCode, errorMessage: response.errorMessage, customMetadata: response.customMetadata };
267
- }
268
- for (const part of response.content?.parts ?? []) {
269
- this.#parts.push(part);
270
- if (part.thought === true && part.text)
271
- this.#thinking.push({ type: 'thinking', text: part.text });
272
- }
273
- return undefined;
274
- }
275
- /** The thinking of a call that did not stream, as one partial before its final. */
276
- thinking() {
277
- return !this.#stream && this.#thinking.length > 0 ? { partial: true, parts: this.#thinking } : undefined;
278
- }
279
- /** The one final for a call that ran to its end. */
280
- final() {
281
- return this.#fold(true);
282
- }
283
- /** The final for a call ADK's Gemini threw on: GEMINI_ERROR, with what arrived before it. */
284
- failed(err, secret) {
285
- const status = errorStatus(err);
286
- return {
287
- ...this.#fold(false),
288
- finishReason: 'error',
289
- error: {
290
- code: 'GEMINI_ERROR',
291
- message: scrub(err, secret),
292
- retryable: classifyError(err).retryable,
293
- ...(status !== undefined ? { status } : {}),
294
- },
295
- };
296
- }
297
- /** The final for a canceled call: never retryable, so no fallback answers it. */
298
- aborted() {
299
- return { ...this.#fold(false), finishReason: 'error', error: { code: 'GEMINI_ERROR', message: 'The Gemini request was aborted.', retryable: false } };
300
- }
301
- /** Everything ADK yielded as one LlmResponse, mapped; with the error it yielded, or (for a throw) without. */
302
- #fold(withError) {
303
- const folded = {
304
- ...(this.#parts.length > 0 ? { content: { role: 'model', parts: this.#parts } } : {}),
305
- ...(this.#usage ? { usageMetadata: this.#usage } : {}),
306
- ...(this.#grounding ? { groundingMetadata: this.#grounding } : {}),
307
- ...(this.#finishReason ? { finishReason: this.#finishReason } : {}),
308
- ...(withError ? this.#error : {}),
309
- };
310
- return llmResponseToModelResponse(folded, { model: this.#model, index: this.#position, searchTool: this.#searchTool });
311
- }
312
- }
313
- // ── Transport ────────────────────────────────────────────────────────────────
314
- function abortError() {
315
- return Object.assign(new Error('The Gemini request was aborted.'), { name: 'AbortError' });
316
- }
317
- /**
318
- * Iterates `source` until `signal` aborts, and then throws an AbortError at
319
- * once: the SDK aborts its fetch on the same signal, but a stalled stream or
320
- * a backoff sleep must not hold the caller. The source is closed in the
321
- * background.
322
- */
323
- async function* untilAborted(source, signal) {
324
- if (!signal) {
325
- yield* source;
326
- return;
327
- }
328
- let onAbort = () => { };
329
- const aborted = new Promise((_, reject) => {
330
- onAbort = () => reject(abortError());
331
- });
332
- aborted.catch(() => { });
333
- if (signal.aborted)
334
- onAbort();
335
- else
336
- signal.addEventListener('abort', onAbort, { once: true });
337
- let done = false;
338
- try {
339
- for (;;) {
340
- const next = await Promise.race([source.next(), aborted]);
341
- if (next.done) {
342
- done = true;
343
- return;
344
- }
345
- yield next.value;
346
- }
347
- }
348
- finally {
349
- signal.removeEventListener('abort', onAbort);
350
- if (!done)
351
- source.return(undefined).catch(() => { });
352
- }
353
- }
354
- // ── Helpers ──────────────────────────────────────────────────────────────────
355
- function setupError(code, message) {
356
- return { code, message, retryable: false };
357
- }
358
- function nonEmpty(value) {
359
- return value?.trim() || undefined;
360
- }
361
- /** The Gemini API key from the environment, in ADK's order. */
362
- function envApiKey() {
363
- return nonEmpty(process.env.GOOGLE_GENAI_API_KEY) ?? nonEmpty(process.env.GOOGLE_API_KEY) ?? nonEmpty(process.env.GEMINI_API_KEY);
364
- }
365
- /** An error's text without the key in use, or anything shaped like a key. */
366
- function scrub(err, secret) {
367
- const message = err instanceof Error ? err.message : String(err);
368
- return errorText(secret ? message.split(secret).join('[redacted]') : message);
369
- }
@@ -1,111 +0,0 @@
1
- /**
2
- * lib/models/adkShim.ts — any ModelAdapter on the engine's own contract
3
- * (lib/models/contract.ts, ADR 0048) as an ADK BaseLlm, so an adapter can
4
- * move onto the contract while ADK still runs every turn (ADR 0045).
5
- *
6
- * WHY this file exists:
7
- * ADK calls `BaseLlm.generateContentAsync(llmRequest, stream, abortSignal)`
8
- * and expects LlmResponses back. An adapter on the contract implements
9
- * `generate(ModelRequest)` and yields ModelResponses. The shim is the one
10
- * class between them: it maps the LlmRequest in and each ModelResponse out
11
- * through lib/models/genaiMapping.ts, and does no translation of its own.
12
- * The adapter tickets move one adapter at a time behind it, and ADK sees
13
- * no change: ClaudeLlm (lib/models/claudeLlm.ts) is the shim around
14
- * ClaudeAdapter. A provider's ADK class may extend `toModelRequest` with
15
- * what its adapter reads on the ADK path only (ADR 0055).
16
- *
17
- * WHAT THE SHIM DOES ONCE, SO NO CONTRACT ADAPTER DOES IT (ADR 0053):
18
- * - The turn's charge and the llm.request span. The call goes through
19
- * `traceLlmGeneration` (lib/observability/tracer.ts) exactly as every
20
- * ADK-path adapter's call does, with the adapter's `provider`, the
21
- * shim's model id and the request, here the ModelRequest the shim hands
22
- * the adapter (an ADK-path adapter maps its LlmRequest to one for a
23
- * failed call's payload). So the step budget, a stopped
24
- * turn's refusal (STEP_LIMIT, DEADLINE_EXCEEDED, CANCELED, as the same
25
- * LlmResponse), the token charge and the span's attributes are what
26
- * GptLlm and the chat-completions adapters produce. A refused
27
- * call never reaches the adapter. The adapter decorates the open span
28
- * with `setLlmSpanAttribute`, and never opens one of its own.
29
- * - The abort signal. The request's `signal` aborts when the turn stops
30
- * (`currentTurnSignal()`) or when the signal ADK passes aborts, whichever
31
- * comes first; an adapter hands it to its provider call.
32
- *
33
- * TWO SEAMS FOR A SUBCLASS, the genai mapping by default:
34
- * - `toLlmResponse` (ADR 0056) keeps what an ADK-path adapter wrote beyond
35
- * the contract: GptLlm's usage and server-side tool record, and the
36
- * chat-completions shims' usage and final shape. It runs inside the
37
- * span, so the tracer reads what it returns.
38
- * - `toModelRequest` (ADR 0057) carries what an adapter reads beside the
39
- * contract: the chat-completions shims' older generateContentConfig
40
- * spelling. The span records the request it returns.
41
- *
42
- * WHAT IT DOES NOT DO:
43
- * - Repair an adapter that breaks the contract. A throw reaches ADK as a
44
- * throw (as Gemini's does today), and every response is mapped as it
45
- * comes, so a missing final or a response after it passes through.
46
- * - Live connections. `connect()` is refused, as on every adapter but
47
- * ADK's Gemini: live sessions are outside the contract.
48
- *
49
- * ONE SHIM PER LEAF ADAPTER. A fallback pair on the ADK path is
50
- * `FallbackLlm(shim(primary), shim(fallback))` (ADR 0044), so each model call
51
- * is charged and traced on its own, as today. A FallbackAdapter behind one
52
- * shim would make a redirected call one span and one charge.
53
- */
54
- import { BaseLlm } from '../adkPeer.ts';
55
- import type { BaseLlmConnection, BaseLlmType, LlmRequest, LlmResponse } from '@google/adk';
56
- import type { ModelAdapter, ModelRequest, ModelResponse } from './contract.ts';
57
- import type { ModelRequestOptions } from './genaiMapping.ts';
58
- export interface AdkShimOptions {
59
- /**
60
- * The model id ADK and the span see, and the request's `model`. Default
61
- * the adapter's own `model`, which is what an adapter for one YAML id has.
62
- */
63
- model?: string;
64
- }
65
- /** Builds the adapter for a model id: what a registered shim class calls when ADK constructs it. */
66
- export type ModelAdapterFactory = (model: string) => ModelAdapter;
67
- export declare class AdkShim extends BaseLlm {
68
- /** The adapter every call runs through. */
69
- readonly adapter: ModelAdapter;
70
- constructor(adapter: ModelAdapter, options?: AdkShimOptions);
71
- /**
72
- * One model call: charged against the turn and traced as one llm.request
73
- * span, then mapped to the contract and back. A call the turn refuses
74
- * yields the refusal and never reaches the adapter.
75
- */
76
- generateContentAsync(llmRequest: LlmRequest, stream?: boolean, abortSignal?: AbortSignal): AsyncGenerator<LlmResponse, void>;
77
- /** Runs inside the span: the adapter's setLlmSpanAttribute calls land on it. */
78
- private generateInner;
79
- /**
80
- * The ModelRequest one call hands the adapter: `llmRequestToModelRequest`.
81
- * A provider's ADK class may extend it with what its adapter reads on the
82
- * ADK path only, an agent setting the contract leaves out: ClaudeLlm's
83
- * older reasoning spelling (ADR 0055), and the chat-completions shims'
84
- * older generateContentConfig spelling (ADR 0057). It never removes or
85
- * rewrites a contract field. The span records what it returns.
86
- */
87
- protected toModelRequest(llmRequest: LlmRequest, options: ModelRequestOptions): ModelRequest;
88
- /**
89
- * One ModelResponse as the LlmResponse ADK sees: modelResponseToLlmResponse.
90
- * It runs inside the llm.request span, so the tracer reads what it returns.
91
- * A subclass that stands in for an ADK-path adapter overrides it to keep
92
- * what that adapter wrote beyond the contract (GptLlm, ADR 0056; the
93
- * chat-completions shims, ADR 0057).
94
- */
95
- protected toLlmResponse(response: ModelResponse): LlmResponse;
96
- /** Live/bidirectional connections are outside the model contract. Throws to say so. */
97
- connect(_llmRequest: LlmRequest): Promise<BaseLlmConnection>;
98
- }
99
- /** A shim around `adapter`, under `model` (default the adapter's own id). */
100
- export declare function adkShim(adapter: ModelAdapter, model?: string): AdkShim;
101
- /**
102
- * A shim class ADK's LLMRegistry can register: it answers for
103
- * `supportedModels` and builds its adapter with `createAdapter(model)` when
104
- * ADK constructs it for a model id. Pass the direct adapter's own pattern
105
- * instances, since the registry keys on the pattern object (see
106
- * lib/models/gatewayLlm.ts), so the shim replaces that entry rather than
107
- * shadowing it.
108
- */
109
- export declare function adkShimClass(supportedModels: Array<string | RegExp>, createAdapter: ModelAdapterFactory): BaseLlmType & (new (params: {
110
- model: string;
111
- }) => AdkShim);
@@ -1,136 +0,0 @@
1
- /**
2
- * lib/models/adkShim.ts — any ModelAdapter on the engine's own contract
3
- * (lib/models/contract.ts, ADR 0048) as an ADK BaseLlm, so an adapter can
4
- * move onto the contract while ADK still runs every turn (ADR 0045).
5
- *
6
- * WHY this file exists:
7
- * ADK calls `BaseLlm.generateContentAsync(llmRequest, stream, abortSignal)`
8
- * and expects LlmResponses back. An adapter on the contract implements
9
- * `generate(ModelRequest)` and yields ModelResponses. The shim is the one
10
- * class between them: it maps the LlmRequest in and each ModelResponse out
11
- * through lib/models/genaiMapping.ts, and does no translation of its own.
12
- * The adapter tickets move one adapter at a time behind it, and ADK sees
13
- * no change: ClaudeLlm (lib/models/claudeLlm.ts) is the shim around
14
- * ClaudeAdapter. A provider's ADK class may extend `toModelRequest` with
15
- * what its adapter reads on the ADK path only (ADR 0055).
16
- *
17
- * WHAT THE SHIM DOES ONCE, SO NO CONTRACT ADAPTER DOES IT (ADR 0053):
18
- * - The turn's charge and the llm.request span. The call goes through
19
- * `traceLlmGeneration` (lib/observability/tracer.ts) exactly as every
20
- * ADK-path adapter's call does, with the adapter's `provider`, the
21
- * shim's model id and the request, here the ModelRequest the shim hands
22
- * the adapter (an ADK-path adapter maps its LlmRequest to one for a
23
- * failed call's payload). So the step budget, a stopped
24
- * turn's refusal (STEP_LIMIT, DEADLINE_EXCEEDED, CANCELED, as the same
25
- * LlmResponse), the token charge and the span's attributes are what
26
- * GptLlm and the chat-completions adapters produce. A refused
27
- * call never reaches the adapter. The adapter decorates the open span
28
- * with `setLlmSpanAttribute`, and never opens one of its own.
29
- * - The abort signal. The request's `signal` aborts when the turn stops
30
- * (`currentTurnSignal()`) or when the signal ADK passes aborts, whichever
31
- * comes first; an adapter hands it to its provider call.
32
- *
33
- * TWO SEAMS FOR A SUBCLASS, the genai mapping by default:
34
- * - `toLlmResponse` (ADR 0056) keeps what an ADK-path adapter wrote beyond
35
- * the contract: GptLlm's usage and server-side tool record, and the
36
- * chat-completions shims' usage and final shape. It runs inside the
37
- * span, so the tracer reads what it returns.
38
- * - `toModelRequest` (ADR 0057) carries what an adapter reads beside the
39
- * contract: the chat-completions shims' older generateContentConfig
40
- * spelling. The span records the request it returns.
41
- *
42
- * WHAT IT DOES NOT DO:
43
- * - Repair an adapter that breaks the contract. A throw reaches ADK as a
44
- * throw (as Gemini's does today), and every response is mapped as it
45
- * comes, so a missing final or a response after it passes through.
46
- * - Live connections. `connect()` is refused, as on every adapter but
47
- * ADK's Gemini: live sessions are outside the contract.
48
- *
49
- * ONE SHIM PER LEAF ADAPTER. A fallback pair on the ADK path is
50
- * `FallbackLlm(shim(primary), shim(fallback))` (ADR 0044), so each model call
51
- * is charged and traced on its own, as today. A FallbackAdapter behind one
52
- * shim would make a redirected call one span and one charge.
53
- */
54
- import { BaseLlm } from '../adkPeer.js';
55
- import { llmRequestToModelRequest, modelResponseToLlmResponse } from './genaiMapping.js';
56
- import { traceLlmGeneration } from '../observability/tracer.js';
57
- import { currentTurnSignal } from '../runtime/turnControl.js';
58
- /** One signal that aborts when any of the given ones does; undefined when none is given. */
59
- function eitherSignal(...candidates) {
60
- const signals = [...new Set(candidates.filter((s) => s !== undefined))];
61
- return signals.length <= 1 ? signals[0] : AbortSignal.any(signals);
62
- }
63
- export class AdkShim extends BaseLlm {
64
- /** The adapter every call runs through. */
65
- adapter;
66
- constructor(adapter, options = {}) {
67
- super({ model: options.model ?? adapter.model });
68
- this.adapter = adapter;
69
- }
70
- /**
71
- * One model call: charged against the turn and traced as one llm.request
72
- * span, then mapped to the contract and back. A call the turn refuses
73
- * yields the refusal and never reaches the adapter.
74
- */
75
- async *generateContentAsync(llmRequest, stream = false, abortSignal) {
76
- const signal = eitherSignal(abortSignal, currentTurnSignal(), llmRequest.config?.abortSignal);
77
- const request = this.toModelRequest(llmRequest, {
78
- model: this.model,
79
- stream,
80
- ...(signal ? { signal } : {}),
81
- });
82
- yield* traceLlmGeneration({ provider: this.adapter.provider, model: this.model, request }, this.generateInner(request));
83
- }
84
- /** Runs inside the span: the adapter's setLlmSpanAttribute calls land on it. */
85
- async *generateInner(request) {
86
- for await (const response of this.adapter.generate(request)) {
87
- yield this.toLlmResponse(response);
88
- }
89
- }
90
- /**
91
- * The ModelRequest one call hands the adapter: `llmRequestToModelRequest`.
92
- * A provider's ADK class may extend it with what its adapter reads on the
93
- * ADK path only, an agent setting the contract leaves out: ClaudeLlm's
94
- * older reasoning spelling (ADR 0055), and the chat-completions shims'
95
- * older generateContentConfig spelling (ADR 0057). It never removes or
96
- * rewrites a contract field. The span records what it returns.
97
- */
98
- toModelRequest(llmRequest, options) {
99
- return llmRequestToModelRequest(llmRequest, options);
100
- }
101
- /**
102
- * One ModelResponse as the LlmResponse ADK sees: modelResponseToLlmResponse.
103
- * It runs inside the llm.request span, so the tracer reads what it returns.
104
- * A subclass that stands in for an ADK-path adapter overrides it to keep
105
- * what that adapter wrote beyond the contract (GptLlm, ADR 0056; the
106
- * chat-completions shims, ADR 0057).
107
- */
108
- toLlmResponse(response) {
109
- return modelResponseToLlmResponse(response);
110
- }
111
- /** Live/bidirectional connections are outside the model contract. Throws to say so. */
112
- async connect(_llmRequest) {
113
- throw new Error(`${this.model} runs through the ADK shim, which does not support live bidirectional connections ` +
114
- '(they are outside the model contract). Use a Gemini model for live/streaming sessions.');
115
- }
116
- }
117
- /** A shim around `adapter`, under `model` (default the adapter's own id). */
118
- export function adkShim(adapter, model = adapter.model) {
119
- return new AdkShim(adapter, { model });
120
- }
121
- /**
122
- * A shim class ADK's LLMRegistry can register: it answers for
123
- * `supportedModels` and builds its adapter with `createAdapter(model)` when
124
- * ADK constructs it for a model id. Pass the direct adapter's own pattern
125
- * instances, since the registry keys on the pattern object (see
126
- * lib/models/gatewayLlm.ts), so the shim replaces that entry rather than
127
- * shadowing it.
128
- */
129
- export function adkShimClass(supportedModels, createAdapter) {
130
- return class AdkShimFor extends AdkShim {
131
- static supportedModels = supportedModels;
132
- constructor({ model }) {
133
- super(createAdapter(model), { model });
134
- }
135
- };
136
- }