dsh-model-groups 0.3.9 → 0.3.10
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/CHANGELOG.md +25 -0
- package/README.md +10 -2
- package/docs/capabilities-beta.md +34 -0
- package/docs/maintenance.md +6 -0
- package/lib/client.js +1 -1
- package/native/pi-ai.patch.json +1336 -53
- package/native/settings-models.patch.json +613 -1
- package/package.json +2 -1
- package/scripts/settings-capabilities.mjs +37 -0
- package/src/capability-defaults.js +93 -0
package/native/pi-ai.patch.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"packageName": "@deepseek-ai/dsh-llm-pi-ai",
|
|
4
4
|
"target": "node_modules/@deepseek-ai/dsh-llm-pi-ai/lib/index.js",
|
|
5
5
|
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
6
|
-
"resultSha256": "
|
|
6
|
+
"resultSha256": "41db75564a03d9f97889ab9305414147be719b2d8063d86566b59dd1e7ae2a5c",
|
|
7
7
|
"variants": [
|
|
8
8
|
{
|
|
9
9
|
"source": "multi-key revision before per-entry API overrides",
|
|
10
10
|
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
11
|
-
"resultSha256": "
|
|
11
|
+
"resultSha256": "e8399ddb7b55a89b5ba8f5628ee78523bf51dc7ff7c175d1d841bd22a3cf0bf2",
|
|
12
12
|
"replacements": [
|
|
13
13
|
{
|
|
14
14
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -88,11 +88,11 @@
|
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
91
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
91
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
95
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
95
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -129,13 +129,33 @@
|
|
|
129
129
|
{
|
|
130
130
|
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
131
131
|
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"before": "const modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields\n});",
|
|
135
|
+
"after": "function applyModelCapabilities(model, defaults) {\n const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);\n const overrides = model.capabilityOverrides;\n if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {\n throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');\n }\n if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {\n throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');\n }\n const { capabilityOverrides: ignored, ...effective } = model;\n if (defaults === undefined) return effective;\n const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];\n const vision = choice('vision');\n if (vision !== undefined) {\n const input = Array.isArray(model.input) ? [...model.input] : ['text'];\n effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];\n if (effective.input.length === 0) effective.input.push('text');\n if (vision && !effective.input.includes('image')) effective.input.push('image');\n }\n const reasoning = choice('reasoning');\n if (reasoning === false) effective.reasoningEfforts = false;\n if (reasoning === true) {\n const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n const efforts = model.reasoningEfforts;\n const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');\n effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };\n }\n return effective;\n}\nconst capabilityDefaultsProfile = z.union([z.object({ vision: z.boolean().required(), reasoning: z.boolean().required() })]);\nconst capabilityOverridesProfile = z.union([z.object({ vision: z.union([z.boolean(), z.const(\"original\")]), reasoning: z.union([z.boolean(), z.const(\"original\")]) })]);\nconst modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields,\n\tcapabilityOverrides: capabilityOverridesProfile\n});"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"before": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()",
|
|
139
|
+
"after": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tcapabilityDefaults: capabilityDefaultsProfile,\n\tmodels: z.array(modelProfile).required()"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"before": "\t\tassertWireModels(source.models, \"models\");",
|
|
143
|
+
"after": "\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const model of source.models ?? []) applyModelCapabilities(model);"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"before": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);",
|
|
147
|
+
"after": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t\tapplyModelCapabilities({}, entry.capabilityDefaults);\n\t\t\tfor (const model of entry.models) applyModelCapabilities(model, entry.capabilityDefaults);"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"before": "\t\t\t\tmodels: entry.models,",
|
|
151
|
+
"after": "\t\t\t\tmodels: entry.models.map(model => applyModelCapabilities(model, entry.capabilityDefaults)),"
|
|
132
152
|
}
|
|
133
153
|
]
|
|
134
154
|
},
|
|
135
155
|
{
|
|
136
156
|
"source": "multi-key revision before per-entry API overrides",
|
|
137
157
|
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
138
|
-
"resultSha256": "
|
|
158
|
+
"resultSha256": "759e4bf808f126e3f9509cf3fe48c8bbf0c2e72f7b9519da8215be516957d34d",
|
|
139
159
|
"replacements": [
|
|
140
160
|
{
|
|
141
161
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -215,11 +235,11 @@
|
|
|
215
235
|
},
|
|
216
236
|
{
|
|
217
237
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
218
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
238
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
219
239
|
},
|
|
220
240
|
{
|
|
221
241
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
222
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
242
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
223
243
|
},
|
|
224
244
|
{
|
|
225
245
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -252,13 +272,164 @@
|
|
|
252
272
|
{
|
|
253
273
|
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
254
274
|
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
278
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"before": "const modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields\n});",
|
|
282
|
+
"after": "function applyModelCapabilities(model, defaults) {\n const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);\n const overrides = model.capabilityOverrides;\n if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {\n throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');\n }\n if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {\n throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');\n }\n const { capabilityOverrides: ignored, ...effective } = model;\n if (defaults === undefined) return effective;\n const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];\n const vision = choice('vision');\n if (vision !== undefined) {\n const input = Array.isArray(model.input) ? [...model.input] : ['text'];\n effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];\n if (!effective.input.includes('text')) effective.input.unshift('text');\n if (vision && !effective.input.includes('image')) effective.input.push('image');\n }\n const reasoning = choice('reasoning');\n if (reasoning === false) effective.reasoningEfforts = false;\n if (reasoning === true) {\n const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n const efforts = model.reasoningEfforts;\n const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');\n effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };\n }\n return effective;\n}\nconst capabilityDefaultsProfile = z.union([z.object({ vision: z.boolean().required(), reasoning: z.boolean().required() })]);\nconst capabilityOverridesProfile = z.union([z.object({ vision: z.union([z.boolean(), z.const(\"original\")]), reasoning: z.union([z.boolean(), z.const(\"original\")]) })]);\nconst modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields,\n\tcapabilityOverrides: capabilityOverridesProfile\n});"
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"before": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()",
|
|
286
|
+
"after": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tcapabilityDefaults: capabilityDefaultsProfile,\n\tmodels: z.array(modelProfile).required()"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"before": "\t\tassertWireModels(source.models, \"models\");",
|
|
290
|
+
"after": "\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const model of source.models ?? []) applyModelCapabilities(model);"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
"before": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);",
|
|
294
|
+
"after": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t\tapplyModelCapabilities({}, entry.capabilityDefaults);\n\t\t\tfor (const model of entry.models) applyModelCapabilities(model, entry.capabilityDefaults);"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"before": "\t\t\t\tmodels: entry.models,",
|
|
298
|
+
"after": "\t\t\t\tmodels: entry.models.map(model => applyModelCapabilities(model, entry.capabilityDefaults)),"
|
|
255
299
|
}
|
|
256
300
|
]
|
|
257
301
|
},
|
|
258
302
|
{
|
|
259
303
|
"source": "multi-key revision before per-entry API overrides",
|
|
260
304
|
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
261
|
-
"resultSha256": "
|
|
305
|
+
"resultSha256": "7c91a878438fd82f047e365a07235aa0aabd62fb749a1bc0292fef91e0f16809",
|
|
306
|
+
"replacements": [
|
|
307
|
+
{
|
|
308
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
309
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
313
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
317
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
321
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
325
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
329
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
333
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
337
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
341
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
345
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
349
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
353
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
357
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
361
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
365
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
369
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
373
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
377
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
381
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
385
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
389
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
393
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
397
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
401
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
405
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
409
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
413
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
417
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
421
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
425
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
426
|
+
}
|
|
427
|
+
]
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
"source": "multi-key revision before per-entry API overrides",
|
|
431
|
+
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
432
|
+
"resultSha256": "bd5b251b3d1d8441ee7aa552b537d0331b3f8f78c0f9f26f3b5035e5a36fe5ed",
|
|
262
433
|
"replacements": [
|
|
263
434
|
{
|
|
264
435
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -363,13 +534,29 @@
|
|
|
363
534
|
{
|
|
364
535
|
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
365
536
|
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
540
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
544
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
548
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
552
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
366
553
|
}
|
|
367
554
|
]
|
|
368
555
|
},
|
|
369
556
|
{
|
|
370
557
|
"source": "multi-key revision before per-entry API overrides",
|
|
371
558
|
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
372
|
-
"resultSha256": "
|
|
559
|
+
"resultSha256": "56b60061cc2ca642d3572214a40dbd679390c7f5427e739a11b3230ac2169ded",
|
|
373
560
|
"replacements": [
|
|
374
561
|
{
|
|
375
562
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -461,7 +648,7 @@
|
|
|
461
648
|
},
|
|
462
649
|
{
|
|
463
650
|
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
464
|
-
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api
|
|
651
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
465
652
|
},
|
|
466
653
|
{
|
|
467
654
|
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
@@ -474,13 +661,25 @@
|
|
|
474
661
|
{
|
|
475
662
|
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
476
663
|
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
667
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
671
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
675
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
477
676
|
}
|
|
478
677
|
]
|
|
479
678
|
},
|
|
480
679
|
{
|
|
481
680
|
"source": "multi-key revision before per-entry API overrides",
|
|
482
681
|
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
483
|
-
"resultSha256": "
|
|
682
|
+
"resultSha256": "7c234a4d68794ee54112583c4d4bfaf7c9819b9227c3149f58fa7584883c7aff",
|
|
484
683
|
"replacements": [
|
|
485
684
|
{
|
|
486
685
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -547,23 +746,867 @@
|
|
|
547
746
|
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
548
747
|
},
|
|
549
748
|
{
|
|
550
|
-
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
551
|
-
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
749
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
750
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
754
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
758
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
762
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
766
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
770
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
774
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
778
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
782
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
786
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
787
|
+
}
|
|
788
|
+
]
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
"source": "multi-key revision before per-entry API overrides",
|
|
792
|
+
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
793
|
+
"resultSha256": "a7cd7d9731924338995b4693f180fb2986887d7908a4f56c13132461cacc4bb1",
|
|
794
|
+
"replacements": [
|
|
795
|
+
{
|
|
796
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
797
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
801
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
805
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
809
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
813
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
817
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
821
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
825
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
829
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
833
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
837
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
838
|
+
},
|
|
839
|
+
{
|
|
840
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
841
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
845
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
849
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
853
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
857
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
861
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
865
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
869
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
873
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
877
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
881
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
885
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api ?? selectedStored?.api ?? \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
889
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
893
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
897
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
898
|
+
}
|
|
899
|
+
]
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
"source": "multi-key revision before per-entry API overrides",
|
|
903
|
+
"baseSha256": "070c839e2c00e2d5f7c5d82307ca3d84dbb2323e46979daf265908751bbe8ae8",
|
|
904
|
+
"resultSha256": "a2b3bcda38015026ed58074cef0d508bbd2d7cb1423c00331651fcbd1a9f6b55",
|
|
905
|
+
"replacements": [
|
|
906
|
+
{
|
|
907
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
908
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
912
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
916
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
920
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
924
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
925
|
+
},
|
|
926
|
+
{
|
|
927
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
928
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
932
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
936
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
940
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
944
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
948
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
952
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
956
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
960
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
964
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
968
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
972
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
976
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
980
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
981
|
+
}
|
|
982
|
+
]
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation",
|
|
986
|
+
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
987
|
+
"resultSha256": "c87f965d9fcc5bbcd386e98ea3b292b05ec7a55d80df1266e18234031b80990e",
|
|
988
|
+
"replacements": [
|
|
989
|
+
{
|
|
990
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
991
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
995
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
999
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
1003
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
1007
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
1011
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
1012
|
+
},
|
|
1013
|
+
{
|
|
1014
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
1015
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
1019
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
1023
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
1027
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
1031
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
1035
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
1039
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
1043
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
1047
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
1051
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
1052
|
+
},
|
|
1053
|
+
{
|
|
1054
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
1055
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
1059
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
1063
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1064
|
+
}
|
|
1065
|
+
]
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
"source": "npm official 0.2.2-rc.1",
|
|
1069
|
+
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
1070
|
+
"resultSha256": "b9416207cc7c27aff40cb2b877c9176e53809eb2f6dc741828c2ed6f6bdf541b",
|
|
1071
|
+
"replacements": [
|
|
1072
|
+
{
|
|
1073
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
1074
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
1078
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
1079
|
+
},
|
|
1080
|
+
{
|
|
1081
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
1082
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
1086
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
1090
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
1094
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
1098
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
1102
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
1106
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
1110
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
1114
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
1118
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
1122
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
1126
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
1130
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
1134
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
1138
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
1142
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
1146
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1147
|
+
}
|
|
1148
|
+
]
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
1152
|
+
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
1153
|
+
"resultSha256": "5ea8e84da726c04caa6bb321cf41617051ad9a80444834328007643b09b43a32",
|
|
1154
|
+
"replacements": [
|
|
1155
|
+
{
|
|
1156
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
1157
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
1158
|
+
},
|
|
1159
|
+
{
|
|
1160
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
1161
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
1165
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
1169
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
1173
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
1177
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
1181
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
1185
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
1189
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
1193
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
1197
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
1201
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
1205
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
1209
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
1213
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
1217
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
1221
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
1225
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
1229
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1230
|
+
},
|
|
1231
|
+
{
|
|
1232
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1233
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1237
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
1241
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
1245
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api ?? selectedStored?.api ?? \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
1249
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
1253
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
1257
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
1258
|
+
}
|
|
1259
|
+
]
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
1263
|
+
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
1264
|
+
"resultSha256": "9ca2b07c19694a4521a29c82b7948fb6326d80ee7741200a1d7b0f3f06e1da19",
|
|
1265
|
+
"replacements": [
|
|
1266
|
+
{
|
|
1267
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
1268
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
1272
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
1276
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
1277
|
+
},
|
|
1278
|
+
{
|
|
1279
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
1280
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
1281
|
+
},
|
|
1282
|
+
{
|
|
1283
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
1284
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
1288
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
1289
|
+
},
|
|
1290
|
+
{
|
|
1291
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
1292
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
1296
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
1300
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
1304
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
1308
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
1312
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
1313
|
+
},
|
|
1314
|
+
{
|
|
1315
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
1316
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
1320
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
1324
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
1328
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
1332
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
1336
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
1340
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1344
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1348
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1349
|
+
},
|
|
1350
|
+
{
|
|
1351
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
1352
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
1356
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
1360
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
1364
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
1365
|
+
},
|
|
1366
|
+
{
|
|
1367
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
1368
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
1369
|
+
}
|
|
1370
|
+
]
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation with per-entry API overrides",
|
|
1374
|
+
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
1375
|
+
"resultSha256": "e999e1a7f2352177c653ff0cea6485c3b1d1ea95b40ea613fc0549e2c8e49662",
|
|
1376
|
+
"replacements": [
|
|
1377
|
+
{
|
|
1378
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
1379
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
1380
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
1383
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
1384
|
+
},
|
|
1385
|
+
{
|
|
1386
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
1387
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
1391
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
1395
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
1399
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
1403
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
1407
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
1411
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
1415
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
1419
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
1423
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
1427
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
1428
|
+
},
|
|
1429
|
+
{
|
|
1430
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
1431
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
1432
|
+
},
|
|
1433
|
+
{
|
|
1434
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
1435
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
1439
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
1440
|
+
},
|
|
1441
|
+
{
|
|
1442
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
1443
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
1447
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
1451
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1455
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1459
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
1463
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
1467
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
1468
|
+
},
|
|
1469
|
+
{
|
|
1470
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
1471
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
1475
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
1476
|
+
},
|
|
1477
|
+
{
|
|
1478
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
1479
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
1480
|
+
}
|
|
1481
|
+
]
|
|
1482
|
+
},
|
|
1483
|
+
{
|
|
1484
|
+
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation with per-entry API overrides",
|
|
1485
|
+
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
1486
|
+
"resultSha256": "0a398e35a7e66f0cb547da9c421e33123b9959bd058c3d006baf3c587ff4c596",
|
|
1487
|
+
"replacements": [
|
|
1488
|
+
{
|
|
1489
|
+
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
1490
|
+
"after": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\n// Model selection carries an opaque entry identity, never a credential or a wire alias.\nconst KEY_MODEL_PREFIX = \"@dsh-key/\";\nconst KEY_ENTRY_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;\nconst FORBIDDEN_KEY_ENTRY_IDS = new Set([\"__proto__\", \"prototype\", \"constructor\"]);\nconst AUTH_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\", \"cf-aig-authorization\"]);\nfunction validKeyEntryId(id) {\n\treturn KEY_ENTRY_ID.test(id) && !FORBIDDEN_KEY_ENTRY_IDS.has(id);\n}\nfunction keyModelId(entryId, model) {\n\treturn `${KEY_MODEL_PREFIX}${encodeURIComponent(entryId)}/${encodeURIComponent(model)}`;\n}\nfunction keyModelRoute(model) {\n\tif (!model.startsWith(KEY_MODEL_PREFIX)) return void 0;\n\ttry {\n\t\tconst parts = model.slice(KEY_MODEL_PREFIX.length).split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\tconst entryId = decodeURIComponent(parts[0]);\n\t\t\tconst wireModel = decodeURIComponent(parts[1]);\n\t\t\tif (validKeyEntryId(entryId) && wireModel.length > 0 && !wireModel.startsWith(KEY_MODEL_PREFIX) && keyModelId(entryId, wireModel) === model) return { entryId, wireModel };\n\t\t}\n\t} catch {}\n\tthrow new LlmError(`invalid multi-key model selection \"${model}\"`, \"UNKNOWN_MODEL\");\n}\nconst keyEntryProfile = z.object({\n\tname: z.string().max(120).required(),\n\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});\nconst profile = z.object({\n\tmultiKey: z.boolean().default(false),\n\tkeyEntries: z.dict(keyEntryProfile).default({}),\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n"
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
"before": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\") {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n",
|
|
1494
|
+
"after": "* @param providers - configured provider profiles keyed by route.\n* @param validation - writes require a complete catalog; stored reads retain catalog diagnostics.\n* @returns validated profiles in configuration order.\n*/\nfunction resolveProfiles(providers, validation = \"strict\", forKeyEntry = false) {\n\tif (Array.isArray(providers)) throw new Error(\"llm-pi-ai: providers is now a dict keyed by provider route, not an array of profiles\");\n\tconst entries = Object.entries(providers ?? {});\n\tconst resolved = /* @__PURE__ */ new Map();\n\tfor (const [provider, source] of entries) {\n"
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
"before": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n",
|
|
1498
|
+
"after": "\t\tif (provider.length === 0) throw new Error(\"llm-pi-ai: provider names must be non-empty\");\n\t\tif (source.baseURL !== void 0 && source.baseURL.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty baseURL`);\n\t\tif (source.displayName !== void 0 && source.displayName.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" has an empty displayName`);\n\t\tassertValidHeaders(provider, source.headers);\n\t\tconst entrySources = source.keyEntries ?? {};\n\t\tif (typeof entrySources !== \"object\" || entrySources === null || Array.isArray(entrySources)) throw new Error(`llm-pi-ai: provider \"${provider}\" keyEntries must be a dictionary`);\n\t\tconst assertWireModels = (models, site) => {\n\t\t\tfor (const model of models ?? []) if (typeof model.id !== \"string\" || model.id.length === 0 || model.id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} must use non-empty upstream model ids outside the reserved ${KEY_MODEL_PREFIX} prefix`);\n\t\t};\n\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const id of Object.keys(source.modelOverrides ?? {})) if (id.startsWith(KEY_MODEL_PREFIX)) throw new Error(`llm-pi-ai: provider \"${provider}\" modelOverrides uses reserved prefix ${KEY_MODEL_PREFIX}`);\n\t\tfor (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tif (!validKeyEntryId(id)) throw new Error(`llm-pi-ai: provider \"${provider}\" has an invalid key entry id \"${id}\"`);\n\t\t\tif (typeof entry !== \"object\" || entry === null || typeof entry.name !== \"string\" || entry.name.trim().length === 0 || entry.name.length > 120) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs a name of 1-120 characters`);\n\t\t\tif (typeof entry.apiKeyEnv !== \"string\" || entry.apiKeyEnv.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own apiKeyEnv`);\n\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" needs its own models list`);\n\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t}\n\t\tconst assertEntryHeaders = (headers, site) => {\n\t\t\tfor (const name of Object.keys(headers ?? {})) if (AUTH_HEADER_NAMES.has(name.toLowerCase())) throw new Error(`llm-pi-ai: provider \"${provider}\" ${site} authentication header \"${name}\" would override the selected key; remove it before enabling multiKey`);\n\t\t};\n\t\tif (source.multiKey === true || forKeyEntry) {\n\t\t\tassertEntryHeaders(source.headers, \"shared\");\n\t\t\tfor (const model of source.models ?? []) assertEntryHeaders(model.headers, `model \"${model.id}\"`);\n\t\t\tfor (const [id, override] of Object.entries(source.modelOverrides ?? {})) assertEntryHeaders(override.headers, `model override \"${id}\"`);\n\t\t}\n\t\tconst streamIdleTimeoutMs = source.streamIdleTimeoutMs ?? 3e5;\n\t\tif (!Number.isFinite(streamIdleTimeoutMs) || streamIdleTimeoutMs <= 0 || streamIdleTimeoutMs > MAX_TIMER_DELAY_MS) throw new Error(`llm-pi-ai: provider \"${provider}\" streamIdleTimeoutMs must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);\n\t\tconst maxRequestImageBytes = source.maxRequestImageBytes ?? 20971520;\n\t\tif (!Number.isInteger(maxRequestImageBytes) || maxRequestImageBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" maxRequestImageBytes must be a positive integer`);\n"
|
|
1499
|
+
},
|
|
1500
|
+
{
|
|
1501
|
+
"before": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\ttry {\n\t\t\tcatalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n",
|
|
1502
|
+
"after": "\t\tif (!Number.isSafeInteger(requestImageMaxBytes) || requestImageMaxBytes <= 0) throw new Error(`llm-pi-ai: provider \"${provider}\" requestImageMaxBytes must be a positive safe integer`);\n\t\tconst defaultInput = [...source.defaultInput ?? DEFAULT_INPUT];\n\t\tif (defaultInput.length === 0) throw new Error(`llm-pi-ai: provider \"${provider}\" defaultInput must name at least one modality`);\n\t\tconst displayName = source.displayName ?? provider;\n\t\tconst keyEntries = new Map();\n\t\t// Disabled entries remain stored, but cannot constrain legacy single-key runtime behavior.\n\t\tif (source.multiKey === true) for (const [id, entry] of Object.entries(entrySources)) {\n\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,\n\t\t\t\tkeyEntries: {},\n\t\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\t\tmodels: entry.models,\n\t\t\t\tmodelOverrides: void 0\n\t\t\t} }, validation, true).get(provider);\n\t\t\t// Entry auth must not consult a catalog provider's ambient/OAuth credential or endpoint.\n\t\t\tkeyEntries.set(id, {\n\t\t\t\t...child,\n\t\t\t\tkeyEntryId: id,\n\t\t\t\tkeyEntryName: entry.name,\n\t\t\t\t...child.piProvider === void 0 ? {} : { piProvider: { ...child.piProvider, auth: { apiKey: harnessApiKeyAuth(entry.name) } } }\n\t\t\t});\n\t\t}\n\t\tlet catalog;\n\t\tlet piProvider;\n\t\tlet catalogError;\n\t\tconst entryOnly = forKeyEntry && source.models.length === 0 || source.multiKey === true && keyEntries.size > 0 && (source.models ?? []).length === 0 && catalogModels(provider).size === 0;\n\t\ttry {\n\t\t\tif (entryOnly) catalog = { models: [], configuredMaxTokens: new Map(), modelErrors: new Map() };\n\t\t\telse catalog = resolveRouteModels({\n\t\t\t\tprovider,\n\t\t\t\t...source.api === void 0 ? {} : { api: source.api },\n\t\t\t\t...source.baseURL === void 0 ? {} : { baseURL: source.baseURL },\n\t\t\t\t...source.models === void 0 ? {} : { models: source.models },\n"
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
"before": "\t\t\t}, validation);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;",
|
|
1506
|
+
"after": "\t\t\t}, validation);\n\t\t\tif (source.multiKey === true || forKeyEntry) for (const model of catalog.models) assertEntryHeaders(model.headers, `resolved model \"${model.id}\"`);\n\t\t\tcatalogError = catalog.modelErrors.values().next().value;"
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
"before": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },",
|
|
1510
|
+
"after": "\t\tconst { apiKeyEnv, retryPolicy, models: _models, displayName: _displayName, keyEntries: _keyEntries, ...rest } = source;\n\t\tresolved.set(provider, {\n\t\t\t...rest,\n\t\t\tprovider,\n\t\t\tdisplayName,\n\t\t\tkeyEntries,\n\t\t\t...apiKeyEnv === void 0 ? {} : { apiKeyEnv: credentialRef(apiKeyEnv) },"
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
"before": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/**\n* pi-ai-backed multi-provider adapter.",
|
|
1514
|
+
"after": "\t} catch (error) {\n\t\tthrow new LlmError(`pi-ai request header preparation failed for provider \"${options.provider}\": ${requestHeaderDiagnostic(error)}`, \"REQUEST_HEADERS\", { cause: error });\n\t}\n}\n/** Decode only the selected replay owner at the pi boundary; never merge equal wire ids across keys. */\nfunction replayContextForRoute(context, provider, requestedModel, wireModel) {\n\tconst selectedEntry = requestedModel.startsWith(KEY_MODEL_PREFIX);\n\treturn {\n\t\t...context,\n\t\tmessages: context.messages.map((message) => {\n\t\t\tif (message.role !== \"assistant\") return message;\n\t\t\tif (message.provider === provider && message.model === requestedModel) return { ...message, model: wireModel.id };\n\t\t\tif (!message.model.startsWith(KEY_MODEL_PREFIX) && !(selectedEntry && message.provider === provider && message.model === wireModel.id)) return message;\n\t\t\treturn {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tapi: \"dsh-foreign\",\n\t\t\t\tprovider: message.provider,\n\t\t\t\tmodel: \"dsh-foreign\",\n\t\t\t\tcontent: message.content.flatMap((block) => {\n\t\t\t\t\tif (block.type === \"thinking\") return [];\n\t\t\t\t\tif (block.type === \"text\") return [{ type: \"text\", text: block.text }];\n\t\t\t\t\tif (block.type === \"toolCall\") return [{ type: \"toolCall\", id: block.id, name: block.name, arguments: block.arguments }];\n\t\t\t\t\treturn [block];\n\t\t\t\t}),\n\t\t\t\tusage: message.usage,\n\t\t\t\tstopReason: message.stopReason,\n\t\t\t\ttimestamp: message.timestamp\n\t\t\t};\n\t\t})\n\t};\n}\n/**\n* pi-ai-backed multi-provider adapter."
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
"before": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tfor (const profile of profiles.values()) if (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n",
|
|
1518
|
+
"after": "\tcurrent() {\n\t\tconst profiles = this.config.profiles();\n\t\tif (this.snapshot?.profiles === profiles) return this.snapshot;\n\t\tconst models = createModels(this.config.auth);\n\t\tconst entryModels = new Map();\n\t\tfor (const [provider, profile] of profiles) {\n\t\t\tif (profile.piProvider !== void 0) models.setProvider(profile.piProvider);\n\t\t\tconst entries = new Map();\n\t\t\tfor (const [id, entry] of profile.keyEntries) {\n\t\t\t\tconst collection = createModels(this.config.auth);\n\t\t\t\tif (entry.piProvider !== void 0) collection.setProvider(entry.piProvider);\n\t\t\t\tentries.set(id, collection);\n\t\t\t}\n\t\t\tentryModels.set(provider, entries);\n\t\t}\n\t\tthis.snapshot = {\n\t\t\tprofiles,\n\t\t\tmodels,\n\t\t\tentryModels\n\t\t};\n\t\treturn this.snapshot;\n\t}\n\t/** The profile for one route within one snapshot, or the not-owned failure. */\n"
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
"before": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst failure = profile.modelErrors.get(model) ?? (profile.piProvider === void 0 ? profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = snapshot.models.getModel(provider, model);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n",
|
|
1522
|
+
"after": "\t\tconst profile = snapshot.profiles.get(provider);\n\t\tif (profile === void 0) throw new LlmError(`pi-ai adapter does not own provider \"${provider}\"`, \"NO_ADAPTER\");\n\t\treturn profile;\n\t}\n\t/** Bind the public selection to an exact profile, independent model collection, and upstream id. */\n\trouteOf(snapshot, provider, model) {\n\t\tconst root = this.profileOf(snapshot, provider);\n\t\tconst entry = keyModelRoute(model);\n\t\tif (entry === void 0) return { profile: root, models: snapshot.models, wireModel: model };\n\t\tif (root.multiKey !== true) throw new LlmError(`multi-key selection is disabled for provider \"${provider}\"`, \"UNKNOWN_MODEL\");\n\t\tconst profile = root.keyEntries.get(entry.entryId);\n\t\tconst models = snapshot.entryModels.get(provider)?.get(entry.entryId);\n\t\tif (profile === void 0 || models === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${entry.entryId}\"`, \"UNKNOWN_MODEL\");\n\t\treturn { profile, models, wireModel: entry.wireModel };\n\t}\n\t/** The configured descriptor for one exact route/model pair within one snapshot. */\n\tmodelOf(snapshot, provider, model) {\n\t\tconst route = this.routeOf(snapshot, provider, model);\n\t\tconst failure = route.profile.modelErrors.get(route.wireModel) ?? (route.profile.piProvider === void 0 ? route.profile.catalogError : void 0);\n\t\tif (failure !== void 0) throw new LlmError(failure, \"INVALID_CONFIG\");\n\t\tconst resolved = route.models.getModel(provider, route.wireModel);\n\t\tif (resolved === void 0) throw new LlmError(`pi-ai provider \"${provider}\" has no configured model \"${model}\"`, \"UNKNOWN_MODEL\");\n\t\treturn resolved;\n\t}\n\tproviderInfo(provider) {\n"
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
"before": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\ttry {\n\t\t\t\tawait this.config.resolveApiKey(provider, profile);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn snapshot.models.getModels(provider).map((model) => ({\n\t\t\t\tprovider,\n\t\t\t\tid: model.id,\n\t\t\t\tname: model.name,\n\t\t\t\tinputModalities: [...model.input]\n\t\t\t}));\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n",
|
|
1526
|
+
"after": "\tlistModels(provider) {\n\t\treturn Promise.resolve().then(async () => {\n\t\t\tconst snapshot = this.current();\n\t\t\tconst profile = this.profileOf(snapshot, provider);\n\t\t\tconst listed = [];\n\t\t\tconst append = async (owner, models, entryId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst key = await this.config.resolveApiKey(provider, owner);\n\t\t\t\t\tif (entryId !== void 0 && (key === void 0 || key.length === 0)) return;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof LlmError && error.code === \"MISSING_CREDENTIAL\") return;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tfor (const model of models.getModels(provider)) listed.push({\n\t\t\t\t\tprovider,\n\t\t\t\t\tid: entryId === void 0 ? model.id : keyModelId(entryId, model.id),\n\t\t\t\t\tname: model.name,\n\t\t\t\t\tinputModalities: [...model.input]\n\t\t\t\t});\n\t\t\t};\n\t\t\t// Keep raw root models advertised so old sessions remain routable without changing keys.\n\t\t\tawait append(profile, snapshot.models);\n\t\t\tif (profile.multiKey === true) for (const [id, entry] of profile.keyEntries) await append(entry, snapshot.entryModels.get(provider).get(id), id);\n\t\t\treturn listed;\n\t\t});\n\t}\n\tresolveModel(provider, model, _signal) {\n\t\treturn Promise.resolve().then(() => {\n"
|
|
1527
|
+
},
|
|
1528
|
+
{
|
|
1529
|
+
"before": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst profile = this.profileOf(snapshot, provider);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(model);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n",
|
|
1530
|
+
"after": "\t\t\treturn this.modelInfo(snapshot, provider, model);\n\t\t});\n\t}\n\tmodelInfo(snapshot, provider, model) {\n\t\tconst { profile, wireModel } = this.routeOf(snapshot, provider, model);\n\t\tconst resolvedModel = this.modelOf(snapshot, provider, model);\n\t\tconst defaultLevel = describableReasoningLevel(resolvedModel, profile.reasoning);\n\t\tconst configuredMaxTokens = profile.configuredMaxTokens.get(wireModel);\n\t\treturn {\n\t\t\tprovider,\n\t\t\tid: model,\n\t\t\tname: resolvedModel.name,\n"
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
"before": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => this.streamWithSnapshot(options, snapshot)\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot(options, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n",
|
|
1534
|
+
"after": "\tprepareCall(provider, model, _signal) {\n\t\tconst snapshot = this.current();\n\t\treturn Promise.resolve({\n\t\t\tmodel: this.modelInfo(snapshot, provider, model),\n\t\t\tstream: (options) => {\n\t\t\t\tif (options.provider !== provider || options.model !== model) throw new LlmError(\"prepared multi-key model selection changed before adapter dispatch\", \"INVALID_CONFIG\");\n\t\t\t\t// Async generators are lazy; pin the owner before returning one to caller-owned code.\n\t\t\t\treturn this.streamWithSnapshot({ ...options, provider, model }, snapshot);\n\t\t\t}\n\t\t});\n\t}\n\tstream(options) {\n\t\treturn this.streamWithSnapshot({ ...options }, this.current());\n\t}\n\tasync *streamWithSnapshot(options, snapshot) {\n\t\tconst env_1 = {\n\t\t\tstack: [],\n"
|
|
1535
|
+
},
|
|
1536
|
+
{
|
|
1537
|
+
"before": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst profile = this.profileOf(snapshot, options.provider);\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n",
|
|
1538
|
+
"after": "\t\t\thasError: false\n\t\t};\n\t\ttry {\n\t\t\tif (options.stop !== void 0) throw new LlmError(\"llm-pi-ai does not support GenerateOptions.stop\", \"UNSUPPORTED_OPTION\");\n\t\t\tconst route = this.routeOf(snapshot, options.provider, options.model);\n\t\t\tconst profile = route.profile;\n\t\t\tconst model = this.modelOf(snapshot, options.provider, options.model);\n\t\t\tconst updates = conversationUpdates(model);\n\t\t\tconst tools = options.tools ?? [];\n\t\t\tconst request = model.api === \"anthropic-messages\" && updates.toolUpdate === \"in-history\" && tools.every((tool) => tool.deferLoading === true) && options.messages.some((message) => message.role === \"developer\" && message.content.some((block) => block.type === \"tool-addition\" || block.type === \"tool-removal\")) ? {\n"
|
|
1539
|
+
},
|
|
1540
|
+
{
|
|
1541
|
+
"before": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n",
|
|
1542
|
+
"after": "\t\t\t\ttools: [ANTHROPIC_TOOL_UPDATE_PLACEHOLDER, ...tools]\n\t\t\t} : options;\n\t\t\tconst reasoning = resolveReasoningLevel(model, options.reasoningEffort ?? profile.reasoning);\n\t\t\tconst apiKey = await this.config.resolveApiKey(options.provider, profile);\n\t\t\tif (profile.keyEntryId !== void 0 && (apiKey === void 0 || apiKey.length === 0)) throw new LlmError(`provider \"${options.provider}\" key entry \"${profile.keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\tconst consumer = new AbortController();\n\t\t\tconst upstream = options.signal === void 0 ? consumer.signal : AbortSignal.any([options.signal, consumer.signal]);\n\t\t\tconst streamIdleTimeoutMs = profile.streamIdleTimeoutMs;\n\t\t\tconst watchdog = __addDisposableResource(env_1, idleWatchdog(upstream, streamIdleTimeoutMs, \"LLM_STREAM_IDLE_TIMEOUT\"), false);\n"
|
|
1543
|
+
},
|
|
1544
|
+
{
|
|
1545
|
+
"before": "\t\t\t\tconst iterator = toStreamChunks(snapshot.models.streamSimple(model, context, {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, model.id)[Symbol.asyncIterator]();",
|
|
1546
|
+
"after": "\t\t\t\tconst iterator = toStreamChunks(route.models.streamSimple(model, replayContextForRoute(context, options.provider, options.model, model), {\n\t\t\t\t\t...profileOptions(profile, reasoning, apiKey),\n\t\t\t\t\t...options.temperature === void 0 ? {} : { temperature: options.temperature },\n\t\t\t\t\t...options.maxTokens === void 0 ? {} : { maxTokens: options.maxTokens },\n\t\t\t\t\t...options.sessionId === void 0 ? {} : { sessionId: String(options.sessionId) },\n\t\t\t\t\tsignal: watchdog.signal,\n\t\t\t\t\theaders: requestHeaders(profile.headers, options, model.baseUrl, this.config.prepareHeaders)\n\t\t\t\t}), model.contextWindow, options.signal, options.model)[Symbol.asyncIterator]();"
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
"before": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n",
|
|
1550
|
+
"after": "* @throws LlmError when the protocol has no readable listing, the endpoint\n* refuses or fails the request, or the reply is not a model listing.\n*/\nasync function discoverModels(request, storedProfile) {\n\t// Validate host-owned entry identity even on the installed-catalog shortcut.\n\tconst selectedStored = request.keyEntryId === void 0 ? void 0 : storedProfile?.();\n\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {\n\t\tconst installed = catalogModels(request.provider);\n\t\tif (installed.size > 0) return [...installed.values()].map((model) => ({\n\t\t\tid: model.id,\n"
|
|
1551
|
+
},
|
|
1552
|
+
{
|
|
1553
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n",
|
|
1554
|
+
"after": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);\n\tconst stored = selectedStored ?? storedProfile?.();\n\tconst supplied = request.apiKey ?? await stored?.resolveApiKey();\n\tconst apiKey = supplied === void 0 ? void 0 : usableProbeKey(supplied);\n\tlet response;\n\ttry {\n"
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
"before": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n",
|
|
1558
|
+
"after": "function registrationFacts(profiles) {\n\treturn [...profiles.entries()].map(([provider, profile]) => ({\n\t\tprovider,\n\t\tdisplayName: profile.displayName,\n\t\tretryPolicy: profile.retryPolicy,\n\t\tmultiKey: profile.multiKey === true,\n\t\tapiKeyEnv: profile.apiKeyEnv,\n\t\tmodels: profile.piProvider?.getModels(),\n\t\tmodelErrors: [...profile.modelErrors],\n\t\tkeyEntries: [...profile.keyEntries].map(([id, entry]) => ({\n\t\t\tid,\n\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),\n\t\t\tmodelErrors: [...entry.modelErrors]\n\t\t})).sort((left, right) => left.id.localeCompare(right.id))\n\t})).sort((left, right) => left.provider.localeCompare(right.provider));\n}\n/**\n* The configurable-provider directory: every installed catalog route, plus\n"
|
|
1559
|
+
},
|
|
1560
|
+
{
|
|
1561
|
+
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
1562
|
+
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1566
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1567
|
+
},
|
|
1568
|
+
{
|
|
1569
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1570
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1571
|
+
},
|
|
1572
|
+
{
|
|
1573
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
1574
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
1578
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
1579
|
+
},
|
|
1580
|
+
{
|
|
1581
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
1582
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
1586
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
1590
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
1594
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
552
1595
|
},
|
|
553
1596
|
{
|
|
554
|
-
"before": "
|
|
555
|
-
"after": "
|
|
1597
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
1598
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
556
1599
|
},
|
|
557
1600
|
{
|
|
558
|
-
"before": "\
|
|
559
|
-
"after": "\
|
|
1601
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
1602
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
560
1603
|
}
|
|
561
1604
|
]
|
|
562
1605
|
},
|
|
563
1606
|
{
|
|
564
|
-
"source": "0.2.2-rc.1 with
|
|
565
|
-
"baseSha256": "
|
|
566
|
-
"resultSha256": "
|
|
1607
|
+
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
1608
|
+
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
1609
|
+
"resultSha256": "432b9050dff97c9dae6744b6c6189e0c4a4363321afab6310aec4d5fcb418681",
|
|
567
1610
|
"replacements": [
|
|
568
1611
|
{
|
|
569
1612
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -640,13 +1683,53 @@
|
|
|
640
1683
|
{
|
|
641
1684
|
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
642
1685
|
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1689
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1693
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
1697
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
1701
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
1705
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
1709
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
1713
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
1717
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
1721
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
1725
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
643
1726
|
}
|
|
644
1727
|
]
|
|
645
1728
|
},
|
|
646
1729
|
{
|
|
647
|
-
"source": "
|
|
648
|
-
"baseSha256": "
|
|
649
|
-
"resultSha256": "
|
|
1730
|
+
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation with per-entry API overrides",
|
|
1731
|
+
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
1732
|
+
"resultSha256": "c7d20ebe93753ae6f30d614121bac533fe5a8fa9372ea1c5fdc8bf2f7eba1d1d",
|
|
650
1733
|
"replacements": [
|
|
651
1734
|
{
|
|
652
1735
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -723,13 +1806,57 @@
|
|
|
723
1806
|
{
|
|
724
1807
|
"before": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider) => {\n\t\tif (provider === void 0) return void 0;\n\t\tconst profile = profiles().get(provider);\n\t\tif (profile === void 0) return void 0;\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: () => resolveApiKey(provider, profile)\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n",
|
|
725
1808
|
"after": "\t\tdirectoryFacts = entries;\n\t};\n\tensureDirectory();\n\t/** Host-owned request inputs for discovery of one configured route. */\n\tconst storedDiscoveryProfile = (provider, keyEntryId) => {\n\t\tif (provider === void 0) {\n\t\t\tif (keyEntryId !== void 0) throw new LlmError(\"multi-key discovery needs a provider id\", \"INVALID_CONFIG\");\n\t\t\treturn void 0;\n\t\t}\n\t\tconst root = profiles().get(provider);\n\t\tif (keyEntryId === void 0) return root === void 0 ? void 0 : { headers: root.headers, resolveApiKey: () => resolveApiKey(provider, root) };\n\t\tif (!validKeyEntryId(keyEntryId) || root?.multiKey !== true) throw new LlmError(`multi-key discovery is not enabled for provider \"${provider}\"`, \"INVALID_CONFIG\");\n\t\tconst profile = root.keyEntries.get(keyEntryId);\n\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,\n\t\t\tresolveApiKey: async () => {\n\t\t\t\tconst key = await resolveApiKey(provider, profile);\n\t\t\t\tif (key === void 0 || key.length === 0) throw new LlmError(`provider \"${provider}\" key entry \"${keyEntryId}\" has no credential`, \"MISSING_CREDENTIAL\");\n\t\t\t\treturn key;\n\t\t\t}\n\t\t};\n\t};\n\tctx.llm.registerModelDiscovery(settingsNs, (request, signal) => discoverModels({\n\t\t...request,\n\t\t...signal === void 0 ? {} : { signal }\n\t}, () => storedDiscoveryProfile(request.provider, request.keyEntryId)));\n\tlet registration;\n\tlet registeredFacts;\n\tconst ensureRegistrationFacts = () => {\n\t\tconst facts = registrationFacts(profiles());\n"
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1812
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1813
|
+
},
|
|
1814
|
+
{
|
|
1815
|
+
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1816
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1817
|
+
},
|
|
1818
|
+
{
|
|
1819
|
+
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
1820
|
+
"after": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\t...entry.api === void 0 ? {} : { api: entry.api },\n\t\t\t\tmultiKey: false,"
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
1824
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
1828
|
+
"after": "\tconst baseURL = request.baseURL ?? selectedStored?.baseURL;\n\tif (baseURL === void 0 || baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(baseURL, api);"
|
|
1829
|
+
},
|
|
1830
|
+
{
|
|
1831
|
+
"before": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tmodels: entry.piProvider?.getModels(),",
|
|
1832
|
+
"after": "\t\t\tname: entry.keyEntryName,\n\t\t\tapiKeyEnv: entry.apiKeyEnv,\n\t\t\tapi: entry.api,\n\t\t\tmodels: entry.piProvider?.getModels(),"
|
|
1833
|
+
},
|
|
1834
|
+
{
|
|
1835
|
+
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
1836
|
+
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
1837
|
+
},
|
|
1838
|
+
{
|
|
1839
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
1840
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
1841
|
+
},
|
|
1842
|
+
{
|
|
1843
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
1844
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
1848
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
1852
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
726
1853
|
}
|
|
727
1854
|
]
|
|
728
1855
|
},
|
|
729
1856
|
{
|
|
730
1857
|
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
731
1858
|
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
732
|
-
"resultSha256": "
|
|
1859
|
+
"resultSha256": "2e00ea0c1b447153e341a1ca675ed6a829094c7caf62c6524e6809bd6540be71",
|
|
733
1860
|
"replacements": [
|
|
734
1861
|
{
|
|
735
1862
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -821,7 +1948,7 @@
|
|
|
821
1948
|
},
|
|
822
1949
|
{
|
|
823
1950
|
"before": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tif (request.provider !== void 0) {",
|
|
824
|
-
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api
|
|
1951
|
+
"after": "\tif (request.keyEntryId !== void 0 && selectedStored === void 0) throw new LlmError(\"multi-key model discovery requires an existing provider key entry\", \"INVALID_CONFIG\");\n\tconst api = request.api !== void 0 ? request.api : selectedStored?.api !== void 0 ? selectedStored.api : \"openai-completions\";\n\tif (!supportedProtocols().includes(api)) throw new LlmError(`pi-ai model discovery api must be a supported protocol`, \"INVALID_CONFIG\");\n\t// A selected entry must probe its effective protocol, not a catalog for another API.\n\tconst entryProtocol = request.keyEntryId !== void 0 && (request.api !== void 0 || selectedStored.api !== void 0);\n\tif (!entryProtocol && request.provider !== void 0) {"
|
|
825
1952
|
},
|
|
826
1953
|
{
|
|
827
1954
|
"before": "\tif (request.baseURL === void 0 || request.baseURL.length === 0) throw new LlmError(`pi-ai ships no catalog for provider \"${request.provider ?? \"\"}\", so its models can only come from its endpoint; set a baseURL, or enter this provider's models by hand`, \"DISCOVERY_FAILED\");\n\tconst api = request.api ?? \"openai-completions\";\n\tif (!LISTABLE_PROTOCOLS.has(api)) throw new LlmError(`pi-ai protocol \"${api}\" has no model listing this build can read; enter this provider's models by hand`, \"DISCOVERY_UNSUPPORTED\");\n\tconst url = listingUrl(request.baseURL, api);",
|
|
@@ -834,13 +1961,29 @@
|
|
|
834
1961
|
{
|
|
835
1962
|
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
836
1963
|
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
1964
|
+
},
|
|
1965
|
+
{
|
|
1966
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
1967
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
1968
|
+
},
|
|
1969
|
+
{
|
|
1970
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
1971
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
1972
|
+
},
|
|
1973
|
+
{
|
|
1974
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
1975
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
1979
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
837
1980
|
}
|
|
838
1981
|
]
|
|
839
1982
|
},
|
|
840
1983
|
{
|
|
841
1984
|
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
842
1985
|
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
843
|
-
"resultSha256": "
|
|
1986
|
+
"resultSha256": "97baad73bbaa8e71c7a4b0a76cab946d0aee8d94a81bafc1cdbfa9100feec3b6",
|
|
844
1987
|
"replacements": [
|
|
845
1988
|
{
|
|
846
1989
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -920,11 +2063,11 @@
|
|
|
920
2063
|
},
|
|
921
2064
|
{
|
|
922
2065
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
923
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
2066
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
924
2067
|
},
|
|
925
2068
|
{
|
|
926
2069
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
927
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
2070
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
928
2071
|
},
|
|
929
2072
|
{
|
|
930
2073
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -945,13 +2088,29 @@
|
|
|
945
2088
|
{
|
|
946
2089
|
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
947
2090
|
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
2091
|
+
},
|
|
2092
|
+
{
|
|
2093
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
2094
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
2095
|
+
},
|
|
2096
|
+
{
|
|
2097
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
2098
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
2099
|
+
},
|
|
2100
|
+
{
|
|
2101
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
2102
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
2103
|
+
},
|
|
2104
|
+
{
|
|
2105
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
2106
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
948
2107
|
}
|
|
949
2108
|
]
|
|
950
2109
|
},
|
|
951
2110
|
{
|
|
952
2111
|
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation with per-entry API overrides",
|
|
953
2112
|
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
954
|
-
"resultSha256": "
|
|
2113
|
+
"resultSha256": "70dd30c46c5504422738a158acf789bb6fe43cff9b134d5ddf5e5c3da3236340",
|
|
955
2114
|
"replacements": [
|
|
956
2115
|
{
|
|
957
2116
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -1031,11 +2190,11 @@
|
|
|
1031
2190
|
},
|
|
1032
2191
|
{
|
|
1033
2192
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1034
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
2193
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1035
2194
|
},
|
|
1036
2195
|
{
|
|
1037
2196
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1038
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
2197
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1039
2198
|
},
|
|
1040
2199
|
{
|
|
1041
2200
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -1056,13 +2215,29 @@
|
|
|
1056
2215
|
{
|
|
1057
2216
|
"before": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\theaders: profile.headers,",
|
|
1058
2217
|
"after": "\t\tif (profile === void 0) throw new LlmError(`provider \"${provider}\" has no key entry \"${keyEntryId}\"`, \"INVALID_CONFIG\");\n\t\treturn {\n\t\t\tapi: profile.api,\n\t\t\tbaseURL: profile.baseURL ?? catalogProvider(provider)?.baseUrl,\n\t\t\theaders: profile.headers,"
|
|
2218
|
+
},
|
|
2219
|
+
{
|
|
2220
|
+
"before": "function listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");",
|
|
2221
|
+
"after": "/** Strip trailing slashes and one trailing `/v1`, the segment the Anthropic SDK adds itself. */\nfunction anthropicRoot(baseURL) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");\n\treturn base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\nfunction listingUrl(baseURL, api) {\n\tconst base = baseURL.replace(/\\/+$/, \"\");"
|
|
2222
|
+
},
|
|
2223
|
+
{
|
|
2224
|
+
"before": "\t\tconst baseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (baseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);",
|
|
2225
|
+
"after": "\t\tconst configuredBaseUrl = request.baseURL ?? base?.baseUrl ?? providerBaseUrl;\n\t\tif (configuredBaseUrl === void 0) invalid(provider, `model \"${entry.id}\" needs a baseURL; the installed catalog does not describe this route`);\n\t\tconst baseUrl = api === \"anthropic-messages\" ? anthropicRoot(configuredBaseUrl) : configuredBaseUrl;"
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
2229
|
+
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
2233
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
1059
2234
|
}
|
|
1060
2235
|
]
|
|
1061
2236
|
},
|
|
1062
2237
|
{
|
|
1063
|
-
"source": "0.2.2-rc.1 with
|
|
1064
|
-
"baseSha256": "
|
|
1065
|
-
"resultSha256": "
|
|
2238
|
+
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
2239
|
+
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
2240
|
+
"resultSha256": "77bbab114ae7ea64df49e7b1f610fae893990c8e477ed7408f1b6cfc3cef717a",
|
|
1066
2241
|
"replacements": [
|
|
1067
2242
|
{
|
|
1068
2243
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -1142,11 +2317,11 @@
|
|
|
1142
2317
|
},
|
|
1143
2318
|
{
|
|
1144
2319
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1145
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
2320
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1146
2321
|
},
|
|
1147
2322
|
{
|
|
1148
2323
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1149
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
2324
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1150
2325
|
},
|
|
1151
2326
|
{
|
|
1152
2327
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -1179,13 +2354,37 @@
|
|
|
1179
2354
|
{
|
|
1180
2355
|
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
1181
2356
|
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
2357
|
+
},
|
|
2358
|
+
{
|
|
2359
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
2360
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
"before": "const modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields\n});",
|
|
2364
|
+
"after": "function applyModelCapabilities(model, defaults) {\n const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);\n const overrides = model.capabilityOverrides;\n if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {\n throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');\n }\n if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {\n throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');\n }\n const { capabilityOverrides: ignored, ...effective } = model;\n if (defaults === undefined) return effective;\n const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];\n const vision = choice('vision');\n if (vision !== undefined) {\n const input = Array.isArray(model.input) ? [...model.input] : ['text'];\n effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];\n if (!effective.input.includes('text')) effective.input.unshift('text');\n if (vision && !effective.input.includes('image')) effective.input.push('image');\n }\n const reasoning = choice('reasoning');\n if (reasoning === false) effective.reasoningEfforts = false;\n if (reasoning === true) {\n const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n const efforts = model.reasoningEfforts;\n const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');\n effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };\n }\n return effective;\n}\nconst capabilityDefaultsProfile = z.union([z.object({ vision: z.boolean().required(), reasoning: z.boolean().required() })]);\nconst capabilityOverridesProfile = z.union([z.object({ vision: z.union([z.boolean(), z.const(\"original\")]), reasoning: z.union([z.boolean(), z.const(\"original\")]) })]);\nconst modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields,\n\tcapabilityOverrides: capabilityOverridesProfile\n});"
|
|
2365
|
+
},
|
|
2366
|
+
{
|
|
2367
|
+
"before": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()",
|
|
2368
|
+
"after": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tcapabilityDefaults: capabilityDefaultsProfile,\n\tmodels: z.array(modelProfile).required()"
|
|
2369
|
+
},
|
|
2370
|
+
{
|
|
2371
|
+
"before": "\t\tassertWireModels(source.models, \"models\");",
|
|
2372
|
+
"after": "\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const model of source.models ?? []) applyModelCapabilities(model);"
|
|
2373
|
+
},
|
|
2374
|
+
{
|
|
2375
|
+
"before": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);",
|
|
2376
|
+
"after": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t\tapplyModelCapabilities({}, entry.capabilityDefaults);\n\t\t\tfor (const model of entry.models) applyModelCapabilities(model, entry.capabilityDefaults);"
|
|
2377
|
+
},
|
|
2378
|
+
{
|
|
2379
|
+
"before": "\t\t\t\tmodels: entry.models,",
|
|
2380
|
+
"after": "\t\t\t\tmodels: entry.models.map(model => applyModelCapabilities(model, entry.capabilityDefaults)),"
|
|
1182
2381
|
}
|
|
1183
2382
|
]
|
|
1184
2383
|
},
|
|
1185
2384
|
{
|
|
1186
|
-
"source": "
|
|
1187
|
-
"baseSha256": "
|
|
1188
|
-
"resultSha256": "
|
|
2385
|
+
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation with per-entry API overrides",
|
|
2386
|
+
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
2387
|
+
"resultSha256": "a611e953f9ffa8468dec99faded5f5fb5412f6409681ee0da424558d21d73577",
|
|
1189
2388
|
"replacements": [
|
|
1190
2389
|
{
|
|
1191
2390
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -1265,11 +2464,11 @@
|
|
|
1265
2464
|
},
|
|
1266
2465
|
{
|
|
1267
2466
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1268
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
2467
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1269
2468
|
},
|
|
1270
2469
|
{
|
|
1271
2470
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1272
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
2471
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1273
2472
|
},
|
|
1274
2473
|
{
|
|
1275
2474
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -1302,13 +2501,37 @@
|
|
|
1302
2501
|
{
|
|
1303
2502
|
"before": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${base.endsWith(\"/v1\") ? base.slice(0, -3) : base}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;",
|
|
1304
2503
|
"after": "\tif (api !== \"anthropic-messages\") return `${base}/models`;\n\treturn `${anthropicRoot(base)}/v1/models?limit=${String(ANTHROPIC_MODEL_LIMIT)}`;"
|
|
2504
|
+
},
|
|
2505
|
+
{
|
|
2506
|
+
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
2507
|
+
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
2508
|
+
},
|
|
2509
|
+
{
|
|
2510
|
+
"before": "const modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields\n});",
|
|
2511
|
+
"after": "function applyModelCapabilities(model, defaults) {\n const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);\n const overrides = model.capabilityOverrides;\n if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {\n throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');\n }\n if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {\n throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');\n }\n const { capabilityOverrides: ignored, ...effective } = model;\n if (defaults === undefined) return effective;\n const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];\n const vision = choice('vision');\n if (vision !== undefined) {\n const input = Array.isArray(model.input) ? [...model.input] : ['text'];\n effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];\n if (!effective.input.includes('text')) effective.input.unshift('text');\n if (vision && !effective.input.includes('image')) effective.input.push('image');\n }\n const reasoning = choice('reasoning');\n if (reasoning === false) effective.reasoningEfforts = false;\n if (reasoning === true) {\n const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n const efforts = model.reasoningEfforts;\n const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');\n effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };\n }\n return effective;\n}\nconst capabilityDefaultsProfile = z.union([z.object({ vision: z.boolean().required(), reasoning: z.boolean().required() })]);\nconst capabilityOverridesProfile = z.union([z.object({ vision: z.union([z.boolean(), z.const(\"original\")]), reasoning: z.union([z.boolean(), z.const(\"original\")]) })]);\nconst modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields,\n\tcapabilityOverrides: capabilityOverridesProfile\n});"
|
|
2512
|
+
},
|
|
2513
|
+
{
|
|
2514
|
+
"before": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()",
|
|
2515
|
+
"after": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tcapabilityDefaults: capabilityDefaultsProfile,\n\tmodels: z.array(modelProfile).required()"
|
|
2516
|
+
},
|
|
2517
|
+
{
|
|
2518
|
+
"before": "\t\tassertWireModels(source.models, \"models\");",
|
|
2519
|
+
"after": "\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const model of source.models ?? []) applyModelCapabilities(model);"
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
"before": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);",
|
|
2523
|
+
"after": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t\tapplyModelCapabilities({}, entry.capabilityDefaults);\n\t\t\tfor (const model of entry.models) applyModelCapabilities(model, entry.capabilityDefaults);"
|
|
2524
|
+
},
|
|
2525
|
+
{
|
|
2526
|
+
"before": "\t\t\t\tmodels: entry.models,",
|
|
2527
|
+
"after": "\t\t\t\tmodels: entry.models.map(model => applyModelCapabilities(model, entry.capabilityDefaults)),"
|
|
1305
2528
|
}
|
|
1306
2529
|
]
|
|
1307
2530
|
},
|
|
1308
2531
|
{
|
|
1309
|
-
"source": "0.2.2-rc.1 with
|
|
1310
|
-
"baseSha256": "
|
|
1311
|
-
"resultSha256": "
|
|
2532
|
+
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
2533
|
+
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
2534
|
+
"resultSha256": "b7a189885f4d805096ae4fc250c1978002232374e20af2a8fa5778094273c523",
|
|
1312
2535
|
"replacements": [
|
|
1313
2536
|
{
|
|
1314
2537
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -1388,11 +2611,11 @@
|
|
|
1388
2611
|
},
|
|
1389
2612
|
{
|
|
1390
2613
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1391
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
2614
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1392
2615
|
},
|
|
1393
2616
|
{
|
|
1394
2617
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1395
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
2618
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1396
2619
|
},
|
|
1397
2620
|
{
|
|
1398
2621
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -1429,13 +2652,33 @@
|
|
|
1429
2652
|
{
|
|
1430
2653
|
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
1431
2654
|
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
2655
|
+
},
|
|
2656
|
+
{
|
|
2657
|
+
"before": "const modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields\n});",
|
|
2658
|
+
"after": "function applyModelCapabilities(model, defaults) {\n const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);\n const overrides = model.capabilityOverrides;\n if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {\n throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');\n }\n if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {\n throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');\n }\n const { capabilityOverrides: ignored, ...effective } = model;\n if (defaults === undefined) return effective;\n const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];\n const vision = choice('vision');\n if (vision !== undefined) {\n const input = Array.isArray(model.input) ? [...model.input] : ['text'];\n effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];\n if (effective.input.length === 0) effective.input.push('text');\n if (vision && !effective.input.includes('image')) effective.input.push('image');\n }\n const reasoning = choice('reasoning');\n if (reasoning === false) effective.reasoningEfforts = false;\n if (reasoning === true) {\n const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n const efforts = model.reasoningEfforts;\n const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');\n effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };\n }\n return effective;\n}\nconst capabilityDefaultsProfile = z.union([z.object({ vision: z.boolean().required(), reasoning: z.boolean().required() })]);\nconst capabilityOverridesProfile = z.union([z.object({ vision: z.union([z.boolean(), z.const(\"original\")]), reasoning: z.union([z.boolean(), z.const(\"original\")]) })]);\nconst modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields,\n\tcapabilityOverrides: capabilityOverridesProfile\n});"
|
|
2659
|
+
},
|
|
2660
|
+
{
|
|
2661
|
+
"before": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()",
|
|
2662
|
+
"after": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tcapabilityDefaults: capabilityDefaultsProfile,\n\tmodels: z.array(modelProfile).required()"
|
|
2663
|
+
},
|
|
2664
|
+
{
|
|
2665
|
+
"before": "\t\tassertWireModels(source.models, \"models\");",
|
|
2666
|
+
"after": "\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const model of source.models ?? []) applyModelCapabilities(model);"
|
|
2667
|
+
},
|
|
2668
|
+
{
|
|
2669
|
+
"before": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);",
|
|
2670
|
+
"after": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t\tapplyModelCapabilities({}, entry.capabilityDefaults);\n\t\t\tfor (const model of entry.models) applyModelCapabilities(model, entry.capabilityDefaults);"
|
|
2671
|
+
},
|
|
2672
|
+
{
|
|
2673
|
+
"before": "\t\t\t\tmodels: entry.models,",
|
|
2674
|
+
"after": "\t\t\t\tmodels: entry.models.map(model => applyModelCapabilities(model, entry.capabilityDefaults)),"
|
|
1432
2675
|
}
|
|
1433
2676
|
]
|
|
1434
2677
|
},
|
|
1435
2678
|
{
|
|
1436
|
-
"source": "
|
|
1437
|
-
"baseSha256": "
|
|
1438
|
-
"resultSha256": "
|
|
2679
|
+
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation with per-entry API overrides",
|
|
2680
|
+
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
2681
|
+
"resultSha256": "580fa3abca9a8f9c3f42897aa041bd77eae9a5b692e7a94fd53bfa80cec13a4d",
|
|
1439
2682
|
"replacements": [
|
|
1440
2683
|
{
|
|
1441
2684
|
"before": "\t...modelFields\n});\n/** A {@link modelProfile} whose id lives in the `modelOverrides` dict key. */\nconst modelOverride = z.object(modelFields);\nconst profile = z.object({\n\tapiKeyEnv: z.string().role(\"credential-ref\"),\n\tdisplayName: z.string(),\n\tapi: z.union(supportedProtocols()),\n\tbaseURL: z.string(),\n",
|
|
@@ -1515,11 +2758,11 @@
|
|
|
1515
2758
|
},
|
|
1516
2759
|
{
|
|
1517
2760
|
"before": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tmodels: z.array(modelProfile).required()\n});",
|
|
1518
|
-
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
2761
|
+
"after": "\tapiKeyEnv: z.string().role(\"credential-ref\").required(),\n\tapi: z.union(supportedProtocols()),\n\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()\n});"
|
|
1519
2762
|
},
|
|
1520
2763
|
{
|
|
1521
2764
|
"before": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (!Array.isArray(entry.models))",
|
|
1522
|
-
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
2765
|
+
"after": "\t\t\tcredentialRef(entry.apiKeyEnv);\n\t\t\tif (entry.api !== void 0 && !supportedProtocols().includes(entry.api)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" api must be a supported protocol`);\n\t\t\tif (entry.sortOrder !== void 0 && (!Number.isSafeInteger(entry.sortOrder) || entry.sortOrder < 0)) throw new Error(`llm-pi-ai: provider \"${provider}\" key entry \"${id}\" sortOrder must be a nonnegative safe integer`);\n\t\t\tif (!Array.isArray(entry.models))"
|
|
1523
2766
|
},
|
|
1524
2767
|
{
|
|
1525
2768
|
"before": "\t\t\tconst child = resolveProfiles({ [provider]: {\n\t\t\t\t...source,\n\t\t\t\tmultiKey: false,",
|
|
@@ -1556,19 +2799,39 @@
|
|
|
1556
2799
|
{
|
|
1557
2800
|
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
1558
2801
|
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
2802
|
+
},
|
|
2803
|
+
{
|
|
2804
|
+
"before": "const modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields\n});",
|
|
2805
|
+
"after": "function applyModelCapabilities(model, defaults) {\n const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);\n const overrides = model.capabilityOverrides;\n if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {\n throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');\n }\n if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {\n throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');\n }\n const { capabilityOverrides: ignored, ...effective } = model;\n if (defaults === undefined) return effective;\n const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];\n const vision = choice('vision');\n if (vision !== undefined) {\n const input = Array.isArray(model.input) ? [...model.input] : ['text'];\n effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];\n if (effective.input.length === 0) effective.input.push('text');\n if (vision && !effective.input.includes('image')) effective.input.push('image');\n }\n const reasoning = choice('reasoning');\n if (reasoning === false) effective.reasoningEfforts = false;\n if (reasoning === true) {\n const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n const efforts = model.reasoningEfforts;\n const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');\n effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };\n }\n return effective;\n}\nconst capabilityDefaultsProfile = z.union([z.object({ vision: z.boolean().required(), reasoning: z.boolean().required() })]);\nconst capabilityOverridesProfile = z.union([z.object({ vision: z.union([z.boolean(), z.const(\"original\")]), reasoning: z.union([z.boolean(), z.const(\"original\")]) })]);\nconst modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields,\n\tcapabilityOverrides: capabilityOverridesProfile\n});"
|
|
2806
|
+
},
|
|
2807
|
+
{
|
|
2808
|
+
"before": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()",
|
|
2809
|
+
"after": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tcapabilityDefaults: capabilityDefaultsProfile,\n\tmodels: z.array(modelProfile).required()"
|
|
2810
|
+
},
|
|
2811
|
+
{
|
|
2812
|
+
"before": "\t\tassertWireModels(source.models, \"models\");",
|
|
2813
|
+
"after": "\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const model of source.models ?? []) applyModelCapabilities(model);"
|
|
2814
|
+
},
|
|
2815
|
+
{
|
|
2816
|
+
"before": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);",
|
|
2817
|
+
"after": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t\tapplyModelCapabilities({}, entry.capabilityDefaults);\n\t\t\tfor (const model of entry.models) applyModelCapabilities(model, entry.capabilityDefaults);"
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
"before": "\t\t\t\tmodels: entry.models,",
|
|
2821
|
+
"after": "\t\t\t\tmodels: entry.models.map(model => applyModelCapabilities(model, entry.capabilityDefaults)),"
|
|
1559
2822
|
}
|
|
1560
2823
|
]
|
|
1561
2824
|
},
|
|
1562
2825
|
{
|
|
1563
2826
|
"source": "npm official 0.2.2-rc.1 with per-entry API overrides",
|
|
1564
2827
|
"baseSha256": "1a3ae58f9b4493e15459cc5000ec80f21b5d3f4a9dea6ffb6bc28e5b8817ae8a",
|
|
1565
|
-
"resultSha256": "
|
|
2828
|
+
"resultSha256": "0206db4d8c2aed56f905edc893541efe519aade4ed65173fc1051d4c41f883fc",
|
|
1566
2829
|
"current": true
|
|
1567
2830
|
},
|
|
1568
2831
|
{
|
|
1569
2832
|
"source": "0.2.2-rc.1 with tool lookup optimization and credential cancellation with per-entry API overrides",
|
|
1570
2833
|
"baseSha256": "0a99a2fc903a4285044b09114b3bd20ee1e612f39aa381e4a71b10d48ee9e9f7",
|
|
1571
|
-
"resultSha256": "
|
|
2834
|
+
"resultSha256": "e6cb26e24b4f29dc9eefa8b10a70d3887b698d41479a60e11cfba5c0609250a4",
|
|
1572
2835
|
"current": true
|
|
1573
2836
|
}
|
|
1574
2837
|
],
|
|
@@ -1692,6 +2955,26 @@
|
|
|
1692
2955
|
{
|
|
1693
2956
|
"before": "* gateway documentation publishes both spellings of the same root. Only this\n* listing URL normalizes that segment; model requests receive the configured\n* `baseURL` unchanged.\n*/",
|
|
1694
2957
|
"after": "* gateway documentation publishes both spellings of the same root. Anthropic\n* listings and model requests both normalize it, because the Anthropic SDK\n* appends `/v1/messages` on its own while OpenAI protocols append\n* `/chat/completions`; without this, one root would work for chat and double\n* the segment for Anthropic.\n*/"
|
|
2958
|
+
},
|
|
2959
|
+
{
|
|
2960
|
+
"before": "const modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields\n});",
|
|
2961
|
+
"after": "function applyModelCapabilities(model, defaults) {\n const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);\n const overrides = model.capabilityOverrides;\n if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {\n throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');\n }\n if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {\n throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');\n }\n const { capabilityOverrides: ignored, ...effective } = model;\n if (defaults === undefined) return effective;\n const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];\n const vision = choice('vision');\n if (vision !== undefined) {\n const input = Array.isArray(model.input) ? [...model.input] : ['text'];\n effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];\n if (effective.input.length === 0) effective.input.push('text');\n if (vision && !effective.input.includes('image')) effective.input.push('image');\n }\n const reasoning = choice('reasoning');\n if (reasoning === false) effective.reasoningEfforts = false;\n if (reasoning === true) {\n const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];\n const efforts = model.reasoningEfforts;\n const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');\n effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };\n }\n return effective;\n}\nfunction validateCapabilityBatch(batch) {\n if (batch !== undefined && (batch === null || typeof batch !== 'object' || Array.isArray(batch) || Object.keys(batch).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(batch).some(value => typeof value !== 'boolean'))) {\n throw new TypeError('capabilityBatch must contain only boolean vision and reasoning');\n }\n return batch;\n}\nconst capabilityBatchProfile = z.union([z.object({ vision: z.boolean(), reasoning: z.boolean() })]);\nconst capabilityDefaultsProfile = z.union([z.object({ vision: z.boolean().required(), reasoning: z.boolean().required() })]);\nconst capabilityOverridesProfile = z.union([z.object({ vision: z.union([z.boolean(), z.const(\"original\")]), reasoning: z.union([z.boolean(), z.const(\"original\")]) })]);\nconst modelProfile = z.object({\n\tid: z.string().required(),\n\t...modelFields,\n\tcapabilityOverrides: capabilityOverridesProfile\n});"
|
|
2962
|
+
},
|
|
2963
|
+
{
|
|
2964
|
+
"before": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tmodels: z.array(modelProfile).required()",
|
|
2965
|
+
"after": "\tsortOrder: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER),\n\tcapabilityDefaults: capabilityDefaultsProfile,\n\tcapabilityBatch: capabilityBatchProfile,\n\tmodels: z.array(modelProfile).required()"
|
|
2966
|
+
},
|
|
2967
|
+
{
|
|
2968
|
+
"before": "\t\tassertWireModels(source.models, \"models\");",
|
|
2969
|
+
"after": "\t\tassertWireModels(source.models, \"models\");\n\t\tfor (const model of source.models ?? []) applyModelCapabilities(model);"
|
|
2970
|
+
},
|
|
2971
|
+
{
|
|
2972
|
+
"before": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);",
|
|
2973
|
+
"after": "\t\t\tassertWireModels(entry.models, `key entry \"${id}\" models`);\n\t\t\tapplyModelCapabilities({}, entry.capabilityDefaults);\n\t\t\tvalidateCapabilityBatch(entry.capabilityBatch);\n\t\t\tfor (const model of entry.models) applyModelCapabilities(model, entry.capabilityDefaults);"
|
|
2974
|
+
},
|
|
2975
|
+
{
|
|
2976
|
+
"before": "\t\t\t\tmodels: entry.models,",
|
|
2977
|
+
"after": "\t\t\t\tmodels: entry.models.map(model => applyModelCapabilities(model, entry.capabilityDefaults)),"
|
|
1695
2978
|
}
|
|
1696
2979
|
],
|
|
1697
2980
|
"current": true,
|