@genesislcap/foundation-ai 14.494.0 → 14.496.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/dist/dts/ai-provider-di.d.ts +136 -0
- package/dist/dts/ai-provider-di.d.ts.map +1 -0
- package/dist/dts/ai-provider.d.ts +1 -112
- package/dist/dts/ai-provider.d.ts.map +1 -1
- package/dist/dts/index.d.ts +5 -3
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/transports/anthropic-transport.d.ts.map +1 -1
- package/dist/dts/transports/gemini-transport.d.ts.map +1 -1
- package/dist/dts/types/chat.types.d.ts +63 -0
- package/dist/dts/types/chat.types.d.ts.map +1 -1
- package/dist/dts/types/config.types.d.ts +1 -1
- package/dist/dts/types/config.types.d.ts.map +1 -1
- package/dist/esm/ai-provider-di.js +84 -0
- package/dist/esm/ai-provider.js +0 -83
- package/dist/esm/index.js +6 -1
- package/dist/esm/transports/anthropic-transport.js +99 -31
- package/dist/esm/transports/gemini-transport.js +12 -1
- package/dist/esm/types/config.types.js +2 -0
- package/dist/foundation-ai.api.json +174 -8
- package/dist/foundation-ai.d.ts +66 -1
- package/package.json +11 -11
package/dist/esm/ai-provider.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
|
-
import { DI, Registration } from '@microsoft/fast-foundation';
|
|
3
2
|
import { AnthropicProvider } from './providers/anthropic-provider';
|
|
4
3
|
import { ChromeProvider } from './providers/chrome-provider';
|
|
5
4
|
import { DefaultAIProvider } from './providers/default-provider';
|
|
@@ -300,85 +299,3 @@ export class MutableAIProviderRegistry {
|
|
|
300
299
|
listener();
|
|
301
300
|
}
|
|
302
301
|
}
|
|
303
|
-
/**
|
|
304
|
-
* The DI token for the {@link (AIProviderRegistry:interface)}. When no host
|
|
305
|
-
* registers a concrete registry, the container resolves the built-in empty
|
|
306
|
-
* registry (a single no-op provider) so consumers degrade to inert rather
|
|
307
|
-
* than throwing.
|
|
308
|
-
*
|
|
309
|
-
* Prefer the {@link registerAIProviders} helper over registering this token
|
|
310
|
-
* directly.
|
|
311
|
-
*
|
|
312
|
-
* @beta
|
|
313
|
-
*/
|
|
314
|
-
export const AIProviderRegistry = DI.createInterface((x) => x.singleton(EmptyAIProviderRegistry));
|
|
315
|
-
/**
|
|
316
|
-
* Registers one or more named AI providers as an {@link (AIProviderRegistry:interface)}
|
|
317
|
-
* on the given DI container.
|
|
318
|
-
*
|
|
319
|
-
* @remarks
|
|
320
|
-
* - With a single provider, the default is inferred — `options.default` may be omitted.
|
|
321
|
-
* - With multiple providers, `options.default` is required to avoid implicit ordering.
|
|
322
|
-
* - Throws when `providers` is empty, when the named default isn't present, or
|
|
323
|
-
* when multiple providers are passed without an explicit default.
|
|
324
|
-
*
|
|
325
|
-
* @example
|
|
326
|
-
* ```ts
|
|
327
|
-
* // Single provider — default inferred
|
|
328
|
-
* registerAIProviders(container, { openai: createAIProvider(openAiConfig) });
|
|
329
|
-
*
|
|
330
|
-
* // Multiple providers — explicit default
|
|
331
|
-
* registerAIProviders(
|
|
332
|
-
* container,
|
|
333
|
-
* { fast: chromeProvider, deep: anthropicProvider },
|
|
334
|
-
* { default: 'deep' },
|
|
335
|
-
* );
|
|
336
|
-
* ```
|
|
337
|
-
*
|
|
338
|
-
* @returns the constructed {@link MutableAIProviderRegistry}, so a host that
|
|
339
|
-
* wants to switch providers at runtime can keep the handle and call
|
|
340
|
-
* {@link MutableAIProviderRegistry.set | set} /
|
|
341
|
-
* {@link MutableAIProviderRegistry.setDefault | setDefault} /
|
|
342
|
-
* {@link MutableAIProviderRegistry.update | update} on it later. Callers that
|
|
343
|
-
* register once and never switch can ignore the return value.
|
|
344
|
-
*
|
|
345
|
-
* @beta
|
|
346
|
-
*/
|
|
347
|
-
export function registerAIProviders(container, providers, options = {}) {
|
|
348
|
-
const entries = Object.entries(providers);
|
|
349
|
-
if (entries.length === 0) {
|
|
350
|
-
throw new Error('registerAIProviders: at least one provider is required.');
|
|
351
|
-
}
|
|
352
|
-
let defaultName;
|
|
353
|
-
if (options.default !== undefined) {
|
|
354
|
-
if (!Object.prototype.hasOwnProperty.call(providers, options.default)) {
|
|
355
|
-
throw new Error(`registerAIProviders: default "${options.default}" is not one of the registered providers (${entries.map(([k]) => k).join(', ')}).`);
|
|
356
|
-
}
|
|
357
|
-
defaultName = options.default;
|
|
358
|
-
}
|
|
359
|
-
else if (entries.length === 1) {
|
|
360
|
-
defaultName = entries[0][0];
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
throw new Error(`registerAIProviders: multiple providers registered (${entries.map(([k]) => k).join(', ')}) — must specify { default } to disambiguate.`);
|
|
364
|
-
}
|
|
365
|
-
const registry = new MutableAIProviderRegistry(new Map(entries), defaultName);
|
|
366
|
-
registerAIProviderRegistry(container, registry);
|
|
367
|
-
return registry;
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Registers a host-supplied {@link (AIProviderRegistry:interface)} instance on
|
|
371
|
-
* the DI container under the {@link (AIProviderRegistry:variable)} token.
|
|
372
|
-
*
|
|
373
|
-
* @remarks
|
|
374
|
-
* A thin wrapper over FAST's `Registration.instance` so a host can register its
|
|
375
|
-
* own pre-built registry — typically a {@link MutableAIProviderRegistry} it
|
|
376
|
-
* owns and mutates for runtime provider switching — **without importing FAST
|
|
377
|
-
* primitives** itself. {@link registerAIProviders} delegates to this; reach for
|
|
378
|
-
* it directly when you construct the registry yourself.
|
|
379
|
-
*
|
|
380
|
-
* @beta
|
|
381
|
-
*/
|
|
382
|
-
export function registerAIProviderRegistry(container, registry) {
|
|
383
|
-
container.register(Registration.instance(AIProviderRegistry, registry));
|
|
384
|
-
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createAIProvider, isObservableAIProviderRegistry, MutableAIProviderRegistry, resolveAIConfig, } from './ai-provider';
|
|
2
|
+
// FAST DI wiring lives in its own module so `@microsoft/fast-foundation` (→ `fast-element`, which
|
|
3
|
+
// touches `document` at eval) is only pulled when a host actually uses the container. Keeps the
|
|
4
|
+
// registry/driver path loadable in bare Node. `AIProviderRegistry` carries both the interface
|
|
5
|
+
// (type) and the DI token (value) — both merged in `ai-provider-di.ts`. See `ai-provider-di.ts`.
|
|
6
|
+
export { AIProviderRegistry, registerAIProviderRegistry, registerAIProviders, } from './ai-provider-di';
|
|
2
7
|
export { AnthropicProvider } from './providers/anthropic-provider';
|
|
3
8
|
export { GeminiProvider } from './providers/gemini-provider';
|
|
4
9
|
export { AnthropicTransport, ResponseTruncatedError } from './transports/anthropic-transport';
|
|
@@ -30,6 +30,8 @@ function toAnthropicToolChoice(choice) {
|
|
|
30
30
|
* Source: https://docs.claude.com/en/docs/about-claude/models/overview
|
|
31
31
|
*/
|
|
32
32
|
const ANTHROPIC_CONTEXT_LIMITS = {
|
|
33
|
+
'claude-fable-5': 1000000,
|
|
34
|
+
'claude-opus-4-8': 1000000,
|
|
33
35
|
'claude-opus-4-7': 1000000,
|
|
34
36
|
'claude-sonnet-5': 1000000,
|
|
35
37
|
'claude-sonnet-4-6': 1000000,
|
|
@@ -41,6 +43,8 @@ const ANTHROPIC_CONTEXT_LIMITS = {
|
|
|
41
43
|
* and surfaces the `input` field as the structured response.
|
|
42
44
|
*/
|
|
43
45
|
const STRUCTURED_OUTPUT_TOOL_NAME = 'emit_structured_response';
|
|
46
|
+
/** Beta flag enabling the server-side `fallbacks` request parameter (refusal rescue). */
|
|
47
|
+
const SERVER_SIDE_FALLBACK_BETA = 'server-side-fallback-2026-06-01';
|
|
44
48
|
function assertSupportedAnthropicModel(model) {
|
|
45
49
|
if (!SUPPORTED_ANTHROPIC_MODEL_IDS.includes(model)) {
|
|
46
50
|
throw new Error(`AnthropicTransport: unsupported model "${model}". Use one of: ${SUPPORTED_ANTHROPIC_MODEL_IDS.join(', ')}.`);
|
|
@@ -53,36 +57,60 @@ function estimatedAnthropicRatesUsdPerMillion(model) {
|
|
|
53
57
|
if (model === 'claude-haiku-4-5-20251001') {
|
|
54
58
|
return { promptPerMillion: 1, candidatePerMillion: 5 };
|
|
55
59
|
}
|
|
60
|
+
// Fable 5 — Anthropic's most capable widely-released model; priced above Opus tier.
|
|
61
|
+
if (model === 'claude-fable-5') {
|
|
62
|
+
return { promptPerMillion: 10, candidatePerMillion: 50 };
|
|
63
|
+
}
|
|
56
64
|
// Sonnet 5 and Sonnet 4.6 share the standard Sonnet tier ($3 / $15 per MTok). Sonnet 5's
|
|
57
65
|
// introductory rate ($2 / $10 through 2026-08-31) is deliberately NOT used here — standard rates.
|
|
58
66
|
if (model === 'claude-sonnet-5' || model === 'claude-sonnet-4-6') {
|
|
59
67
|
return { promptPerMillion: 3, candidatePerMillion: 15 };
|
|
60
68
|
}
|
|
61
|
-
// Opus 4.7
|
|
69
|
+
// Opus 4.7 / 4.8 — same $5 / $25 per MTok.
|
|
62
70
|
return { promptPerMillion: 5, candidatePerMillion: 25 };
|
|
63
71
|
}
|
|
64
72
|
/**
|
|
65
73
|
* Models that reject non-default sampling parameters (`temperature`/`top_p`/`top_k`) with a 400 —
|
|
66
|
-
* the Opus 4.7+ generation. Sonnet 4.6 and Haiku 4.5 still accept them.
|
|
74
|
+
* the Opus 4.7+ / Sonnet 5 / Fable 5 generation. Sonnet 4.6 and Haiku 4.5 still accept them.
|
|
67
75
|
*/
|
|
68
76
|
function rejectsSamplingParams(model) {
|
|
69
|
-
return model === 'claude-
|
|
77
|
+
return (model === 'claude-fable-5' ||
|
|
78
|
+
model === 'claude-opus-4-8' ||
|
|
79
|
+
model === 'claude-opus-4-7' ||
|
|
80
|
+
model === 'claude-sonnet-5');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Whether the model enforces structured output natively via decode-time `output_config.format`
|
|
84
|
+
* (json_schema). True for Fable 5, Opus 4.8, Sonnet 5, and Haiku 4.5; false for Opus 4.7 and
|
|
85
|
+
* Sonnet 4.6, which fall back to the forced-tool approach on the one-shot path. `output_config`
|
|
86
|
+
* composes with a live `tools` array, so no tool-vs-schema gating is needed on this provider.
|
|
87
|
+
*/
|
|
88
|
+
function supportsNativeStructuredOutput(model) {
|
|
89
|
+
return (model === 'claude-fable-5' ||
|
|
90
|
+
model === 'claude-opus-4-8' ||
|
|
91
|
+
model === 'claude-sonnet-5' ||
|
|
92
|
+
model === 'claude-haiku-4-5-20251001');
|
|
70
93
|
}
|
|
71
94
|
/**
|
|
72
|
-
* Extended-thinking posture for a request. Sonnet 5
|
|
95
|
+
* Extended-thinking posture for a request. Sonnet 5 and Fable 5 run adaptive thinking with
|
|
73
96
|
* `display:'summarized'` — regardless of `tool_choice`. First-party has no forced-tool-vs-thinking
|
|
74
97
|
* restriction (that's Bedrock-only), and "adaptive" self-regulates (the model thinks little on
|
|
75
98
|
* trivial/mechanical turns on its own), so there is no reason to special-case forced tool calls.
|
|
76
99
|
* `display:'summarized'` means the reasoning summary is always returned (billed regardless; the
|
|
77
100
|
* host's toggle decides visibility).
|
|
78
101
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
102
|
+
* Fable 5 runs thinking *unconditionally* (it cannot be disabled — an explicit
|
|
103
|
+
* `{type:'disabled'}` is a 400), so it must never be sent `disabled`; `{type:'adaptive'}`
|
|
104
|
+
* is the accepted posture and is what we return.
|
|
105
|
+
*
|
|
106
|
+
* Every other model (Opus 4.8/4.7, Sonnet 4.6, Haiku) returns undefined → `thinking` is omitted
|
|
107
|
+
* → it runs WITHOUT thinking on the chat path (Opus 4.8 adaptive thinking is opt-in). So the older
|
|
108
|
+
* "forced tool + thinking is incompatible" restriction never applies to them (you can't hit it when
|
|
109
|
+
* thinking is off). If thinking is ever enabled for one of those, revisit the forced-tool
|
|
110
|
+
* interaction for that model then.
|
|
83
111
|
*/
|
|
84
112
|
function anthropicThinking(model) {
|
|
85
|
-
if (model !== 'claude-sonnet-5')
|
|
113
|
+
if (model !== 'claude-sonnet-5' && model !== 'claude-fable-5')
|
|
86
114
|
return undefined;
|
|
87
115
|
return { type: 'adaptive', display: 'summarized' };
|
|
88
116
|
}
|
|
@@ -178,8 +206,11 @@ export class AnthropicTransport {
|
|
|
178
206
|
else if (model === 'claude-sonnet-4-6') {
|
|
179
207
|
logger.warn('AnthropicTransport: using claude-sonnet-4-6 — higher cost than Haiku; use for stronger reasoning or agent tasks.');
|
|
180
208
|
}
|
|
181
|
-
else if (model === 'claude-opus-4-7') {
|
|
182
|
-
logger.warn(
|
|
209
|
+
else if (model === 'claude-opus-4-7' || model === 'claude-opus-4-8') {
|
|
210
|
+
logger.warn(`AnthropicTransport: using ${model} — significantly higher cost; reserve for tasks where Sonnet reliability is insufficient.`);
|
|
211
|
+
}
|
|
212
|
+
else if (model === 'claude-fable-5') {
|
|
213
|
+
logger.warn('AnthropicTransport: using claude-fable-5 — Anthropic\'s most capable model, priced above Opus tier ($10 / $50 per MTok) with thinking always on. Reserve for the hardest reasoning/long-horizon work. Requires ≥30-day data retention (unavailable under ZDR) and may return stop_reason "refusal"; pair with an Opus 4.8 fallback.');
|
|
183
214
|
}
|
|
184
215
|
this.timeout = (_b = config.timeout) !== null && _b !== void 0 ? _b : DEFAULT_TIMEOUT;
|
|
185
216
|
this.stallTimeout = (_c = config.stallTimeout) !== null && _c !== void 0 ? _c : DEFAULT_STALL_TIMEOUT;
|
|
@@ -213,19 +244,6 @@ export class AnthropicTransport {
|
|
|
213
244
|
var _a, _b, _c;
|
|
214
245
|
const { systemPrompt, userPrompt, responseSchema } = options;
|
|
215
246
|
const messages = [{ role: 'user', content: userPrompt }];
|
|
216
|
-
// Anthropic has no native JSON-schema response format. The supported pattern
|
|
217
|
-
// is to define a tool whose input_schema is the desired schema, then force
|
|
218
|
-
// the model to call it via tool_choice. The tool's `input` is the structured
|
|
219
|
-
// payload we surface back to the caller as a JSON string.
|
|
220
|
-
const tools = responseSchema
|
|
221
|
-
? [
|
|
222
|
-
{
|
|
223
|
-
name: STRUCTURED_OUTPUT_TOOL_NAME,
|
|
224
|
-
description: 'Emit the structured response that matches the required schema.',
|
|
225
|
-
input_schema: responseSchema,
|
|
226
|
-
},
|
|
227
|
-
]
|
|
228
|
-
: undefined;
|
|
229
247
|
const body = {
|
|
230
248
|
model: this.model,
|
|
231
249
|
max_tokens: this.maxTokens,
|
|
@@ -233,19 +251,38 @@ export class AnthropicTransport {
|
|
|
233
251
|
};
|
|
234
252
|
if (systemPrompt)
|
|
235
253
|
body.system = systemPrompt;
|
|
236
|
-
|
|
237
|
-
|
|
254
|
+
// Prefer native decode-time enforcement (`output_config.format`) where the model supports it —
|
|
255
|
+
// the answer comes back as ordinary text conforming to the schema. On models without native
|
|
256
|
+
// support (Opus 4.7, Sonnet 4.6), fall back to the legacy pattern: a tool whose `input_schema`
|
|
257
|
+
// is the schema, forced via `tool_choice`, with the structured payload read from its `tool_use`.
|
|
258
|
+
const useNative = responseSchema != null && supportsNativeStructuredOutput(this.model);
|
|
259
|
+
const useForcedTool = responseSchema != null && !useNative;
|
|
260
|
+
if (useNative) {
|
|
261
|
+
body.output_config = {
|
|
262
|
+
format: { type: 'json_schema', schema: responseSchema },
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
else if (useForcedTool) {
|
|
266
|
+
body.tools = [
|
|
267
|
+
{
|
|
268
|
+
name: STRUCTURED_OUTPUT_TOOL_NAME,
|
|
269
|
+
description: 'Emit the structured response that matches the required schema.',
|
|
270
|
+
input_schema: responseSchema,
|
|
271
|
+
},
|
|
272
|
+
];
|
|
238
273
|
body.tool_choice = { type: 'tool', name: STRUCTURED_OUTPUT_TOOL_NAME };
|
|
239
274
|
}
|
|
240
|
-
// Sonnet 5 runs adaptive thinking by default; disable it for
|
|
241
|
-
//
|
|
275
|
+
// Sonnet 5 runs adaptive thinking by default; disable it for one-shot prompts — the reasoning
|
|
276
|
+
// would be billed but discarded. Fable 5 runs thinking unconditionally (cannot be disabled — a
|
|
277
|
+
// 400), so it is left on; its summary is simply unused here.
|
|
242
278
|
if (this.model === 'claude-sonnet-5')
|
|
243
279
|
body.thinking = { type: 'disabled' };
|
|
244
280
|
const response = yield this.post(body);
|
|
245
|
-
if (
|
|
281
|
+
if (useForcedTool) {
|
|
246
282
|
const toolUse = ((_a = response.content) !== null && _a !== void 0 ? _a : []).find((b) => b.type === 'tool_use' && b.name === STRUCTURED_OUTPUT_TOOL_NAME);
|
|
247
283
|
return toolUse ? JSON.stringify((_b = toolUse.input) !== null && _b !== void 0 ? _b : {}) : '';
|
|
248
284
|
}
|
|
285
|
+
// Native structured output and plain prompts both return the answer as text.
|
|
249
286
|
return ((_c = response.content) !== null && _c !== void 0 ? _c : [])
|
|
250
287
|
.filter((b) => b.type === 'text')
|
|
251
288
|
.map((b) => b.text)
|
|
@@ -255,7 +292,7 @@ export class AnthropicTransport {
|
|
|
255
292
|
// ── ChatTransport (multi-turn chat) ────────────────────────────────────
|
|
256
293
|
sendChatMessage(history, userMessage, options) {
|
|
257
294
|
return __awaiter(this, void 0, void 0, function* () {
|
|
258
|
-
var _a, _b;
|
|
295
|
+
var _a, _b, _c;
|
|
259
296
|
const messages = this.toAnthropicMessages(history, userMessage, options === null || options === void 0 ? void 0 : options.attachments);
|
|
260
297
|
const body = {
|
|
261
298
|
model: this.model,
|
|
@@ -294,6 +331,22 @@ export class AnthropicTransport {
|
|
|
294
331
|
maxTemp: ANTHROPIC_MAX_TEMPERATURE,
|
|
295
332
|
});
|
|
296
333
|
}
|
|
334
|
+
// Structured output: constrain the final text answer to the caller's JSON schema via
|
|
335
|
+
// decode-time `output_config.format`. Composes with a live `tools` array — the model still
|
|
336
|
+
// calls tools through the turn and conforms its closing answer to the schema. Applied only on
|
|
337
|
+
// models that support it natively; elsewhere the schema is dropped here (the caller keeps a
|
|
338
|
+
// prompt-instruction + validator fallback). No beta header needed.
|
|
339
|
+
if ((options === null || options === void 0 ? void 0 : options.responseSchema) && supportsNativeStructuredOutput(this.model)) {
|
|
340
|
+
body.output_config = {
|
|
341
|
+
format: { type: 'json_schema', schema: options.responseSchema },
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
// Refusal fallback chain (e.g. Fable 5 → Opus 4.8). Sent as the server-side `fallbacks`
|
|
345
|
+
// param; `post` adds the required beta header when this is present. A refused turn is
|
|
346
|
+
// re-run on the next model in one round trip.
|
|
347
|
+
if ((_c = options === null || options === void 0 ? void 0 : options.fallbacks) === null || _c === void 0 ? void 0 : _c.length) {
|
|
348
|
+
body.fallbacks = options.fallbacks.map((f) => f.maxTokens != null ? { model: f.model, max_tokens: f.maxTokens } : { model: f.model });
|
|
349
|
+
}
|
|
297
350
|
// Place prompt-cache breakpoints per the resolved policy (no-op for `'default'`/absent).
|
|
298
351
|
if (options === null || options === void 0 ? void 0 : options.cachePolicy) {
|
|
299
352
|
this.applyCacheControl(body, options.cachePolicy);
|
|
@@ -553,6 +606,14 @@ export class AnthropicTransport {
|
|
|
553
606
|
if (response.stop_reason === 'max_tokens') {
|
|
554
607
|
base.responseMeta = { finishReason: 'max_tokens' };
|
|
555
608
|
}
|
|
609
|
+
// A `refusal` stop means safety classifiers declined the request (Fable 5 and other
|
|
610
|
+
// recent models). Content is empty (pre-output) or partial (mid-stream) — return it
|
|
611
|
+
// rather than crash on an assumed non-empty block, and flag the reason so the driver
|
|
612
|
+
// (or a server-side `fallbacks` chain) can respond deterministically instead of
|
|
613
|
+
// retrying into the same wall. Usage/cost is already logged above.
|
|
614
|
+
if (response.stop_reason === 'refusal') {
|
|
615
|
+
base.responseMeta = { finishReason: 'refusal' };
|
|
616
|
+
}
|
|
556
617
|
return base;
|
|
557
618
|
}
|
|
558
619
|
buildEndpoint() {
|
|
@@ -584,10 +645,17 @@ export class AnthropicTransport {
|
|
|
584
645
|
}
|
|
585
646
|
post(body, signal) {
|
|
586
647
|
return __awaiter(this, void 0, void 0, function* () {
|
|
648
|
+
var _a;
|
|
587
649
|
const { url, headers, credentials } = this.buildEndpoint();
|
|
650
|
+
// The server-side `fallbacks` param is gated behind a beta flag; add it per-request only
|
|
651
|
+
// when a fallback chain is set (the param without the header is a 400, and vice-versa is fine).
|
|
652
|
+
const requestHeaders = ((_a = body.fallbacks) === null || _a === void 0 ? void 0 : _a.length)
|
|
653
|
+
? Object.assign(Object.assign({}, headers), { 'anthropic-beta': [headers['anthropic-beta'], SERVER_SIDE_FALLBACK_BETA]
|
|
654
|
+
.filter(Boolean)
|
|
655
|
+
.join(',') }) : headers;
|
|
588
656
|
return (yield postWithRetry({
|
|
589
657
|
url,
|
|
590
|
-
headers,
|
|
658
|
+
headers: requestHeaders,
|
|
591
659
|
body,
|
|
592
660
|
credentials,
|
|
593
661
|
vendorLabel: 'Anthropic',
|
|
@@ -258,7 +258,18 @@ export class GeminiTransport {
|
|
|
258
258
|
// Names of the tools offered this turn — used to validate a repaired
|
|
259
259
|
// malformed call against the real tool surface before accepting it.
|
|
260
260
|
const offeredToolNames = new Set(((_b = options === null || options === void 0 ? void 0 : options.tools) !== null && _b !== void 0 ? _b : []).map((t) => t.name));
|
|
261
|
-
|
|
261
|
+
// Structured output (native JSON mode). Gemini 3 can combine it with function calling;
|
|
262
|
+
// Gemini 2.x cannot, so on 2.x apply the schema only on a pure structured turn (no tools) and
|
|
263
|
+
// otherwise drop it (the caller keeps a prompt-instruction + validator fallback). Passed as a
|
|
264
|
+
// top-level `responseSchema` that `toDirectPayload` maps to `generationConfig.responseMimeType`
|
|
265
|
+
// + `responseSchema` on the direct path (the proxy forwards it).
|
|
266
|
+
const schemaCoexistsWithTools = this.model.startsWith('gemini-3');
|
|
267
|
+
const applyResponseSchema = (options === null || options === void 0 ? void 0 : options.responseSchema) != null && (!tools || schemaCoexistsWithTools);
|
|
268
|
+
const response = yield this.post(Object.assign({ model: this.model, contents,
|
|
269
|
+
tools,
|
|
270
|
+
systemInstruction,
|
|
271
|
+
toolConfig,
|
|
272
|
+
generationConfig }, (applyResponseSchema ? { responseSchema: options.responseSchema } : {})), options === null || options === void 0 ? void 0 : options.signal);
|
|
262
273
|
return this.fromGeminiResponse(response, offeredToolNames);
|
|
263
274
|
});
|
|
264
275
|
}
|
|
@@ -719,7 +719,7 @@
|
|
|
719
719
|
"text": "export interface AIProviderRegistry "
|
|
720
720
|
}
|
|
721
721
|
],
|
|
722
|
-
"fileUrlPath": "src/ai-provider.ts",
|
|
722
|
+
"fileUrlPath": "src/ai-provider-di.ts",
|
|
723
723
|
"releaseTag": "Beta",
|
|
724
724
|
"name": "AIProviderRegistry",
|
|
725
725
|
"preserveMemberOrder": false,
|
|
@@ -995,7 +995,7 @@
|
|
|
995
995
|
"text": ">"
|
|
996
996
|
}
|
|
997
997
|
],
|
|
998
|
-
"fileUrlPath": "src/ai-provider.ts",
|
|
998
|
+
"fileUrlPath": "src/ai-provider-di.ts",
|
|
999
999
|
"isReadonly": true,
|
|
1000
1000
|
"releaseTag": "Beta",
|
|
1001
1001
|
"name": "AIProviderRegistry",
|
|
@@ -1441,7 +1441,7 @@
|
|
|
1441
1441
|
},
|
|
1442
1442
|
{
|
|
1443
1443
|
"kind": "Content",
|
|
1444
|
-
"text": "'claude-opus-4-7' | 'claude-sonnet-5' | 'claude-sonnet-4-6' | 'claude-haiku-4-5-20251001'"
|
|
1444
|
+
"text": "'claude-fable-5' | 'claude-opus-4-8' | 'claude-opus-4-7' | 'claude-sonnet-5' | 'claude-sonnet-4-6' | 'claude-haiku-4-5-20251001'"
|
|
1445
1445
|
},
|
|
1446
1446
|
{
|
|
1447
1447
|
"kind": "Content",
|
|
@@ -3138,7 +3138,16 @@
|
|
|
3138
3138
|
},
|
|
3139
3139
|
{
|
|
3140
3140
|
"kind": "Content",
|
|
3141
|
-
"text": "{\n reason: 'done';\n
|
|
3141
|
+
"text": "{\n reason: 'done';\n failureReason?: "
|
|
3142
|
+
},
|
|
3143
|
+
{
|
|
3144
|
+
"kind": "Reference",
|
|
3145
|
+
"text": "TurnFailureReason",
|
|
3146
|
+
"canonicalReference": "@genesislcap/foundation-ai!TurnFailureReason:type"
|
|
3147
|
+
},
|
|
3148
|
+
{
|
|
3149
|
+
"kind": "Content",
|
|
3150
|
+
"text": ";\n} | {\n reason: 'agent-handoff';\n summary: string;\n remainingTask: string;\n}"
|
|
3142
3151
|
},
|
|
3143
3152
|
{
|
|
3144
3153
|
"kind": "Content",
|
|
@@ -3150,9 +3159,81 @@
|
|
|
3150
3159
|
"name": "ChatDriverResult",
|
|
3151
3160
|
"typeTokenRange": {
|
|
3152
3161
|
"startIndex": 1,
|
|
3153
|
-
"endIndex":
|
|
3162
|
+
"endIndex": 4
|
|
3154
3163
|
}
|
|
3155
3164
|
},
|
|
3165
|
+
{
|
|
3166
|
+
"kind": "Interface",
|
|
3167
|
+
"canonicalReference": "@genesislcap/foundation-ai!ChatFallback:interface",
|
|
3168
|
+
"docComment": "/**\n * One entry in a {@link ChatRequestOptions.fallbacks} chain.\n *\n * @beta\n */\n",
|
|
3169
|
+
"excerptTokens": [
|
|
3170
|
+
{
|
|
3171
|
+
"kind": "Content",
|
|
3172
|
+
"text": "export interface ChatFallback "
|
|
3173
|
+
}
|
|
3174
|
+
],
|
|
3175
|
+
"fileUrlPath": "src/types/chat.types.ts",
|
|
3176
|
+
"releaseTag": "Beta",
|
|
3177
|
+
"name": "ChatFallback",
|
|
3178
|
+
"preserveMemberOrder": false,
|
|
3179
|
+
"members": [
|
|
3180
|
+
{
|
|
3181
|
+
"kind": "PropertySignature",
|
|
3182
|
+
"canonicalReference": "@genesislcap/foundation-ai!ChatFallback#maxTokens:member",
|
|
3183
|
+
"docComment": "/**\n * Optional per-hop `max_tokens` cap for this fallback attempt.\n */\n",
|
|
3184
|
+
"excerptTokens": [
|
|
3185
|
+
{
|
|
3186
|
+
"kind": "Content",
|
|
3187
|
+
"text": "maxTokens?: "
|
|
3188
|
+
},
|
|
3189
|
+
{
|
|
3190
|
+
"kind": "Content",
|
|
3191
|
+
"text": "number"
|
|
3192
|
+
},
|
|
3193
|
+
{
|
|
3194
|
+
"kind": "Content",
|
|
3195
|
+
"text": ";"
|
|
3196
|
+
}
|
|
3197
|
+
],
|
|
3198
|
+
"isReadonly": false,
|
|
3199
|
+
"isOptional": true,
|
|
3200
|
+
"releaseTag": "Beta",
|
|
3201
|
+
"name": "maxTokens",
|
|
3202
|
+
"propertyTypeTokenRange": {
|
|
3203
|
+
"startIndex": 1,
|
|
3204
|
+
"endIndex": 2
|
|
3205
|
+
}
|
|
3206
|
+
},
|
|
3207
|
+
{
|
|
3208
|
+
"kind": "PropertySignature",
|
|
3209
|
+
"canonicalReference": "@genesislcap/foundation-ai!ChatFallback#model:member",
|
|
3210
|
+
"docComment": "/**\n * Model id to fall back to (e.g. `'claude-opus-4-8'`).\n */\n",
|
|
3211
|
+
"excerptTokens": [
|
|
3212
|
+
{
|
|
3213
|
+
"kind": "Content",
|
|
3214
|
+
"text": "model: "
|
|
3215
|
+
},
|
|
3216
|
+
{
|
|
3217
|
+
"kind": "Content",
|
|
3218
|
+
"text": "string"
|
|
3219
|
+
},
|
|
3220
|
+
{
|
|
3221
|
+
"kind": "Content",
|
|
3222
|
+
"text": ";"
|
|
3223
|
+
}
|
|
3224
|
+
],
|
|
3225
|
+
"isReadonly": false,
|
|
3226
|
+
"isOptional": false,
|
|
3227
|
+
"releaseTag": "Beta",
|
|
3228
|
+
"name": "model",
|
|
3229
|
+
"propertyTypeTokenRange": {
|
|
3230
|
+
"startIndex": 1,
|
|
3231
|
+
"endIndex": 2
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
],
|
|
3235
|
+
"extendsTokenRanges": []
|
|
3236
|
+
},
|
|
3156
3237
|
{
|
|
3157
3238
|
"kind": "TypeAlias",
|
|
3158
3239
|
"canonicalReference": "@genesislcap/foundation-ai!ChatInputDuringExecutionMode:type",
|
|
@@ -4014,6 +4095,65 @@
|
|
|
4014
4095
|
"endIndex": 2
|
|
4015
4096
|
}
|
|
4016
4097
|
},
|
|
4098
|
+
{
|
|
4099
|
+
"kind": "PropertySignature",
|
|
4100
|
+
"canonicalReference": "@genesislcap/foundation-ai!ChatRequestOptions#fallbacks:member",
|
|
4101
|
+
"docComment": "/**\n * Provider-neutral refusal-fallback chain: if the model declines the request (`stop_reason: 'refusal'`, e.g. Fable 5 safety classifiers), the provider re-runs the same request on the next listed model and returns its answer. Ordered most- to least-preferred; each entry may cap its own `maxTokens`. Providers that support it apply it server-side (Anthropic: `fallbacks` + the `server-side-fallback` beta); others ignore it. Typical use: Fable 5 with an Opus 4.8 fallback.\n *\n * @beta\n */\n",
|
|
4102
|
+
"excerptTokens": [
|
|
4103
|
+
{
|
|
4104
|
+
"kind": "Content",
|
|
4105
|
+
"text": "fallbacks?: "
|
|
4106
|
+
},
|
|
4107
|
+
{
|
|
4108
|
+
"kind": "Reference",
|
|
4109
|
+
"text": "ChatFallback",
|
|
4110
|
+
"canonicalReference": "@genesislcap/foundation-ai!ChatFallback:interface"
|
|
4111
|
+
},
|
|
4112
|
+
{
|
|
4113
|
+
"kind": "Content",
|
|
4114
|
+
"text": "[]"
|
|
4115
|
+
},
|
|
4116
|
+
{
|
|
4117
|
+
"kind": "Content",
|
|
4118
|
+
"text": ";"
|
|
4119
|
+
}
|
|
4120
|
+
],
|
|
4121
|
+
"isReadonly": false,
|
|
4122
|
+
"isOptional": true,
|
|
4123
|
+
"releaseTag": "Beta",
|
|
4124
|
+
"name": "fallbacks",
|
|
4125
|
+
"propertyTypeTokenRange": {
|
|
4126
|
+
"startIndex": 1,
|
|
4127
|
+
"endIndex": 3
|
|
4128
|
+
}
|
|
4129
|
+
},
|
|
4130
|
+
{
|
|
4131
|
+
"kind": "PropertySignature",
|
|
4132
|
+
"canonicalReference": "@genesislcap/foundation-ai!ChatRequestOptions#responseSchema:member",
|
|
4133
|
+
"docComment": "/**\n * Structured-output schema (JSON Schema) for this turn. When set, the model's final (non-tool) answer is constrained to it instead of free text. Composes with `tools` — the model may still call tools this turn, then conform its closing answer to the schema. Providers apply it natively where the model supports it (Anthropic `output_config.format`, Gemini JSON mode) and drop it otherwise (caller keeps a prompt-instruction + validator fallback). Keep to the portable JSON-Schema subset providers share (`additionalProperties: false`, explicit `required`, enums, `anyOf` for nullables — no numeric/string constraints or recursion).\n *\n * @beta\n */\n",
|
|
4134
|
+
"excerptTokens": [
|
|
4135
|
+
{
|
|
4136
|
+
"kind": "Content",
|
|
4137
|
+
"text": "responseSchema?: "
|
|
4138
|
+
},
|
|
4139
|
+
{
|
|
4140
|
+
"kind": "Content",
|
|
4141
|
+
"text": "object"
|
|
4142
|
+
},
|
|
4143
|
+
{
|
|
4144
|
+
"kind": "Content",
|
|
4145
|
+
"text": ";"
|
|
4146
|
+
}
|
|
4147
|
+
],
|
|
4148
|
+
"isReadonly": false,
|
|
4149
|
+
"isOptional": true,
|
|
4150
|
+
"releaseTag": "Beta",
|
|
4151
|
+
"name": "responseSchema",
|
|
4152
|
+
"propertyTypeTokenRange": {
|
|
4153
|
+
"startIndex": 1,
|
|
4154
|
+
"endIndex": 2
|
|
4155
|
+
}
|
|
4156
|
+
},
|
|
4017
4157
|
{
|
|
4018
4158
|
"kind": "PropertySignature",
|
|
4019
4159
|
"canonicalReference": "@genesislcap/foundation-ai!ChatRequestOptions#signal:member",
|
|
@@ -8669,7 +8809,7 @@
|
|
|
8669
8809
|
"text": ";"
|
|
8670
8810
|
}
|
|
8671
8811
|
],
|
|
8672
|
-
"fileUrlPath": "src/ai-provider.ts",
|
|
8812
|
+
"fileUrlPath": "src/ai-provider-di.ts",
|
|
8673
8813
|
"returnTypeTokenRange": {
|
|
8674
8814
|
"startIndex": 5,
|
|
8675
8815
|
"endIndex": 6
|
|
@@ -8755,7 +8895,7 @@
|
|
|
8755
8895
|
"text": ";"
|
|
8756
8896
|
}
|
|
8757
8897
|
],
|
|
8758
|
-
"fileUrlPath": "src/ai-provider.ts",
|
|
8898
|
+
"fileUrlPath": "src/ai-provider-di.ts",
|
|
8759
8899
|
"returnTypeTokenRange": {
|
|
8760
8900
|
"startIndex": 10,
|
|
8761
8901
|
"endIndex": 11
|
|
@@ -8800,7 +8940,7 @@
|
|
|
8800
8940
|
"text": "export interface RegisterAIProvidersOptions "
|
|
8801
8941
|
}
|
|
8802
8942
|
],
|
|
8803
|
-
"fileUrlPath": "src/ai-provider.ts",
|
|
8943
|
+
"fileUrlPath": "src/ai-provider-di.ts",
|
|
8804
8944
|
"releaseTag": "Beta",
|
|
8805
8945
|
"name": "RegisterAIProvidersOptions",
|
|
8806
8946
|
"preserveMemberOrder": false,
|
|
@@ -9597,6 +9737,32 @@
|
|
|
9597
9737
|
"startIndex": 1,
|
|
9598
9738
|
"endIndex": 4
|
|
9599
9739
|
}
|
|
9740
|
+
},
|
|
9741
|
+
{
|
|
9742
|
+
"kind": "TypeAlias",
|
|
9743
|
+
"canonicalReference": "@genesislcap/foundation-ai!TurnFailureReason:type",
|
|
9744
|
+
"docComment": "/**\n * Why a driver turn ended in failure — the typed taxonomy the tool loop already records onto its debug-log timeline (`turn.error` / `turn.retry` details), surfaced here so callers of a turn can read the outcome structurally instead of scraping prose. Distinct from {@link SubAgentFailureReason} (underscored, its own `timeout` member) because the two enums serialise into different log surfaces and READMEs; a transport request timeout on the main turn is recorded as `exception`.\n *\n * - `exception` — an uncaught error escaped the tool loop (catch-all, includes a transport request timeout). - `malformed-function-call` — the provider returned an unparseable tool call. - `empty-response` — the model returned no content and no tool calls. - `unknown-tool-limit` — the model repeatedly called tools it couldn't dispatch, whether hallucinated or stale (real earlier, retired now). - `max-iterations` — the tool loop hit its iteration cap. - `response-truncated` — a turn stopped at the provider's output-token cap with an incomplete tool call; deterministic, so it bails without retry.\n *\n * @beta\n */\n",
|
|
9745
|
+
"excerptTokens": [
|
|
9746
|
+
{
|
|
9747
|
+
"kind": "Content",
|
|
9748
|
+
"text": "export type TurnFailureReason = "
|
|
9749
|
+
},
|
|
9750
|
+
{
|
|
9751
|
+
"kind": "Content",
|
|
9752
|
+
"text": "'exception' | 'malformed-function-call' | 'empty-response' | 'unknown-tool-limit' | 'max-iterations' | 'response-truncated'"
|
|
9753
|
+
},
|
|
9754
|
+
{
|
|
9755
|
+
"kind": "Content",
|
|
9756
|
+
"text": ";"
|
|
9757
|
+
}
|
|
9758
|
+
],
|
|
9759
|
+
"fileUrlPath": "src/types/chat.types.ts",
|
|
9760
|
+
"releaseTag": "Beta",
|
|
9761
|
+
"name": "TurnFailureReason",
|
|
9762
|
+
"typeTokenRange": {
|
|
9763
|
+
"startIndex": 1,
|
|
9764
|
+
"endIndex": 2
|
|
9765
|
+
}
|
|
9600
9766
|
}
|
|
9601
9767
|
]
|
|
9602
9768
|
}
|