@fgv/ts-extras 5.1.0-52 → 5.1.0-54
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/packlets/ai-assist/completionClient.js +147 -19
- package/dist/packlets/ai-assist/completionClient.js.map +1 -1
- package/dist/packlets/ai-assist/index.js +2 -1
- package/dist/packlets/ai-assist/index.js.map +1 -1
- package/dist/packlets/ai-assist/jsonCompletion.js +20 -2
- package/dist/packlets/ai-assist/jsonCompletion.js.map +1 -1
- package/dist/packlets/ai-assist/model.js.map +1 -1
- package/dist/packlets/ai-assist/registry.js +39 -1
- package/dist/packlets/ai-assist/registry.js.map +1 -1
- package/dist/packlets/ai-assist/structuredOutput.js +315 -0
- package/dist/packlets/ai-assist/structuredOutput.js.map +1 -0
- package/dist/packlets/ai-assist/structuredOutputTypes.js +21 -0
- package/dist/packlets/ai-assist/structuredOutputTypes.js.map +1 -0
- package/dist/packlets/ai-assist/toolFormats.js +36 -0
- package/dist/packlets/ai-assist/toolFormats.js.map +1 -1
- package/dist/ts-extras.d.ts +229 -2
- package/lib/packlets/ai-assist/completionClient.d.ts +13 -0
- package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -1
- package/lib/packlets/ai-assist/completionClient.js +146 -18
- package/lib/packlets/ai-assist/completionClient.js.map +1 -1
- package/lib/packlets/ai-assist/index.d.ts +3 -1
- package/lib/packlets/ai-assist/index.d.ts.map +1 -1
- package/lib/packlets/ai-assist/index.js +6 -2
- package/lib/packlets/ai-assist/index.js.map +1 -1
- package/lib/packlets/ai-assist/jsonCompletion.d.ts.map +1 -1
- package/lib/packlets/ai-assist/jsonCompletion.js +20 -2
- package/lib/packlets/ai-assist/jsonCompletion.js.map +1 -1
- package/lib/packlets/ai-assist/model.d.ts +38 -2
- package/lib/packlets/ai-assist/model.d.ts.map +1 -1
- package/lib/packlets/ai-assist/model.js.map +1 -1
- package/lib/packlets/ai-assist/registry.d.ts +20 -0
- package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
- package/lib/packlets/ai-assist/registry.js +41 -1
- package/lib/packlets/ai-assist/registry.js.map +1 -1
- package/lib/packlets/ai-assist/structuredOutput.d.ts +88 -0
- package/lib/packlets/ai-assist/structuredOutput.d.ts.map +1 -0
- package/lib/packlets/ai-assist/structuredOutput.js +321 -0
- package/lib/packlets/ai-assist/structuredOutput.js.map +1 -0
- package/lib/packlets/ai-assist/structuredOutputTypes.d.ts +142 -0
- package/lib/packlets/ai-assist/structuredOutputTypes.d.ts.map +1 -0
- package/lib/packlets/ai-assist/structuredOutputTypes.js +22 -0
- package/lib/packlets/ai-assist/structuredOutputTypes.js.map +1 -0
- package/lib/packlets/ai-assist/toolFormats.d.ts +0 -23
- package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -1
- package/lib/packlets/ai-assist/toolFormats.js +36 -0
- package/lib/packlets/ai-assist/toolFormats.js.map +1 -1
- package/package.json +7 -7
|
@@ -185,16 +185,52 @@ export function toAnthropicTools(tools) {
|
|
|
185
185
|
*
|
|
186
186
|
* @internal
|
|
187
187
|
*/
|
|
188
|
+
/**
|
|
189
|
+
* The non-`null` member of a draft-07 nullable `type` union, or `undefined` when `type` is
|
|
190
|
+
* not one.
|
|
191
|
+
*
|
|
192
|
+
* @remarks
|
|
193
|
+
* Deliberately narrow: only the two-member `[<type>, 'null']` shape `JsonSchema` emits is
|
|
194
|
+
* recognised. A general union has no OpenAPI equivalent, so translating one would be
|
|
195
|
+
* inventing a meaning — it is passed through unchanged and Gemini refuses it, which is the
|
|
196
|
+
* honest outcome.
|
|
197
|
+
* @internal
|
|
198
|
+
*/
|
|
199
|
+
function _nullableUnionMember(type) {
|
|
200
|
+
if (!Array.isArray(type) || type.length !== 2 || !type.includes('null')) {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
const other = type.find((member) => member !== 'null');
|
|
204
|
+
return typeof other === 'string' ? other : undefined;
|
|
205
|
+
}
|
|
188
206
|
export function toGeminiParameterSchema(schema) {
|
|
189
207
|
if (Array.isArray(schema)) {
|
|
190
208
|
return schema.map(toGeminiParameterSchema);
|
|
191
209
|
}
|
|
192
210
|
if (schema !== null && typeof schema === 'object') {
|
|
193
211
|
const out = {};
|
|
212
|
+
// Nullability is spelled differently in the two dialects and they are mutually
|
|
213
|
+
// exclusive: draft-07 (and OpenAI strict mode) wants `type: ['string', 'null']`,
|
|
214
|
+
// OpenAPI 3.0 (and Gemini) wants `type: 'string'` + `nullable: true` and rejects the
|
|
215
|
+
// union array. This is the same class of translation as the `additionalProperties`
|
|
216
|
+
// strip above — a dialect difference the consumer should not have to know about.
|
|
217
|
+
const nullableType = _nullableUnionMember(schema.type);
|
|
194
218
|
for (const [key, value] of Object.entries(schema)) {
|
|
195
219
|
if (key === 'additionalProperties' || key === '$schema') {
|
|
196
220
|
continue;
|
|
197
221
|
}
|
|
222
|
+
if (nullableType !== undefined && key === 'type') {
|
|
223
|
+
out.type = nullableType;
|
|
224
|
+
out.nullable = true;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (nullableType !== undefined && key === 'enum' && Array.isArray(value)) {
|
|
228
|
+
// A nullable enum carries `null` among its values in draft-07. OpenAPI expresses
|
|
229
|
+
// that with `nullable` alone, so the member is dropped rather than sent as a value
|
|
230
|
+
// Gemini would reject.
|
|
231
|
+
out.enum = value.filter((member) => member !== null);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
198
234
|
if (key === 'properties' && value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
199
235
|
// `properties` maps user-defined parameter names to subschemas: recurse each
|
|
200
236
|
// subschema value but never treat a parameter name as a strippable keyword.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolFormats.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/toolFormats.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;AAkBZ,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAiC,EACjC,aAAgD,EAChD,YAAgD;IAEhD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IAErD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,aAAa;SACjB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SACjD,GAAG,CAAC,CAAC,CAAC,EAAsB,EAAE,WAAC,OAAA,MAAA,CAAC,CAAC,MAAM,mCAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA,EAAA,CAAC,CAAC;AAClE,CAAC;AAED,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAE/E;;;GAGG;AACH,SAAS,uBAAuB,CAAC,MAA8B;IAC7D,MAAM,IAAI,GAA4B,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAE7D,IAAI,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QACnD,MAAM,OAAO,GAA4B,EAAE,CAAC;QAC5C,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1B,OAAO,CAAC,eAAe,GAAG,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1B,OAAO,CAAC,gBAAgB,GAAG,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,IAAI,MAAM,CAAC,wBAAwB,EAAE,CAAC;QACpC,IAAI,CAAC,0BAA0B,GAAG,IAAI,CAAC;IACzC,CAAC;IAED,OAAO,IAAkB,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,SAAS,wBAAwB,CAAC,MAA2B;IAC3D,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,UAAU,EAAE,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE;KAC/B,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAkC;IACpE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACrB,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,YAAY;gBACf,OAAO,uBAAuB,CAAC,CAAC,CAAC,CAAC;YACpC,KAAK,aAAa;gBAChB,OAAO,wBAAwB,CAAC,CAAC,CAAC,CAAC;YACrC,qFAAqF;YACrF,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,WAAW,GAAU,CAAC,CAAC;gBAC7B,OAAO,EAAE,IAAI,EAAE,WAAW,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,EAAgB,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,gCAAgC;AAChC,+EAA+E;AAE/E;;;GAGG;AACH,SAAS,oBAAoB,CAAC,MAA8B;IAC1D,MAAM,IAAI,GAA4B;QACpC,IAAI,EAAE,qBAAqB;QAC3B,IAAI,EAAE,YAAY;KACnB,CAAC;IAEF,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC;IACjC,CAAC;IACD,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1B,IAAI,CAAC,eAAe,GAAG,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;QAC1B,IAAI,CAAC,eAAe,GAAG,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,IAAkB,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,SAAS,qBAAqB,CAAC,MAA2B;IACxD,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE;KACjC,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAkC;IACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACrB,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,YAAY;gBACf,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC;YACjC,KAAK,aAAa;gBAChB,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC;YAClC,qFAAqF;YACrF,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,WAAW,GAAU,CAAC,CAAC;gBAC7B,OAAO,EAAE,IAAI,EAAE,WAAW,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,EAAgB,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAiB;IACvD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,GAAG,GAAe,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAClD,IAAI,GAAG,KAAK,sBAAsB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACxD,SAAS;YACX,CAAC;YACD,IAAI,GAAG,KAAK,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjG,6EAA6E;gBAC7E,4EAA4E;gBAC5E,MAAM,UAAU,GAAe,EAAE,CAAC;gBAClC,KAAK,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;oBACvD,UAAU,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;gBACzD,CAAC;gBACD,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,CAAC,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,aAAa,CAAC,KAAkC;IAC9D,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,MAAM,oBAAoB,GAAiB,EAAE,CAAC;IAE9C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,YAAY;gBACf,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,EAAE,EAAgB,CAAC,CAAC;gBACjD,MAAM;YACR,KAAK,aAAa;gBAChB,oBAAoB,CAAC,IAAI,CAAC;oBACxB,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,WAAW,EAAE,CAAC,CAAC,WAAW;oBAC1B,UAAU,EAAE,uBAAuB,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC;iBACnD,CAAC,CAAC;gBACjB,MAAM;YACR,qFAAqF;YACrF,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,WAAW,GAAU,CAAC,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,EAAgB,CAAC,CAAC;YAChF,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,EAAE,qBAAqB,EAAE,oBAAoB,EAAgB,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Provider-specific tool format translation and tool resolution logic.\n * @packageDocumentation\n */\n\nimport { type JsonObject, type JsonValue } from '@fgv/ts-json-base';\n\nimport {\n type AiServerToolConfig,\n type AiToolConfig,\n type IAiClientToolConfig,\n type IAiProviderDescriptor,\n type IAiToolEnablement,\n type IAiWebSearchToolConfig\n} from './model';\n\n// ============================================================================\n// Tool resolution\n// ============================================================================\n\n/**\n * Resolves the effective tools for a completion call.\n *\n * - If per-call tools are provided, they override settings-level tools entirely.\n * - Otherwise, settings-level enabled tools are used.\n * - Only tools supported by the provider are included.\n * - Returns an empty array if no tools are enabled (= no tools sent).\n *\n * @param descriptor - The provider descriptor (used to filter by supported tools)\n * @param settingsTools - Tool enablement from provider settings (optional)\n * @param perCallTools - Per-call tool override (optional)\n * @returns The resolved list of tool configs to include in the request\n * @public\n */\nexport function resolveEffectiveTools(\n descriptor: IAiProviderDescriptor,\n settingsTools?: ReadonlyArray<IAiToolEnablement>,\n perCallTools?: ReadonlyArray<AiServerToolConfig>\n): ReadonlyArray<AiServerToolConfig> {\n const supported = new Set(descriptor.supportedTools);\n\n if (perCallTools !== undefined) {\n return perCallTools.filter((t) => supported.has(t.type));\n }\n\n if (settingsTools === undefined) {\n return [];\n }\n\n return settingsTools\n .filter((e) => e.enabled && supported.has(e.type))\n .map((e): AiServerToolConfig => e.config ?? { type: e.type });\n}\n\n// ============================================================================\n// OpenAI / xAI Responses API format\n// ============================================================================\n\n/**\n * Formats a web search tool config for the xAI/OpenAI Responses API.\n * @internal\n */\nfunction webSearchToResponsesApi(config: IAiWebSearchToolConfig): JsonObject {\n const tool: Record<string, unknown> = { type: 'web_search' };\n\n if (config.allowedDomains || config.blockedDomains) {\n const filters: Record<string, unknown> = {};\n if (config.allowedDomains) {\n filters.allowed_domains = [...config.allowedDomains];\n }\n if (config.blockedDomains) {\n filters.excluded_domains = [...config.blockedDomains];\n }\n tool.filters = filters;\n }\n\n if (config.enableImageUnderstanding) {\n tool.enable_image_understanding = true;\n }\n\n return tool as JsonObject;\n}\n\n/**\n * Formats a client tool config for the xAI/OpenAI Responses API.\n * @internal\n */\nfunction clientToolToResponsesApi(config: IAiClientToolConfig): JsonObject {\n return {\n type: 'function',\n name: config.name,\n description: config.description,\n parameters: config.parametersSchema.toJson()\n } as JsonObject;\n}\n\n/**\n * Formats tool configs for the xAI/OpenAI Responses API.\n * @param tools - The resolved tool configs (server-side and/or client-defined)\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toResponsesApiTools(tools: ReadonlyArray<AiToolConfig>): ReadonlyArray<JsonObject> {\n return tools.map((t) => {\n switch (t.type) {\n case 'web_search':\n return webSearchToResponsesApi(t);\n case 'client_tool':\n return clientToolToResponsesApi(t);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t;\n return { type: `unknown:${JSON.stringify(_exhaustive)}` } as JsonObject;\n }\n }\n });\n}\n\n// ============================================================================\n// Anthropic Messages API format\n// ============================================================================\n\n/**\n * Formats a web search tool config for the Anthropic Messages API.\n * @internal\n */\nfunction webSearchToAnthropic(config: IAiWebSearchToolConfig): JsonObject {\n const tool: Record<string, unknown> = {\n type: 'web_search_20250305',\n name: 'web_search'\n };\n\n if (config.maxUses !== undefined) {\n tool.max_uses = config.maxUses;\n }\n if (config.allowedDomains) {\n tool.allowed_domains = [...config.allowedDomains];\n }\n if (config.blockedDomains) {\n tool.blocked_domains = [...config.blockedDomains];\n }\n\n return tool as JsonObject;\n}\n\n/**\n * Formats a client tool config for the Anthropic Messages API.\n * Note: Anthropic client tools have no `type` field (unlike server tools).\n * @internal\n */\nfunction clientToolToAnthropic(config: IAiClientToolConfig): JsonObject {\n return {\n name: config.name,\n description: config.description,\n input_schema: config.parametersSchema.toJson()\n } as JsonObject;\n}\n\n/**\n * Formats tool configs for the Anthropic Messages API.\n * @param tools - The resolved tool configs (server-side and/or client-defined)\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toAnthropicTools(tools: ReadonlyArray<AiToolConfig>): ReadonlyArray<JsonObject> {\n return tools.map((t) => {\n switch (t.type) {\n case 'web_search':\n return webSearchToAnthropic(t);\n case 'client_tool':\n return clientToolToAnthropic(t);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t;\n return { type: `unknown:${JSON.stringify(_exhaustive)}` } as JsonObject;\n }\n }\n });\n}\n\n// ============================================================================\n// Gemini generateContent API format\n// ============================================================================\n\n/**\n * Sanitizes a draft-07 JSON Schema (as emitted by `JsonSchema.object(...).toJson()`)\n * into the OpenAPI 3.0 Schema Object subset that Gemini's `function_declarations[].parameters`\n * accepts.\n *\n * @remarks\n * Gemini's function-declaration schema is **not** full JSON Schema — it is a subset of\n * the OpenAPI 3.0 Schema Object and **rejects** (rather than ignores) draft-07-only\n * keywords. `JsonSchema` objects are strict-by-default, so `.toJson()` emits\n * `additionalProperties: false` on every object node, which 400s the whole request on\n * Gemini. This helper recursively strips the unsupported keywords so any\n * `JsonSchema`-authored client tool works on Gemini without consumer awareness of the\n * dialect difference. Stripping is infallible, so it returns a plain value rather than a\n * `Result`.\n *\n * `additionalProperties` and `$schema` are stripped only where they appear as schema\n * *keywords* (siblings of `type`/`properties`/etc.). Inside a `properties` map the keys\n * are user-defined parameter names, not keywords, so they are preserved verbatim while\n * each property's subschema value is still recursively sanitized — a tool parameter\n * legitimately named `additionalProperties` survives.\n *\n * @internal\n */\nexport function toGeminiParameterSchema(schema: JsonValue): JsonValue {\n if (Array.isArray(schema)) {\n return schema.map(toGeminiParameterSchema);\n }\n if (schema !== null && typeof schema === 'object') {\n const out: JsonObject = {};\n for (const [key, value] of Object.entries(schema)) {\n if (key === 'additionalProperties' || key === '$schema') {\n continue;\n }\n if (key === 'properties' && value !== null && typeof value === 'object' && !Array.isArray(value)) {\n // `properties` maps user-defined parameter names to subschemas: recurse each\n // subschema value but never treat a parameter name as a strippable keyword.\n const properties: JsonObject = {};\n for (const [name, propSchema] of Object.entries(value)) {\n properties[name] = toGeminiParameterSchema(propSchema);\n }\n out[key] = properties;\n } else {\n out[key] = toGeminiParameterSchema(value);\n }\n }\n return out;\n }\n return schema;\n}\n\n/**\n * Formats tool configs for the Gemini generateContent API.\n *\n * @remarks\n * Gemini uses `google_search` for search grounding (no per-tool config).\n * Client-defined tools are accumulated into a single `function_declarations` entry.\n * Each client tool's parameters schema is sanitized to Gemini's OpenAPI-subset\n * dialect via {@link toGeminiParameterSchema} (the raw draft-07 `.toJson()` output\n * carries `additionalProperties`, which Gemini rejects).\n *\n * @param tools - The resolved tool configs (server-side and/or client-defined)\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toGeminiTools(tools: ReadonlyArray<AiToolConfig>): ReadonlyArray<JsonObject> {\n const result: JsonObject[] = [];\n const functionDeclarations: JsonObject[] = [];\n\n for (const t of tools) {\n switch (t.type) {\n case 'web_search':\n result.push({ google_search: {} } as JsonObject);\n break;\n case 'client_tool':\n functionDeclarations.push({\n name: t.name,\n description: t.description,\n parameters: toGeminiParameterSchema(t.parametersSchema.toJson())\n } as JsonObject);\n break;\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t;\n result.push({ type: `unknown:${JSON.stringify(_exhaustive)}` } as JsonObject);\n }\n }\n }\n\n if (functionDeclarations.length > 0) {\n result.push({ function_declarations: functionDeclarations } as JsonObject);\n }\n\n return result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"toolFormats.js","sourceRoot":"","sources":["../../../src/packlets/ai-assist/toolFormats.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\n/**\n * Provider-specific tool format translation and tool resolution logic.\n * @packageDocumentation\n */\n\nimport { type JsonObject, type JsonValue } from '@fgv/ts-json-base';\n\nimport {\n type AiServerToolConfig,\n type AiToolConfig,\n type IAiClientToolConfig,\n type IAiProviderDescriptor,\n type IAiToolEnablement,\n type IAiWebSearchToolConfig\n} from './model';\n\n// ============================================================================\n// Tool resolution\n// ============================================================================\n\n/**\n * Resolves the effective tools for a completion call.\n *\n * - If per-call tools are provided, they override settings-level tools entirely.\n * - Otherwise, settings-level enabled tools are used.\n * - Only tools supported by the provider are included.\n * - Returns an empty array if no tools are enabled (= no tools sent).\n *\n * @param descriptor - The provider descriptor (used to filter by supported tools)\n * @param settingsTools - Tool enablement from provider settings (optional)\n * @param perCallTools - Per-call tool override (optional)\n * @returns The resolved list of tool configs to include in the request\n * @public\n */\nexport function resolveEffectiveTools(\n descriptor: IAiProviderDescriptor,\n settingsTools?: ReadonlyArray<IAiToolEnablement>,\n perCallTools?: ReadonlyArray<AiServerToolConfig>\n): ReadonlyArray<AiServerToolConfig> {\n const supported = new Set(descriptor.supportedTools);\n\n if (perCallTools !== undefined) {\n return perCallTools.filter((t) => supported.has(t.type));\n }\n\n if (settingsTools === undefined) {\n return [];\n }\n\n return settingsTools\n .filter((e) => e.enabled && supported.has(e.type))\n .map((e): AiServerToolConfig => e.config ?? { type: e.type });\n}\n\n// ============================================================================\n// OpenAI / xAI Responses API format\n// ============================================================================\n\n/**\n * Formats a web search tool config for the xAI/OpenAI Responses API.\n * @internal\n */\nfunction webSearchToResponsesApi(config: IAiWebSearchToolConfig): JsonObject {\n const tool: Record<string, unknown> = { type: 'web_search' };\n\n if (config.allowedDomains || config.blockedDomains) {\n const filters: Record<string, unknown> = {};\n if (config.allowedDomains) {\n filters.allowed_domains = [...config.allowedDomains];\n }\n if (config.blockedDomains) {\n filters.excluded_domains = [...config.blockedDomains];\n }\n tool.filters = filters;\n }\n\n if (config.enableImageUnderstanding) {\n tool.enable_image_understanding = true;\n }\n\n return tool as JsonObject;\n}\n\n/**\n * Formats a client tool config for the xAI/OpenAI Responses API.\n * @internal\n */\nfunction clientToolToResponsesApi(config: IAiClientToolConfig): JsonObject {\n return {\n type: 'function',\n name: config.name,\n description: config.description,\n parameters: config.parametersSchema.toJson()\n } as JsonObject;\n}\n\n/**\n * Formats tool configs for the xAI/OpenAI Responses API.\n * @param tools - The resolved tool configs (server-side and/or client-defined)\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toResponsesApiTools(tools: ReadonlyArray<AiToolConfig>): ReadonlyArray<JsonObject> {\n return tools.map((t) => {\n switch (t.type) {\n case 'web_search':\n return webSearchToResponsesApi(t);\n case 'client_tool':\n return clientToolToResponsesApi(t);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t;\n return { type: `unknown:${JSON.stringify(_exhaustive)}` } as JsonObject;\n }\n }\n });\n}\n\n// ============================================================================\n// Anthropic Messages API format\n// ============================================================================\n\n/**\n * Formats a web search tool config for the Anthropic Messages API.\n * @internal\n */\nfunction webSearchToAnthropic(config: IAiWebSearchToolConfig): JsonObject {\n const tool: Record<string, unknown> = {\n type: 'web_search_20250305',\n name: 'web_search'\n };\n\n if (config.maxUses !== undefined) {\n tool.max_uses = config.maxUses;\n }\n if (config.allowedDomains) {\n tool.allowed_domains = [...config.allowedDomains];\n }\n if (config.blockedDomains) {\n tool.blocked_domains = [...config.blockedDomains];\n }\n\n return tool as JsonObject;\n}\n\n/**\n * Formats a client tool config for the Anthropic Messages API.\n * Note: Anthropic client tools have no `type` field (unlike server tools).\n * @internal\n */\nfunction clientToolToAnthropic(config: IAiClientToolConfig): JsonObject {\n return {\n name: config.name,\n description: config.description,\n input_schema: config.parametersSchema.toJson()\n } as JsonObject;\n}\n\n/**\n * Formats tool configs for the Anthropic Messages API.\n * @param tools - The resolved tool configs (server-side and/or client-defined)\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toAnthropicTools(tools: ReadonlyArray<AiToolConfig>): ReadonlyArray<JsonObject> {\n return tools.map((t) => {\n switch (t.type) {\n case 'web_search':\n return webSearchToAnthropic(t);\n case 'client_tool':\n return clientToolToAnthropic(t);\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t;\n return { type: `unknown:${JSON.stringify(_exhaustive)}` } as JsonObject;\n }\n }\n });\n}\n\n// ============================================================================\n// Gemini generateContent API format\n// ============================================================================\n\n/**\n * Sanitizes a draft-07 JSON Schema (as emitted by `JsonSchema.object(...).toJson()`)\n * into the OpenAPI 3.0 Schema Object subset that Gemini's `function_declarations[].parameters`\n * accepts.\n *\n * @remarks\n * Gemini's function-declaration schema is **not** full JSON Schema — it is a subset of\n * the OpenAPI 3.0 Schema Object and **rejects** (rather than ignores) draft-07-only\n * keywords. `JsonSchema` objects are strict-by-default, so `.toJson()` emits\n * `additionalProperties: false` on every object node, which 400s the whole request on\n * Gemini. This helper recursively strips the unsupported keywords so any\n * `JsonSchema`-authored client tool works on Gemini without consumer awareness of the\n * dialect difference. Stripping is infallible, so it returns a plain value rather than a\n * `Result`.\n *\n * `additionalProperties` and `$schema` are stripped only where they appear as schema\n * *keywords* (siblings of `type`/`properties`/etc.). Inside a `properties` map the keys\n * are user-defined parameter names, not keywords, so they are preserved verbatim while\n * each property's subschema value is still recursively sanitized — a tool parameter\n * legitimately named `additionalProperties` survives.\n *\n * @internal\n */\n/**\n * The non-`null` member of a draft-07 nullable `type` union, or `undefined` when `type` is\n * not one.\n *\n * @remarks\n * Deliberately narrow: only the two-member `[<type>, 'null']` shape `JsonSchema` emits is\n * recognised. A general union has no OpenAPI equivalent, so translating one would be\n * inventing a meaning — it is passed through unchanged and Gemini refuses it, which is the\n * honest outcome.\n * @internal\n */\nfunction _nullableUnionMember(type: JsonValue | undefined): string | undefined {\n if (!Array.isArray(type) || type.length !== 2 || !type.includes('null')) {\n return undefined;\n }\n const other: JsonValue | undefined = type.find((member) => member !== 'null');\n return typeof other === 'string' ? other : undefined;\n}\n\nexport function toGeminiParameterSchema(schema: JsonValue): JsonValue {\n if (Array.isArray(schema)) {\n return schema.map(toGeminiParameterSchema);\n }\n if (schema !== null && typeof schema === 'object') {\n const out: JsonObject = {};\n // Nullability is spelled differently in the two dialects and they are mutually\n // exclusive: draft-07 (and OpenAI strict mode) wants `type: ['string', 'null']`,\n // OpenAPI 3.0 (and Gemini) wants `type: 'string'` + `nullable: true` and rejects the\n // union array. This is the same class of translation as the `additionalProperties`\n // strip above — a dialect difference the consumer should not have to know about.\n const nullableType: string | undefined = _nullableUnionMember(schema.type);\n for (const [key, value] of Object.entries(schema)) {\n if (key === 'additionalProperties' || key === '$schema') {\n continue;\n }\n if (nullableType !== undefined && key === 'type') {\n out.type = nullableType;\n out.nullable = true;\n continue;\n }\n if (nullableType !== undefined && key === 'enum' && Array.isArray(value)) {\n // A nullable enum carries `null` among its values in draft-07. OpenAPI expresses\n // that with `nullable` alone, so the member is dropped rather than sent as a value\n // Gemini would reject.\n out.enum = value.filter((member) => member !== null);\n continue;\n }\n if (key === 'properties' && value !== null && typeof value === 'object' && !Array.isArray(value)) {\n // `properties` maps user-defined parameter names to subschemas: recurse each\n // subschema value but never treat a parameter name as a strippable keyword.\n const properties: JsonObject = {};\n for (const [name, propSchema] of Object.entries(value)) {\n properties[name] = toGeminiParameterSchema(propSchema);\n }\n out[key] = properties;\n } else {\n out[key] = toGeminiParameterSchema(value);\n }\n }\n return out;\n }\n return schema;\n}\n\n/**\n * Formats tool configs for the Gemini generateContent API.\n *\n * @remarks\n * Gemini uses `google_search` for search grounding (no per-tool config).\n * Client-defined tools are accumulated into a single `function_declarations` entry.\n * Each client tool's parameters schema is sanitized to Gemini's OpenAPI-subset\n * dialect via {@link toGeminiParameterSchema} (the raw draft-07 `.toJson()` output\n * carries `additionalProperties`, which Gemini rejects).\n *\n * @param tools - The resolved tool configs (server-side and/or client-defined)\n * @returns Provider-native tool objects for the `tools` request field\n * @public\n */\nexport function toGeminiTools(tools: ReadonlyArray<AiToolConfig>): ReadonlyArray<JsonObject> {\n const result: JsonObject[] = [];\n const functionDeclarations: JsonObject[] = [];\n\n for (const t of tools) {\n switch (t.type) {\n case 'web_search':\n result.push({ google_search: {} } as JsonObject);\n break;\n case 'client_tool':\n functionDeclarations.push({\n name: t.name,\n description: t.description,\n parameters: toGeminiParameterSchema(t.parametersSchema.toJson())\n } as JsonObject);\n break;\n /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */\n default: {\n const _exhaustive: never = t;\n result.push({ type: `unknown:${JSON.stringify(_exhaustive)}` } as JsonObject);\n }\n }\n }\n\n if (functionDeclarations.length > 0) {\n result.push({ function_declarations: functionDeclarations } as JsonObject);\n }\n\n return result;\n}\n"]}
|
package/dist/ts-extras.d.ts
CHANGED
|
@@ -181,6 +181,8 @@ declare namespace AiAssist {
|
|
|
181
181
|
supportsImageGeneration,
|
|
182
182
|
resolveEmbeddingCapability,
|
|
183
183
|
supportsEmbedding,
|
|
184
|
+
resolveStructuredOutputCapability,
|
|
185
|
+
supportsStructuredOutput,
|
|
184
186
|
DEFAULT_MODEL_CAPABILITY_CONFIG,
|
|
185
187
|
callProviderCompletion,
|
|
186
188
|
callProxiedCompletion,
|
|
@@ -213,6 +215,14 @@ declare namespace AiAssist {
|
|
|
213
215
|
modelSpecKey,
|
|
214
216
|
modelSpec,
|
|
215
217
|
resolveEffectiveTools,
|
|
218
|
+
ANTHROPIC_STRUCTURED_OUTPUT_TOOL_NAME,
|
|
219
|
+
AiStructuredOutputFormat,
|
|
220
|
+
IAiStructuredOutputCapability,
|
|
221
|
+
IJsonObjectStructuredOutputRequest,
|
|
222
|
+
ISchemaStructuredOutputRequest,
|
|
223
|
+
StructuredOutputEnforcement,
|
|
224
|
+
StructuredOutputFallback,
|
|
225
|
+
StructuredOutputRequest,
|
|
216
226
|
classifyJsonParseFailure,
|
|
217
227
|
extractJsonText,
|
|
218
228
|
fencedStringifiedJson,
|
|
@@ -393,6 +403,20 @@ declare type AiServerToolType = 'web_search';
|
|
|
393
403
|
*/
|
|
394
404
|
declare const aiServerToolType: Converter<AiServerToolType>;
|
|
395
405
|
|
|
406
|
+
/**
|
|
407
|
+
* Wire format a provider uses to express a structured-output constraint.
|
|
408
|
+
*
|
|
409
|
+
* @remarks
|
|
410
|
+
* Four shapes, not one, and they differ in more than field names: the OpenAI
|
|
411
|
+
* pair carry the schema in the request body, Gemini carries it inside
|
|
412
|
+
* `generationConfig`, and Anthropic has no response-format field at all —
|
|
413
|
+
* its mechanism is forced tool use, which is why `'tool-forced'` is a distinct
|
|
414
|
+
* {@link AiAssist.StructuredOutputEnforcement} value rather than a spelling of
|
|
415
|
+
* `'schema'`.
|
|
416
|
+
* @public
|
|
417
|
+
*/
|
|
418
|
+
declare type AiStructuredOutputFormat = 'openai-json-schema' | 'openai-responses-format' | 'gemini-response-schema' | 'anthropic-tool-forced';
|
|
419
|
+
|
|
396
420
|
/**
|
|
397
421
|
* Thinking/reasoning mode support for a provider.
|
|
398
422
|
* @public
|
|
@@ -550,6 +574,19 @@ declare const allProviderIds: ReadonlyArray<AiProviderId>;
|
|
|
550
574
|
*/
|
|
551
575
|
declare const ALWAYS_STRIPPED_HEADERS: ReadonlyArray<string>;
|
|
552
576
|
|
|
577
|
+
/**
|
|
578
|
+
* The name the Anthropic forced-tool path gives its synthetic tool.
|
|
579
|
+
*
|
|
580
|
+
* @remarks
|
|
581
|
+
* Anthropic has no `response_format`; its structured-output mechanism is forced
|
|
582
|
+
* tool use, so a tool must exist to be forced. The name is fgv-owned and never
|
|
583
|
+
* reaches the caller — the structured-output resolver re-serializes the tool's
|
|
584
|
+
* `input` back into `IAiCompletionResponse.content`, so a caller's converter sees
|
|
585
|
+
* a JSON string exactly as it does on every other provider.
|
|
586
|
+
* @public
|
|
587
|
+
*/
|
|
588
|
+
declare const ANTHROPIC_STRUCTURED_OUTPUT_TOOL_NAME: string;
|
|
589
|
+
|
|
553
590
|
/**
|
|
554
591
|
* Maps Anthropic effort level to the `thinking.budget_tokens` integer that the
|
|
555
592
|
* Anthropic API requires when `thinking.type === 'enabled'`.
|
|
@@ -2596,11 +2633,29 @@ declare interface IAiCompletionResponse {
|
|
|
2596
2633
|
readonly content: string;
|
|
2597
2634
|
/** Whether the response was truncated due to token limits */
|
|
2598
2635
|
readonly truncated: boolean;
|
|
2636
|
+
/**
|
|
2637
|
+
* Which structured-output constraint the provider was **asked** to apply.
|
|
2638
|
+
*
|
|
2639
|
+
* @remarks
|
|
2640
|
+
* **Required, not optional, and that is the point.** An optional field would
|
|
2641
|
+
* make absence three-ways ambiguous — no capability / not requested / a build
|
|
2642
|
+
* predating the feature — and disambiguating exactly that is what this field
|
|
2643
|
+
* exists for. `'none'` already expresses *"no constraint sent"*, so
|
|
2644
|
+
* always-present costs nothing and removes the ambiguity by construction. The
|
|
2645
|
+
* same remedy as `MemoryEmbedOutcome` in `@fgv/ts-agent-memory`, applied to the
|
|
2646
|
+
* same defect.
|
|
2647
|
+
*
|
|
2648
|
+
* It reports what was *sent*, never whether **this** response conforms — that
|
|
2649
|
+
* is the caller's converter's answer and re-deriving it here would be a second
|
|
2650
|
+
* source of truth. See `StructuredOutputEnforcement` for the three-question
|
|
2651
|
+
* split.
|
|
2652
|
+
*/
|
|
2653
|
+
readonly structuredOutput: StructuredOutputEnforcement;
|
|
2599
2654
|
}
|
|
2600
2655
|
|
|
2601
2656
|
/**
|
|
2602
2657
|
* Embedding capability for a model family within a provider. Used as an entry
|
|
2603
|
-
* in
|
|
2658
|
+
* in `embedding`.
|
|
2604
2659
|
*
|
|
2605
2660
|
* @public
|
|
2606
2661
|
*/
|
|
@@ -2823,7 +2878,7 @@ declare interface IAiImageGenerationResponse {
|
|
|
2823
2878
|
|
|
2824
2879
|
/**
|
|
2825
2880
|
* Image-generation capability for a model family within a provider. Used as
|
|
2826
|
-
* an entry in
|
|
2881
|
+
* an entry in `imageGeneration`.
|
|
2827
2882
|
*
|
|
2828
2883
|
* @public
|
|
2829
2884
|
*/
|
|
@@ -3006,6 +3061,23 @@ declare interface IAiProviderDescriptor {
|
|
|
3006
3061
|
* caller supplies the embedding model via `modelOverride`.
|
|
3007
3062
|
*/
|
|
3008
3063
|
readonly embedding?: ReadonlyArray<IAiEmbeddingModelCapability>;
|
|
3064
|
+
/**
|
|
3065
|
+
* Per-model-family structured-output capability, longest-prefix matched against
|
|
3066
|
+
* the **resolved** completion model id. Absent (or no matching entry) means the
|
|
3067
|
+
* model can enforce nothing, and a request against it reports `'none'`.
|
|
3068
|
+
*
|
|
3069
|
+
* @remarks
|
|
3070
|
+
* Same declaration idiom as `imageGeneration` and
|
|
3071
|
+
* `embedding`, resolved through the same
|
|
3072
|
+
* alias-first helper — a capability lookup on an unresolved alias is the defect
|
|
3073
|
+
* `resolveImageCapability` once had, where a catch-all `modelPrefix: ''` turned
|
|
3074
|
+
* an unknown alias into a confidently wrong answer.
|
|
3075
|
+
*
|
|
3076
|
+
* Note this declares which wire format a model *family* supports, not which
|
|
3077
|
+
* OpenAI endpoint a given call will take — that also depends on whether the
|
|
3078
|
+
* call carries server tools, so the dispatcher supplies it.
|
|
3079
|
+
*/
|
|
3080
|
+
readonly structuredOutput?: ReadonlyArray<IAiStructuredOutputCapability>;
|
|
3009
3081
|
/**
|
|
3010
3082
|
* Concrete model ids (prefix-matched) that must be invoked via the OpenAI
|
|
3011
3083
|
* Responses API rather than chat completions — e.g. `gpt-5.5-pro`. Non-OpenAI
|
|
@@ -3180,6 +3252,28 @@ declare interface IAiStreamToolUseStart {
|
|
|
3180
3252
|
readonly callId?: string;
|
|
3181
3253
|
}
|
|
3182
3254
|
|
|
3255
|
+
/**
|
|
3256
|
+
* Structured-output capability for a model family within a provider. Used as an
|
|
3257
|
+
* entry in `IAiProviderDescriptor.structuredOutput`.
|
|
3258
|
+
*
|
|
3259
|
+
* @remarks
|
|
3260
|
+
* Deliberately thinner than its `imageGeneration` / `embedding` siblings: it
|
|
3261
|
+
* carries no `supportsX` flags, because what each format can enforce is a
|
|
3262
|
+
* property of the provider's **API surface** rather than of any one model, and a
|
|
3263
|
+
* per-entry declaration of it could only ever disagree with the one in code.
|
|
3264
|
+
* @public
|
|
3265
|
+
*/
|
|
3266
|
+
declare interface IAiStructuredOutputCapability {
|
|
3267
|
+
/**
|
|
3268
|
+
* Prefix matched against the resolved completion model id. The empty string is
|
|
3269
|
+
* the catch-all and matches every model. When multiple rules' prefixes match a
|
|
3270
|
+
* model id, the longest prefix wins; ties are broken by first-encountered.
|
|
3271
|
+
*/
|
|
3272
|
+
readonly modelPrefix: string;
|
|
3273
|
+
/** Wire format used to express the constraint for matching models. */
|
|
3274
|
+
readonly format: AiStructuredOutputFormat;
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3183
3277
|
/**
|
|
3184
3278
|
* Behavior annotations for a client-defined tool.
|
|
3185
3279
|
*
|
|
@@ -4543,6 +4637,22 @@ declare interface IImportSecretOptions extends IAddSecretOptions {
|
|
|
4543
4637
|
readonly replace?: boolean;
|
|
4544
4638
|
}
|
|
4545
4639
|
|
|
4640
|
+
/**
|
|
4641
|
+
* Ask the provider for syntactically valid JSON of arbitrary shape.
|
|
4642
|
+
*
|
|
4643
|
+
* @remarks
|
|
4644
|
+
* The weaker floor, and worth having on its own: the failure that motivated this
|
|
4645
|
+
* surface (`Expected ',' or '}' after property value` — an unescaped quote closing
|
|
4646
|
+
* a string early) is **syntactic**, so a JSON-mode guarantee removes it. Schema
|
|
4647
|
+
* constraint is what additionally buys shape. It is also the only mode some
|
|
4648
|
+
* model/provider pairs support.
|
|
4649
|
+
* @public
|
|
4650
|
+
*/
|
|
4651
|
+
declare interface IJsonObjectStructuredOutputRequest {
|
|
4652
|
+
readonly mode: 'json-object';
|
|
4653
|
+
readonly onUnsupported?: StructuredOutputFallback;
|
|
4654
|
+
}
|
|
4655
|
+
|
|
4546
4656
|
/**
|
|
4547
4657
|
* Key derivation parameters stored in encrypted files.
|
|
4548
4658
|
* Discriminated union on `kdf` field: `'pbkdf2'` or `'argon2id'`.
|
|
@@ -5239,6 +5349,18 @@ declare interface IProviderCompletionParams extends IChatRequest {
|
|
|
5239
5349
|
* Messages API requires the field, so it falls back to `DEFAULT_ANTHROPIC_MAX_TOKENS`.
|
|
5240
5350
|
*/
|
|
5241
5351
|
readonly maxTokens?: number;
|
|
5352
|
+
/**
|
|
5353
|
+
* Ask the provider to constrain its output — to a schema, or to syntactically
|
|
5354
|
+
* valid JSON of arbitrary shape.
|
|
5355
|
+
*
|
|
5356
|
+
* @remarks
|
|
5357
|
+
* **The caller supplies intent; the response reports outcome.** A caller cannot
|
|
5358
|
+
* know up front which concrete model will serve the request (a `tier` request
|
|
5359
|
+
* cascades, and aliases resolve at call time), so it never has to: whatever was
|
|
5360
|
+
* actually enforced comes back on
|
|
5361
|
+
* `IAiCompletionResponse.structuredOutput`.
|
|
5362
|
+
*/
|
|
5363
|
+
readonly structuredOutput?: StructuredOutputRequest;
|
|
5242
5364
|
}
|
|
5243
5365
|
|
|
5244
5366
|
/**
|
|
@@ -5820,6 +5942,25 @@ declare interface ISaferFetchResponseHead {
|
|
|
5820
5942
|
readonly contentLength?: number;
|
|
5821
5943
|
}
|
|
5822
5944
|
|
|
5945
|
+
/**
|
|
5946
|
+
* Ask the provider for JSON constrained to a schema.
|
|
5947
|
+
* @public
|
|
5948
|
+
*/
|
|
5949
|
+
declare interface ISchemaStructuredOutputRequest {
|
|
5950
|
+
readonly mode: 'schema';
|
|
5951
|
+
/**
|
|
5952
|
+
* The schema to constrain generation to — **the same object you validate the
|
|
5953
|
+
* reply with**, so the wire schema and the check cannot drift.
|
|
5954
|
+
*
|
|
5955
|
+
* @remarks
|
|
5956
|
+
* Author it with `JsonSchema.object({...})` from `@fgv/ts-json-base`. This is
|
|
5957
|
+
* the property `@fgv/ts-extras-ollama`'s `chatStructured` already has; this
|
|
5958
|
+
* surface is its cloud sibling.
|
|
5959
|
+
*/
|
|
5960
|
+
readonly schema: JsonSchema.ISchemaValidator<unknown>;
|
|
5961
|
+
readonly onUnsupported?: StructuredOutputFallback;
|
|
5962
|
+
}
|
|
5963
|
+
|
|
5823
5964
|
/**
|
|
5824
5965
|
* Checks if a JSON object appears to be an encrypted file.
|
|
5825
5966
|
* Uses the format field as a discriminator.
|
|
@@ -7897,6 +8038,21 @@ declare function resolveModelAlias(descriptor: IAiProviderDescriptor, model: str
|
|
|
7897
8038
|
*/
|
|
7898
8039
|
declare function resolveProviderModel(descriptor: IAiProviderDescriptor, modelOverride: ModelSpec | undefined, context?: ModelSpecKey): Result<string>;
|
|
7899
8040
|
|
|
8041
|
+
/**
|
|
8042
|
+
* The structured-output capability for `modelId` under `descriptor`, or
|
|
8043
|
+
* `undefined` when the model can enforce nothing.
|
|
8044
|
+
*
|
|
8045
|
+
* @remarks
|
|
8046
|
+
* Alias-first, exactly like its `imageGeneration` / `embedding` siblings — an
|
|
8047
|
+
* unresolved alias returns `undefined` rather than prefix-matching a catch-all
|
|
8048
|
+
* `modelPrefix: ''`, which is the defect this helper was written to prevent.
|
|
8049
|
+
*
|
|
8050
|
+
* @param descriptor - The provider descriptor.
|
|
8051
|
+
* @param modelId - A concrete model id or an `@provider:role` alias.
|
|
8052
|
+
* @public
|
|
8053
|
+
*/
|
|
8054
|
+
declare function resolveStructuredOutputCapability(descriptor: IAiProviderDescriptor, modelId: string): IAiStructuredOutputCapability | undefined;
|
|
8055
|
+
|
|
7900
8056
|
/**
|
|
7901
8057
|
* Statuses whose `Retry-After` header is honored.
|
|
7902
8058
|
*
|
|
@@ -8163,6 +8319,71 @@ declare const SMART_JSON_PROMPT_HINT: string;
|
|
|
8163
8319
|
*/
|
|
8164
8320
|
declare function spkiToRawX25519(spki: Uint8Array): Result<Uint8Array>;
|
|
8165
8321
|
|
|
8322
|
+
/**
|
|
8323
|
+
* Which constraint the provider was **asked** to apply to this response.
|
|
8324
|
+
*
|
|
8325
|
+
* @remarks
|
|
8326
|
+
* Three questions hide inside *"did it honour my schema"*, and they have different
|
|
8327
|
+
* owners:
|
|
8328
|
+
*
|
|
8329
|
+
* | question | answerable by |
|
|
8330
|
+
* |---|---|
|
|
8331
|
+
* | did we send a constraint? | this client, at request-build time |
|
|
8332
|
+
* | which constraint did the provider apply? | this client, from the resolved model's capability |
|
|
8333
|
+
* | does *this response* conform to my shape? | the caller's converter, and nothing else |
|
|
8334
|
+
*
|
|
8335
|
+
* This type answers the first two and deliberately not the third. Reporting
|
|
8336
|
+
* conformance would mean re-validating against the caller's own schema to
|
|
8337
|
+
* re-derive an answer the caller already holds.
|
|
8338
|
+
*
|
|
8339
|
+
* - `'none'` — nothing was sent; the resolved model declares no capability.
|
|
8340
|
+
* - `'json-mode'` — syntactically valid JSON is guaranteed; the shape is not.
|
|
8341
|
+
* - `'schema'` — generation was constrained to the supplied schema.
|
|
8342
|
+
* - `'tool-forced'` — Anthropic-style forced tool use; the shape comes from the
|
|
8343
|
+
* forced tool's input schema, and `content` is the re-serialized tool input.
|
|
8344
|
+
* @public
|
|
8345
|
+
*/
|
|
8346
|
+
declare type StructuredOutputEnforcement = 'none' | 'json-mode' | 'schema' | 'tool-forced';
|
|
8347
|
+
|
|
8348
|
+
/**
|
|
8349
|
+
* What to do when the resolved model cannot apply the requested constraint.
|
|
8350
|
+
*
|
|
8351
|
+
* @remarks
|
|
8352
|
+
* `'degrade'` is the default, and it is only safe **because
|
|
8353
|
+
* `IAiCompletionResponse.structuredOutput` is required** rather than
|
|
8354
|
+
* optional. Degrade-and-tell-me is safe; degrade-silently is the failure this
|
|
8355
|
+
* whole surface exists to remove — so the two decisions are one decision, not
|
|
8356
|
+
* two independent ones.
|
|
8357
|
+
*
|
|
8358
|
+
* Reach for `'fail'` when the output is persisted or put on a wire, where an
|
|
8359
|
+
* unconstrained generation that happens to parse is worse than an error because
|
|
8360
|
+
* it is wrong quietly. Leave it at `'degrade'` on paths that are *designed* to
|
|
8361
|
+
* degrade — an extractor that may return nothing, a segmenter that floors to a
|
|
8362
|
+
* mechanical chunker — where a hard failure would make this library less safe
|
|
8363
|
+
* than the code it replaces.
|
|
8364
|
+
* @public
|
|
8365
|
+
*/
|
|
8366
|
+
declare type StructuredOutputFallback = 'degrade' | 'fail';
|
|
8367
|
+
|
|
8368
|
+
/**
|
|
8369
|
+
* A caller's structured-output intent.
|
|
8370
|
+
*
|
|
8371
|
+
* @remarks
|
|
8372
|
+
* A discriminated union rather than an optional `schema` whose absence means
|
|
8373
|
+
* *"json-object please"* — an absence that means something is the shape this repo
|
|
8374
|
+
* has been burned by (see `MemoryEmbedOutcome` in `@fgv/ts-agent-memory`, which
|
|
8375
|
+
* exists because a three-ways-ambiguous absence could not be read).
|
|
8376
|
+
*
|
|
8377
|
+
* **The caller supplies intent; the response reports outcome.** A request never
|
|
8378
|
+
* needs to know whether the constraint will be honoured, because
|
|
8379
|
+
* `resolveProviderModel` resolves aliases and tiers at *call* time — a `tier`
|
|
8380
|
+
* request can cascade — so the concrete model that will serve a request is not
|
|
8381
|
+
* knowable to the caller up front. Requiring it to know would be unsound, which
|
|
8382
|
+
* is why the report rides on the response rather than being a lookup.
|
|
8383
|
+
* @public
|
|
8384
|
+
*/
|
|
8385
|
+
declare type StructuredOutputRequest = ISchemaStructuredOutputRequest | IJsonObjectStructuredOutputRequest;
|
|
8386
|
+
|
|
8166
8387
|
/**
|
|
8167
8388
|
* URL schemes this primitive will ever request.
|
|
8168
8389
|
*
|
|
@@ -8195,6 +8416,12 @@ declare function supportsEmbedding(descriptor: IAiProviderDescriptor): boolean;
|
|
|
8195
8416
|
*/
|
|
8196
8417
|
declare function supportsImageGeneration(descriptor: IAiProviderDescriptor): boolean;
|
|
8197
8418
|
|
|
8419
|
+
/**
|
|
8420
|
+
* Whether `descriptor` declares any structured-output capability at all.
|
|
8421
|
+
* @public
|
|
8422
|
+
*/
|
|
8423
|
+
declare function supportsStructuredOutput(descriptor: IAiProviderDescriptor): boolean;
|
|
8424
|
+
|
|
8198
8425
|
/**
|
|
8199
8426
|
* Helper function to create a `StringConverter` which converts
|
|
8200
8427
|
* `unknown` to `string`, applying template conversions supplied at construction time or at
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Logging, Result } from '@fgv/ts-utils';
|
|
2
2
|
import { type AiServerToolConfig, type IAiCompletionResponse, type IAiProviderDescriptor, type IChatRequest, type IThinkingConfig, type ModelSpec } from './model';
|
|
3
|
+
import type { StructuredOutputRequest } from './structuredOutputTypes';
|
|
3
4
|
/**
|
|
4
5
|
* Parameters for a provider completion request. Carries the unified
|
|
5
6
|
* {@link AiAssist.IChatRequest} shape (`system?` + ordered `messages`, last =
|
|
@@ -52,6 +53,18 @@ export interface IProviderCompletionParams extends IChatRequest {
|
|
|
52
53
|
* Messages API requires the field, so it falls back to `DEFAULT_ANTHROPIC_MAX_TOKENS`.
|
|
53
54
|
*/
|
|
54
55
|
readonly maxTokens?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Ask the provider to constrain its output — to a schema, or to syntactically
|
|
58
|
+
* valid JSON of arbitrary shape.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* **The caller supplies intent; the response reports outcome.** A caller cannot
|
|
62
|
+
* know up front which concrete model will serve the request (a `tier` request
|
|
63
|
+
* cascades, and aliases resolve at call time), so it never has to: whatever was
|
|
64
|
+
* actually enforced comes back on
|
|
65
|
+
* `IAiCompletionResponse.structuredOutput`.
|
|
66
|
+
*/
|
|
67
|
+
readonly structuredOutput?: StructuredOutputRequest;
|
|
55
68
|
}
|
|
56
69
|
/**
|
|
57
70
|
* Calls the appropriate chat completion API for a given provider. Routes by
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completionClient.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/completionClient.ts"],"names":[],"mappings":"AA8BA,OAAO,
|
|
1
|
+
{"version":3,"file":"completionClient.d.ts","sourceRoot":"","sources":["../../../src/packlets/ai-assist/completionClient.ts"],"names":[],"mappings":"AA8BA,OAAO,EAGL,KAAK,OAAO,EACZ,MAAM,EAIP,MAAM,eAAe,CAAC;AAEvB,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE1B,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,SAAS,EAMf,MAAM,SAAS,CAAC;AAiCjB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAMvE;;;;;GAKG;AACH,MAAM,WAAW,yBAA0B,SAAQ,YAAY;IAC7D,8BAA8B;IAC9B,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACxC,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAClC,uGAAuG;IACvG,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACnD,kEAAkE;IAClE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;CACrD;AAigBD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAiLxC;AAMD;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAiHxC"}
|