pi2dsh 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/README.zh.md +4 -2
- package/dist/analyzer-C86ba-27.mjs +626 -0
- package/dist/analyzer-C86ba-27.mjs.map +1 -0
- package/dist/cli.mjs +3 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/compat/pi-ai.d.mts +1 -11
- package/dist/compat/pi-ai.d.mts.map +1 -1
- package/dist/compat/pi-ai.mjs +2 -2
- package/dist/credentials-oauth.mjs +1 -1
- package/dist/generator-C0jNM-yu.mjs +298 -0
- package/dist/generator-C0jNM-yu.mjs.map +1 -0
- package/dist/host.d.mts +2 -0
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +2 -1
- package/dist/host.mjs.map +1 -1
- package/dist/index.d.mts +29 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +27 -4
- package/dist/index.mjs.map +1 -1
- package/dist/mcp-config-Dlou-ZA-.mjs +707 -0
- package/dist/mcp-config-Dlou-ZA-.mjs.map +1 -0
- package/dist/{pi-ai-B4hucafU.mjs → pi-ai-BA3h0Dwv.mjs} +2 -84
- package/dist/pi-ai-BA3h0Dwv.mjs.map +1 -0
- package/dist/{runtime-k3IlCgjQ.mjs → runtime-DMEJknhK.mjs} +115 -960
- package/dist/runtime-DMEJknhK.mjs.map +1 -0
- package/dist/runtime.d.mts +19 -6
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +2 -2
- package/package.json +6 -3
- package/dist/all-BChhwMVE.mjs +0 -28512
- package/dist/all-BChhwMVE.mjs.map +0 -1
- package/dist/anthropic-messages-Dlw4T0S4.mjs +0 -6438
- package/dist/anthropic-messages-Dlw4T0S4.mjs.map +0 -1
- package/dist/azure-openai-responses-vGUNukZ7.mjs +0 -209
- package/dist/azure-openai-responses-vGUNukZ7.mjs.map +0 -1
- package/dist/deferred-tools-BD4g5QUx.mjs +0 -32
- package/dist/deferred-tools-BD4g5QUx.mjs.map +0 -1
- package/dist/diagnostics-CJye1UFh.mjs +0 -99
- package/dist/diagnostics-CJye1UFh.mjs.map +0 -1
- package/dist/dist-DY2GeEQ6.mjs +0 -1089
- package/dist/dist-DY2GeEQ6.mjs.map +0 -1
- package/dist/dist-DYfTHu0C.mjs +0 -1246
- package/dist/dist-DYfTHu0C.mjs.map +0 -1
- package/dist/error-body-DKA3lknR.mjs +0 -104
- package/dist/error-body-DKA3lknR.mjs.map +0 -1
- package/dist/github-copilot-headers-CZo2QHrt.mjs +0 -25
- package/dist/github-copilot-headers-CZo2QHrt.mjs.map +0 -1
- package/dist/google-generative-ai-C30gRjRi.mjs +0 -358
- package/dist/google-generative-ai-C30gRjRi.mjs.map +0 -1
- package/dist/google-shared-W8g3Esep.mjs +0 -29042
- package/dist/google-shared-W8g3Esep.mjs.map +0 -1
- package/dist/google-vertex--O_gyVYA.mjs +0 -402
- package/dist/google-vertex--O_gyVYA.mjs.map +0 -1
- package/dist/hash-Dbggamac.mjs +0 -19
- package/dist/hash-Dbggamac.mjs.map +0 -1
- package/dist/headers-CPzZhJQn.mjs +0 -17
- package/dist/headers-CPzZhJQn.mjs.map +0 -1
- package/dist/images-models-DJtHo9Rp.mjs +0 -1068
- package/dist/images-models-DJtHo9Rp.mjs.map +0 -1
- package/dist/json-parse-C-vG4_qc.mjs +0 -384
- package/dist/json-parse-C-vG4_qc.mjs.map +0 -1
- package/dist/mcp-config-Cful6uUS.mjs +0 -1618
- package/dist/mcp-config-Cful6uUS.mjs.map +0 -1
- package/dist/mistral-conversations-LSDG9sjq.mjs +0 -42007
- package/dist/mistral-conversations-LSDG9sjq.mjs.map +0 -1
- package/dist/multipart-parser-CoM3rwav.mjs +0 -301
- package/dist/multipart-parser-CoM3rwav.mjs.map +0 -1
- package/dist/openai-codex-responses-JxII_OUX.mjs +0 -1169
- package/dist/openai-codex-responses-JxII_OUX.mjs.map +0 -1
- package/dist/openai-completions-Dp-VwLzJ.mjs +0 -947
- package/dist/openai-completions-Dp-VwLzJ.mjs.map +0 -1
- package/dist/openai-completions-lite-BrM7gJzs.mjs +0 -316
- package/dist/openai-completions-lite-BrM7gJzs.mjs.map +0 -1
- package/dist/openai-prompt-cache-BDWtZRKh.mjs +0 -11
- package/dist/openai-prompt-cache-BDWtZRKh.mjs.map +0 -1
- package/dist/openai-responses-CWmNks8K.mjs +0 -247
- package/dist/openai-responses-CWmNks8K.mjs.map +0 -1
- package/dist/openai-responses-shared-CYgIDTf3.mjs +0 -572
- package/dist/openai-responses-shared-CYgIDTf3.mjs.map +0 -1
- package/dist/openrouter-images-DfAULV7l.mjs +0 -127
- package/dist/openrouter-images-DfAULV7l.mjs.map +0 -1
- package/dist/otel-Boafyju5.mjs +0 -24935
- package/dist/otel-Boafyju5.mjs.map +0 -1
- package/dist/pi-ai-B4hucafU.mjs.map +0 -1
- package/dist/pi-messages-HzGtvc5_.mjs +0 -306
- package/dist/pi-messages-HzGtvc5_.mjs.map +0 -1
- package/dist/provider-env-C9gOBFJi.mjs +0 -40
- package/dist/provider-env-C9gOBFJi.mjs.map +0 -1
- package/dist/provider-retry-Cw6YJ4cr.mjs +0 -80
- package/dist/provider-retry-Cw6YJ4cr.mjs.map +0 -1
- package/dist/runtime-k3IlCgjQ.mjs.map +0 -1
- package/dist/sanitize-unicode-wLA11KvZ.mjs +0 -29
- package/dist/sanitize-unicode-wLA11KvZ.mjs.map +0 -1
- package/dist/src-D0GRWCE8.mjs +0 -5031
- package/dist/src-D0GRWCE8.mjs.map +0 -1
- package/dist/transform-messages-DGq9mvhA.mjs +0 -379
- package/dist/transform-messages-DGq9mvhA.mjs.map +0 -1
- package/dist/uuid-DtFT7tOE.mjs +0 -65
- package/dist/uuid-DtFT7tOE.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { P as AssistantMessageEventStream, i as builtinProviders, r as __setPiAiLlmBridge } from "./pi-ai-BA3h0Dwv.mjs";
|
|
3
|
+
import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
|
|
4
4
|
import { E as __setSubagentSessionFactory, S as Theme, f as ExtensionRunner } from "./pi-coding-agent-CznNsOnV.mjs";
|
|
5
5
|
import { a as resolvePiProviderAuth, n as loginPiProvider, r as providerSupportsOAuth, t as FileCredentialStore } from "./oauth-bridge-CfyImWx4.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
@@ -15,9 +15,6 @@ import { createJiti } from "jiti";
|
|
|
15
15
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
16
16
|
import { renderPrompt } from "@deepseek-ai/dsh-system-prompt";
|
|
17
17
|
import { randomUUID } from "node:crypto";
|
|
18
|
-
import { execSync, spawnSync } from "child_process";
|
|
19
|
-
import { Type } from "typebox";
|
|
20
|
-
import { Compile } from "typebox/compile";
|
|
21
18
|
//#region src/session-bridge.ts
|
|
22
19
|
function sidecarDir() {
|
|
23
20
|
return join(getAgentDir(), "session-entries");
|
|
@@ -1114,834 +1111,7 @@ async function* piEventsToDshChunks(events) {
|
|
|
1114
1111
|
};
|
|
1115
1112
|
}
|
|
1116
1113
|
//#endregion
|
|
1117
|
-
//#region src/compat/vendor/pi-json.ts
|
|
1118
|
-
/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */
|
|
1119
|
-
function stripJsonComments(input) {
|
|
1120
|
-
return input.replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, (m) => m[0] === "\"" ? m : "").replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (m, tail) => tail ?? (m[0] === "\"" ? m : ""));
|
|
1121
|
-
}
|
|
1122
|
-
//#endregion
|
|
1123
|
-
//#region src/compat/vendor/pi-model-config.ts
|
|
1124
|
-
/** Immutable, credential-blind models.json snapshot. */
|
|
1125
|
-
const PercentileCutoffsSchema = Type.Object({
|
|
1126
|
-
p50: Type.Optional(Type.Number()),
|
|
1127
|
-
p75: Type.Optional(Type.Number()),
|
|
1128
|
-
p90: Type.Optional(Type.Number()),
|
|
1129
|
-
p99: Type.Optional(Type.Number())
|
|
1130
|
-
});
|
|
1131
|
-
const OpenRouterRoutingSchema = Type.Object({
|
|
1132
|
-
allow_fallbacks: Type.Optional(Type.Boolean()),
|
|
1133
|
-
require_parameters: Type.Optional(Type.Boolean()),
|
|
1134
|
-
data_collection: Type.Optional(Type.Union([Type.Literal("deny"), Type.Literal("allow")])),
|
|
1135
|
-
zdr: Type.Optional(Type.Boolean()),
|
|
1136
|
-
enforce_distillable_text: Type.Optional(Type.Boolean()),
|
|
1137
|
-
order: Type.Optional(Type.Array(Type.String())),
|
|
1138
|
-
only: Type.Optional(Type.Array(Type.String())),
|
|
1139
|
-
ignore: Type.Optional(Type.Array(Type.String())),
|
|
1140
|
-
quantizations: Type.Optional(Type.Array(Type.String())),
|
|
1141
|
-
sort: Type.Optional(Type.Union([Type.String(), Type.Object({
|
|
1142
|
-
by: Type.Optional(Type.String()),
|
|
1143
|
-
partition: Type.Optional(Type.Union([Type.String(), Type.Null()]))
|
|
1144
|
-
})])),
|
|
1145
|
-
max_price: Type.Optional(Type.Object({
|
|
1146
|
-
prompt: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
|
1147
|
-
completion: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
|
1148
|
-
image: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
|
1149
|
-
audio: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
|
1150
|
-
request: Type.Optional(Type.Union([Type.Number(), Type.String()]))
|
|
1151
|
-
})),
|
|
1152
|
-
preferred_min_throughput: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema])),
|
|
1153
|
-
preferred_max_latency: Type.Optional(Type.Union([Type.Number(), PercentileCutoffsSchema]))
|
|
1154
|
-
});
|
|
1155
|
-
const VercelGatewayRoutingSchema = Type.Object({
|
|
1156
|
-
only: Type.Optional(Type.Array(Type.String())),
|
|
1157
|
-
order: Type.Optional(Type.Array(Type.String()))
|
|
1158
|
-
});
|
|
1159
|
-
const ThinkingLevelMapValueSchema = Type.Union([Type.String(), Type.Null()]);
|
|
1160
|
-
const ThinkingLevelMapSchema = Type.Object({
|
|
1161
|
-
off: Type.Optional(ThinkingLevelMapValueSchema),
|
|
1162
|
-
minimal: Type.Optional(ThinkingLevelMapValueSchema),
|
|
1163
|
-
low: Type.Optional(ThinkingLevelMapValueSchema),
|
|
1164
|
-
medium: Type.Optional(ThinkingLevelMapValueSchema),
|
|
1165
|
-
high: Type.Optional(ThinkingLevelMapValueSchema),
|
|
1166
|
-
xhigh: Type.Optional(ThinkingLevelMapValueSchema),
|
|
1167
|
-
max: Type.Optional(ThinkingLevelMapValueSchema)
|
|
1168
|
-
});
|
|
1169
|
-
const ChatTemplateKwargScalarSchema = Type.Union([
|
|
1170
|
-
Type.String(),
|
|
1171
|
-
Type.Number(),
|
|
1172
|
-
Type.Boolean(),
|
|
1173
|
-
Type.Null()
|
|
1174
|
-
]);
|
|
1175
|
-
const ChatTemplateKwargVariableSchema = Type.Object({
|
|
1176
|
-
$var: Type.Union([Type.Literal("thinking.enabled"), Type.Literal("thinking.effort")]),
|
|
1177
|
-
omitWhenOff: Type.Optional(Type.Boolean())
|
|
1178
|
-
});
|
|
1179
|
-
const ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);
|
|
1180
|
-
const OpenAICompletionsCompatSchema = Type.Object({
|
|
1181
|
-
supportsStore: Type.Optional(Type.Boolean()),
|
|
1182
|
-
supportsDeveloperRole: Type.Optional(Type.Boolean()),
|
|
1183
|
-
supportsReasoningEffort: Type.Optional(Type.Boolean()),
|
|
1184
|
-
supportsUsageInStreaming: Type.Optional(Type.Boolean()),
|
|
1185
|
-
maxTokensField: Type.Optional(Type.Union([Type.Literal("max_completion_tokens"), Type.Literal("max_tokens")])),
|
|
1186
|
-
requiresToolResultName: Type.Optional(Type.Boolean()),
|
|
1187
|
-
requiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),
|
|
1188
|
-
requiresThinkingAsText: Type.Optional(Type.Boolean()),
|
|
1189
|
-
requiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),
|
|
1190
|
-
thinkingFormat: Type.Optional(Type.Union([
|
|
1191
|
-
Type.Literal("openai"),
|
|
1192
|
-
Type.Literal("openrouter"),
|
|
1193
|
-
Type.Literal("together"),
|
|
1194
|
-
Type.Literal("baseten"),
|
|
1195
|
-
Type.Literal("deepseek"),
|
|
1196
|
-
Type.Literal("zai"),
|
|
1197
|
-
Type.Literal("qwen"),
|
|
1198
|
-
Type.Literal("chat-template"),
|
|
1199
|
-
Type.Literal("qwen-chat-template"),
|
|
1200
|
-
Type.Literal("string-thinking"),
|
|
1201
|
-
Type.Literal("ant-ling")
|
|
1202
|
-
])),
|
|
1203
|
-
chatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),
|
|
1204
|
-
chatTemplateArgs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),
|
|
1205
|
-
cacheControlFormat: Type.Optional(Type.Literal("anthropic")),
|
|
1206
|
-
openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
|
|
1207
|
-
vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
|
|
1208
|
-
supportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),
|
|
1209
|
-
supportsStrictMode: Type.Optional(Type.Boolean()),
|
|
1210
|
-
sendSessionAffinityHeaders: Type.Optional(Type.Boolean()),
|
|
1211
|
-
deferredToolsMode: Type.Optional(Type.Literal("kimi")),
|
|
1212
|
-
sessionAffinityFormat: Type.Optional(Type.Union([
|
|
1213
|
-
Type.Literal("openai"),
|
|
1214
|
-
Type.Literal("openai-nosession"),
|
|
1215
|
-
Type.Literal("openrouter")
|
|
1216
|
-
])),
|
|
1217
|
-
supportsLongCacheRetention: Type.Optional(Type.Boolean())
|
|
1218
|
-
});
|
|
1219
|
-
const OpenAIResponsesCompatSchema = Type.Object({
|
|
1220
|
-
supportsDeveloperRole: Type.Optional(Type.Boolean()),
|
|
1221
|
-
sessionAffinityFormat: Type.Optional(Type.Union([
|
|
1222
|
-
Type.Literal("openai"),
|
|
1223
|
-
Type.Literal("openai-nosession"),
|
|
1224
|
-
Type.Literal("openrouter")
|
|
1225
|
-
])),
|
|
1226
|
-
supportsLongCacheRetention: Type.Optional(Type.Boolean()),
|
|
1227
|
-
supportsStrictMode: Type.Optional(Type.Boolean()),
|
|
1228
|
-
supportsOpenAIGrammarTools: Type.Optional(Type.Boolean()),
|
|
1229
|
-
supportsAdditionalTools: Type.Optional(Type.Boolean()),
|
|
1230
|
-
supportsToolSearch: Type.Optional(Type.Boolean())
|
|
1231
|
-
});
|
|
1232
|
-
const AnthropicMessagesCompatSchema = Type.Object({
|
|
1233
|
-
supportsEagerToolInputStreaming: Type.Optional(Type.Boolean()),
|
|
1234
|
-
supportsLongCacheRetention: Type.Optional(Type.Boolean()),
|
|
1235
|
-
sendSessionAffinityHeaders: Type.Optional(Type.Boolean()),
|
|
1236
|
-
supportsCacheControlOnTools: Type.Optional(Type.Boolean()),
|
|
1237
|
-
supportsTemperature: Type.Optional(Type.Boolean()),
|
|
1238
|
-
forceAdaptiveThinking: Type.Optional(Type.Boolean()),
|
|
1239
|
-
allowEmptySignature: Type.Optional(Type.Boolean()),
|
|
1240
|
-
supportsStrictTools: Type.Optional(Type.Boolean()),
|
|
1241
|
-
supportsToolReferences: Type.Optional(Type.Boolean())
|
|
1242
|
-
});
|
|
1243
|
-
const ProviderCompatSchema = Type.Union([
|
|
1244
|
-
OpenAICompletionsCompatSchema,
|
|
1245
|
-
OpenAIResponsesCompatSchema,
|
|
1246
|
-
AnthropicMessagesCompatSchema
|
|
1247
|
-
]);
|
|
1248
|
-
const ModelCostRatesSchema = {
|
|
1249
|
-
input: Type.Number(),
|
|
1250
|
-
output: Type.Number(),
|
|
1251
|
-
cacheRead: Type.Number(),
|
|
1252
|
-
cacheWrite: Type.Number()
|
|
1253
|
-
};
|
|
1254
|
-
const ModelCostTierSchema = Type.Object({
|
|
1255
|
-
inputTokensAbove: Type.Number(),
|
|
1256
|
-
...ModelCostRatesSchema
|
|
1257
|
-
});
|
|
1258
|
-
const ModelCostSchema = Type.Object({
|
|
1259
|
-
...ModelCostRatesSchema,
|
|
1260
|
-
tiers: Type.Optional(Type.Array(ModelCostTierSchema))
|
|
1261
|
-
});
|
|
1262
|
-
const ModelDefinitionSchema = Type.Object({
|
|
1263
|
-
id: Type.String({ minLength: 1 }),
|
|
1264
|
-
name: Type.Optional(Type.String({ minLength: 1 })),
|
|
1265
|
-
api: Type.Optional(Type.String({ minLength: 1 })),
|
|
1266
|
-
baseUrl: Type.Optional(Type.String({ minLength: 1 })),
|
|
1267
|
-
reasoning: Type.Optional(Type.Boolean()),
|
|
1268
|
-
thinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),
|
|
1269
|
-
input: Type.Optional(Type.Array(Type.Union([Type.Literal("text"), Type.Literal("image")]))),
|
|
1270
|
-
cost: Type.Optional(ModelCostSchema),
|
|
1271
|
-
contextWindow: Type.Optional(Type.Number()),
|
|
1272
|
-
maxTokens: Type.Optional(Type.Number()),
|
|
1273
|
-
samplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
1274
|
-
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
1275
|
-
compat: Type.Optional(ProviderCompatSchema)
|
|
1276
|
-
});
|
|
1277
|
-
const ModelOverrideSchema = Type.Object({
|
|
1278
|
-
name: Type.Optional(Type.String({ minLength: 1 })),
|
|
1279
|
-
reasoning: Type.Optional(Type.Boolean()),
|
|
1280
|
-
thinkingLevelMap: Type.Optional(ThinkingLevelMapSchema),
|
|
1281
|
-
input: Type.Optional(Type.Array(Type.Union([Type.Literal("text"), Type.Literal("image")]))),
|
|
1282
|
-
cost: Type.Optional(Type.Object({
|
|
1283
|
-
input: Type.Optional(Type.Number()),
|
|
1284
|
-
output: Type.Optional(Type.Number()),
|
|
1285
|
-
cacheRead: Type.Optional(Type.Number()),
|
|
1286
|
-
cacheWrite: Type.Optional(Type.Number()),
|
|
1287
|
-
tiers: Type.Optional(Type.Array(ModelCostTierSchema))
|
|
1288
|
-
})),
|
|
1289
|
-
contextWindow: Type.Optional(Type.Number()),
|
|
1290
|
-
maxTokens: Type.Optional(Type.Number()),
|
|
1291
|
-
samplingParams: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
1292
|
-
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
1293
|
-
compat: Type.Optional(ProviderCompatSchema)
|
|
1294
|
-
});
|
|
1295
|
-
const ProviderConfigSchema = Type.Object({
|
|
1296
|
-
name: Type.Optional(Type.String({ minLength: 1 })),
|
|
1297
|
-
baseUrl: Type.Optional(Type.String({ minLength: 1 })),
|
|
1298
|
-
apiKey: Type.Optional(Type.String({ minLength: 1 })),
|
|
1299
|
-
api: Type.Optional(Type.String({ minLength: 1 })),
|
|
1300
|
-
oauth: Type.Optional(Type.Literal("radius")),
|
|
1301
|
-
headers: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
1302
|
-
compat: Type.Optional(ProviderCompatSchema),
|
|
1303
|
-
authHeader: Type.Optional(Type.Boolean()),
|
|
1304
|
-
models: Type.Optional(Type.Array(ModelDefinitionSchema)),
|
|
1305
|
-
modelOverrides: Type.Optional(Type.Record(Type.String(), ModelOverrideSchema))
|
|
1306
|
-
});
|
|
1307
|
-
const ModelsConfigSchema = Type.Object({ providers: Type.Record(Type.String(), ProviderConfigSchema) });
|
|
1308
|
-
const validateModelsConfig = Compile(ModelsConfigSchema);
|
|
1309
|
-
function formatValidationPath(error) {
|
|
1310
|
-
if (error.keyword === "required") {
|
|
1311
|
-
const requiredProperty = error.params.requiredProperties?.[0];
|
|
1312
|
-
if (requiredProperty) {
|
|
1313
|
-
const basePath = error.instancePath.replace(/^\//, "").replace(/\//g, ".");
|
|
1314
|
-
return basePath ? `${basePath}.${requiredProperty}` : requiredProperty;
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
return error.instancePath.replace(/^\//, "").replace(/\//g, ".") || "root";
|
|
1318
|
-
}
|
|
1319
|
-
function deepFreeze(value) {
|
|
1320
|
-
if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value;
|
|
1321
|
-
for (const child of Object.values(value)) deepFreeze(child);
|
|
1322
|
-
return Object.freeze(value);
|
|
1323
|
-
}
|
|
1324
|
-
/** One immutable load of models.json. */
|
|
1325
|
-
var ModelConfig = class ModelConfig {
|
|
1326
|
-
providers;
|
|
1327
|
-
error;
|
|
1328
|
-
constructor(providers, error) {
|
|
1329
|
-
this.providers = providers;
|
|
1330
|
-
this.error = error;
|
|
1331
|
-
}
|
|
1332
|
-
static async load(modelsJsonPath) {
|
|
1333
|
-
if (!modelsJsonPath) return new ModelConfig(/* @__PURE__ */ new Map());
|
|
1334
|
-
const path = normalizePath(modelsJsonPath);
|
|
1335
|
-
let content;
|
|
1336
|
-
try {
|
|
1337
|
-
content = await readFile(path, "utf-8");
|
|
1338
|
-
} catch (error) {
|
|
1339
|
-
if (error.code === "ENOENT") return new ModelConfig(/* @__PURE__ */ new Map());
|
|
1340
|
-
return new ModelConfig(/* @__PURE__ */ new Map(), `Failed to load models.json: ${error instanceof Error ? error.message : error}\n\nFile: ${path}`);
|
|
1341
|
-
}
|
|
1342
|
-
let parsed;
|
|
1343
|
-
try {
|
|
1344
|
-
parsed = JSON.parse(stripJsonComments(content));
|
|
1345
|
-
} catch (error) {
|
|
1346
|
-
return new ModelConfig(/* @__PURE__ */ new Map(), `Failed to parse models.json: ${error instanceof Error ? error.message : error}\n\nFile: ${path}`);
|
|
1347
|
-
}
|
|
1348
|
-
if (!validateModelsConfig.Check(parsed)) {
|
|
1349
|
-
const errors = validateModelsConfig.Errors(parsed).map((error) => ` - ${formatValidationPath(error)}: ${error.message}`).join("\n") || "Unknown schema error";
|
|
1350
|
-
return new ModelConfig(/* @__PURE__ */ new Map(), `Invalid models.json schema:\n${errors}\n\nFile: ${path}`);
|
|
1351
|
-
}
|
|
1352
|
-
const config = parsed;
|
|
1353
|
-
const providers = /* @__PURE__ */ new Map();
|
|
1354
|
-
for (const [providerId, provider] of Object.entries(config.providers)) providers.set(providerId, deepFreeze(structuredClone(provider)));
|
|
1355
|
-
return new ModelConfig(providers);
|
|
1356
|
-
}
|
|
1357
|
-
getProvider(providerId) {
|
|
1358
|
-
return this.providers.get(providerId);
|
|
1359
|
-
}
|
|
1360
|
-
getProviderIds() {
|
|
1361
|
-
return [...this.providers.keys()];
|
|
1362
|
-
}
|
|
1363
|
-
getError() {
|
|
1364
|
-
return this.error;
|
|
1365
|
-
}
|
|
1366
|
-
};
|
|
1367
|
-
//#endregion
|
|
1368
|
-
//#region src/compat/vendor/pi-models-compose.ts
|
|
1369
|
-
function mergeCompat(base, override) {
|
|
1370
|
-
if (!override) return base;
|
|
1371
|
-
const merged = {
|
|
1372
|
-
...base,
|
|
1373
|
-
...override
|
|
1374
|
-
};
|
|
1375
|
-
const baseNested = base;
|
|
1376
|
-
const overrideNested = override;
|
|
1377
|
-
const mergedNested = merged;
|
|
1378
|
-
for (const key of [
|
|
1379
|
-
"openRouterRouting",
|
|
1380
|
-
"vercelGatewayRouting",
|
|
1381
|
-
"chatTemplateKwargs",
|
|
1382
|
-
"chatTemplateArgs"
|
|
1383
|
-
]) {
|
|
1384
|
-
const baseValue = baseNested?.[key];
|
|
1385
|
-
const overrideValue = overrideNested[key];
|
|
1386
|
-
if (typeof baseValue === "object" && baseValue !== null || typeof overrideValue === "object" && overrideValue !== null) mergedNested[key] = {
|
|
1387
|
-
...baseValue,
|
|
1388
|
-
...overrideValue
|
|
1389
|
-
};
|
|
1390
|
-
}
|
|
1391
|
-
return merged;
|
|
1392
|
-
}
|
|
1393
|
-
function applyModelOverride(model, override) {
|
|
1394
|
-
return {
|
|
1395
|
-
...model,
|
|
1396
|
-
name: override.name ?? model.name,
|
|
1397
|
-
reasoning: override.reasoning ?? model.reasoning,
|
|
1398
|
-
thinkingLevelMap: override.thinkingLevelMap ? {
|
|
1399
|
-
...model.thinkingLevelMap,
|
|
1400
|
-
...override.thinkingLevelMap
|
|
1401
|
-
} : model.thinkingLevelMap,
|
|
1402
|
-
input: override.input ?? model.input,
|
|
1403
|
-
cost: override.cost ? {
|
|
1404
|
-
input: override.cost.input ?? model.cost.input,
|
|
1405
|
-
output: override.cost.output ?? model.cost.output,
|
|
1406
|
-
cacheRead: override.cost.cacheRead ?? model.cost.cacheRead,
|
|
1407
|
-
cacheWrite: override.cost.cacheWrite ?? model.cost.cacheWrite,
|
|
1408
|
-
tiers: override.cost.tiers ?? model.cost.tiers
|
|
1409
|
-
} : model.cost,
|
|
1410
|
-
contextWindow: override.contextWindow ?? model.contextWindow,
|
|
1411
|
-
maxTokens: override.maxTokens ?? model.maxTokens,
|
|
1412
|
-
samplingParams: override.samplingParams ? {
|
|
1413
|
-
...model.samplingParams,
|
|
1414
|
-
...override.samplingParams
|
|
1415
|
-
} : model.samplingParams,
|
|
1416
|
-
compat: mergeCompat(model.compat, override.compat)
|
|
1417
|
-
};
|
|
1418
|
-
}
|
|
1419
|
-
function modelFromJson(providerId, definition, providerConfig, defaults) {
|
|
1420
|
-
const api = definition.api ?? providerConfig.api ?? defaults?.api;
|
|
1421
|
-
if (!api) throw new Error(`Provider ${providerId}, model ${definition.id}: no "api" specified. Set at provider or model level.`);
|
|
1422
|
-
const baseUrl = definition.baseUrl ?? providerConfig.baseUrl ?? defaults?.baseUrl;
|
|
1423
|
-
if (!baseUrl) throw new Error(`Provider ${providerId}: "baseUrl" is required when defining custom models.`);
|
|
1424
|
-
if (definition.contextWindow !== void 0 && definition.contextWindow <= 0) throw new Error(`Provider ${providerId}, model ${definition.id}: invalid contextWindow`);
|
|
1425
|
-
if (definition.maxTokens !== void 0 && definition.maxTokens <= 0) throw new Error(`Provider ${providerId}, model ${definition.id}: invalid maxTokens`);
|
|
1426
|
-
return {
|
|
1427
|
-
id: definition.id,
|
|
1428
|
-
name: definition.name ?? definition.id,
|
|
1429
|
-
api,
|
|
1430
|
-
provider: providerId,
|
|
1431
|
-
baseUrl,
|
|
1432
|
-
reasoning: definition.reasoning ?? false,
|
|
1433
|
-
thinkingLevelMap: definition.thinkingLevelMap,
|
|
1434
|
-
input: definition.input ?? ["text"],
|
|
1435
|
-
cost: definition.cost ?? {
|
|
1436
|
-
input: 0,
|
|
1437
|
-
output: 0,
|
|
1438
|
-
cacheRead: 0,
|
|
1439
|
-
cacheWrite: 0
|
|
1440
|
-
},
|
|
1441
|
-
contextWindow: definition.contextWindow ?? 128e3,
|
|
1442
|
-
maxTokens: definition.maxTokens ?? 16384,
|
|
1443
|
-
samplingParams: definition.samplingParams,
|
|
1444
|
-
headers: void 0,
|
|
1445
|
-
compat: mergeCompat(providerConfig.compat, definition.compat)
|
|
1446
|
-
};
|
|
1447
|
-
}
|
|
1448
|
-
function applyModelsJson(providerId, baseModels, config) {
|
|
1449
|
-
if (!config) return [...baseModels];
|
|
1450
|
-
if (config.oauth && !config.baseUrl) throw new Error(`Provider ${providerId}: "baseUrl" is required when "oauth" is set.`);
|
|
1451
|
-
const hasOverrides = config.modelOverrides && Object.keys(config.modelOverrides).length > 0;
|
|
1452
|
-
if (!config.models?.length && !config.baseUrl && !config.headers && !config.compat && !hasOverrides && !config.apiKey && !config.oauth && config.authHeader === void 0) throw new Error(`Provider ${providerId}: must specify "baseUrl", "headers", "compat", "modelOverrides", or "models".`);
|
|
1453
|
-
const models = baseModels.map((model) => ({
|
|
1454
|
-
...model,
|
|
1455
|
-
baseUrl: config.oauth === "radius" ? model.baseUrl : config.baseUrl ?? model.baseUrl,
|
|
1456
|
-
compat: mergeCompat(model.compat, config.compat)
|
|
1457
|
-
}));
|
|
1458
|
-
for (const definition of config.models ?? []) {
|
|
1459
|
-
const existingIndex = models.findIndex((model) => model.id === definition.id);
|
|
1460
|
-
const model = modelFromJson(providerId, definition, config, existingIndex >= 0 ? models[existingIndex] : models[0]);
|
|
1461
|
-
if (existingIndex >= 0) models[existingIndex] = model;
|
|
1462
|
-
else models.push(model);
|
|
1463
|
-
}
|
|
1464
|
-
return models;
|
|
1465
|
-
}
|
|
1466
|
-
//#endregion
|
|
1467
|
-
//#region src/compat/vendor/pi-shell-config.ts
|
|
1468
|
-
function isLegacyWslBashPath(path) {
|
|
1469
|
-
const normalized = path.replace(/\//g, "\\").toLowerCase();
|
|
1470
|
-
return /^[a-z]:\\windows\\(?:system32|sysnative)\\bash\.exe$/.test(normalized);
|
|
1471
|
-
}
|
|
1472
|
-
function getBashShellConfig(shell) {
|
|
1473
|
-
return isLegacyWslBashPath(shell) ? {
|
|
1474
|
-
shell,
|
|
1475
|
-
args: ["-s"],
|
|
1476
|
-
commandTransport: "stdin"
|
|
1477
|
-
} : {
|
|
1478
|
-
shell,
|
|
1479
|
-
args: ["-c"]
|
|
1480
|
-
};
|
|
1481
|
-
}
|
|
1482
|
-
function findBashOnPath() {
|
|
1483
|
-
if (process.platform === "win32") {
|
|
1484
|
-
try {
|
|
1485
|
-
const result = spawnSync("where", ["bash.exe"], {
|
|
1486
|
-
encoding: "utf-8",
|
|
1487
|
-
timeout: 5e3,
|
|
1488
|
-
windowsHide: true
|
|
1489
|
-
});
|
|
1490
|
-
if (result.status === 0 && result.stdout) {
|
|
1491
|
-
const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
|
|
1492
|
-
if (firstMatch && existsSync(firstMatch)) return firstMatch;
|
|
1493
|
-
}
|
|
1494
|
-
} catch {}
|
|
1495
|
-
return null;
|
|
1496
|
-
}
|
|
1497
|
-
try {
|
|
1498
|
-
const result = spawnSync("which", ["bash"], {
|
|
1499
|
-
encoding: "utf-8",
|
|
1500
|
-
timeout: 5e3
|
|
1501
|
-
});
|
|
1502
|
-
if (result.status === 0 && result.stdout) {
|
|
1503
|
-
const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
|
|
1504
|
-
if (firstMatch) return firstMatch;
|
|
1505
|
-
}
|
|
1506
|
-
} catch {}
|
|
1507
|
-
return null;
|
|
1508
|
-
}
|
|
1509
|
-
/**
|
|
1510
|
-
* Resolve shell configuration based on platform and an optional explicit shell path.
|
|
1511
|
-
* Resolution order:
|
|
1512
|
-
* 1. User-specified shellPath
|
|
1513
|
-
* 2. On Windows: Git Bash in known locations, then bash on PATH
|
|
1514
|
-
* 3. On Unix: /bin/bash, then bash on PATH, then fallback to sh
|
|
1515
|
-
*/
|
|
1516
|
-
function getShellConfig(customShellPath) {
|
|
1517
|
-
if (customShellPath) {
|
|
1518
|
-
if (existsSync(customShellPath)) return getBashShellConfig(customShellPath);
|
|
1519
|
-
throw new Error(`Custom shell path not found: ${customShellPath}`);
|
|
1520
|
-
}
|
|
1521
|
-
if (process.platform === "win32") {
|
|
1522
|
-
const paths = [];
|
|
1523
|
-
const programFiles = process.env.ProgramFiles;
|
|
1524
|
-
if (programFiles) paths.push(`${programFiles}\\Git\\bin\\bash.exe`);
|
|
1525
|
-
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
1526
|
-
if (programFilesX86) paths.push(`${programFilesX86}\\Git\\bin\\bash.exe`);
|
|
1527
|
-
for (const path of paths) if (existsSync(path)) return getBashShellConfig(path);
|
|
1528
|
-
const bashOnPath = findBashOnPath();
|
|
1529
|
-
if (bashOnPath) return getBashShellConfig(bashOnPath);
|
|
1530
|
-
throw new Error(`No bash shell found. Options:
|
|
1531
|
-
1. Install Git for Windows: https://git-scm.com/download/win
|
|
1532
|
-
2. Add your bash to PATH (Cygwin, MSYS2, etc.)
|
|
1533
|
-
3. Set shellPath in settings.json
|
|
1534
|
-
|
|
1535
|
-
Searched Git Bash in:\n${paths.map((p) => ` ${p}`).join("\n")}`);
|
|
1536
|
-
}
|
|
1537
|
-
if (existsSync("/bin/bash")) return getBashShellConfig("/bin/bash");
|
|
1538
|
-
const bashOnPath = findBashOnPath();
|
|
1539
|
-
if (bashOnPath) return getBashShellConfig(bashOnPath);
|
|
1540
|
-
return {
|
|
1541
|
-
shell: "sh",
|
|
1542
|
-
args: ["-c"]
|
|
1543
|
-
};
|
|
1544
|
-
}
|
|
1545
|
-
//#endregion
|
|
1546
|
-
//#region src/compat/vendor/pi-resolve-config-value.ts
|
|
1547
|
-
/**
|
|
1548
|
-
* Resolve configuration values that may be shell commands, environment variables, or literals.
|
|
1549
|
-
* Used by auth-storage.ts and model-registry.ts.
|
|
1550
|
-
*/
|
|
1551
|
-
const ENV_VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1552
|
-
const ENV_VAR_NAME_PREFIX_RE = /^[A-Za-z_][A-Za-z0-9_]*/;
|
|
1553
|
-
function appendLiteral(parts, value) {
|
|
1554
|
-
if (!value) return;
|
|
1555
|
-
const previousPart = parts[parts.length - 1];
|
|
1556
|
-
if (previousPart?.type === "literal") {
|
|
1557
|
-
previousPart.value += value;
|
|
1558
|
-
return;
|
|
1559
|
-
}
|
|
1560
|
-
parts.push({
|
|
1561
|
-
type: "literal",
|
|
1562
|
-
value
|
|
1563
|
-
});
|
|
1564
|
-
}
|
|
1565
|
-
function parseConfigValueTemplate(config) {
|
|
1566
|
-
const parts = [];
|
|
1567
|
-
let index = 0;
|
|
1568
|
-
while (index < config.length) {
|
|
1569
|
-
const dollarIndex = config.indexOf("$", index);
|
|
1570
|
-
if (dollarIndex < 0) {
|
|
1571
|
-
appendLiteral(parts, config.slice(index));
|
|
1572
|
-
break;
|
|
1573
|
-
}
|
|
1574
|
-
appendLiteral(parts, config.slice(index, dollarIndex));
|
|
1575
|
-
const nextChar = config[dollarIndex + 1];
|
|
1576
|
-
if (nextChar === "$" || nextChar === "!") {
|
|
1577
|
-
appendLiteral(parts, nextChar);
|
|
1578
|
-
index = dollarIndex + 2;
|
|
1579
|
-
continue;
|
|
1580
|
-
}
|
|
1581
|
-
if (nextChar === "{") {
|
|
1582
|
-
const endIndex = config.indexOf("}", dollarIndex + 2);
|
|
1583
|
-
if (endIndex < 0) {
|
|
1584
|
-
appendLiteral(parts, "$");
|
|
1585
|
-
index = dollarIndex + 1;
|
|
1586
|
-
continue;
|
|
1587
|
-
}
|
|
1588
|
-
const name = config.slice(dollarIndex + 2, endIndex);
|
|
1589
|
-
if (ENV_VAR_NAME_RE.test(name)) parts.push({
|
|
1590
|
-
type: "env",
|
|
1591
|
-
name
|
|
1592
|
-
});
|
|
1593
|
-
else appendLiteral(parts, config.slice(dollarIndex, endIndex + 1));
|
|
1594
|
-
index = endIndex + 1;
|
|
1595
|
-
continue;
|
|
1596
|
-
}
|
|
1597
|
-
const match = config.slice(dollarIndex + 1).match(ENV_VAR_NAME_PREFIX_RE);
|
|
1598
|
-
if (match) {
|
|
1599
|
-
parts.push({
|
|
1600
|
-
type: "env",
|
|
1601
|
-
name: match[0]
|
|
1602
|
-
});
|
|
1603
|
-
index = dollarIndex + 1 + match[0].length;
|
|
1604
|
-
continue;
|
|
1605
|
-
}
|
|
1606
|
-
appendLiteral(parts, "$");
|
|
1607
|
-
index = dollarIndex + 1;
|
|
1608
|
-
}
|
|
1609
|
-
return parts;
|
|
1610
|
-
}
|
|
1611
|
-
function parseConfigValueReference(config) {
|
|
1612
|
-
if (config.startsWith("!")) return {
|
|
1613
|
-
type: "command",
|
|
1614
|
-
config
|
|
1615
|
-
};
|
|
1616
|
-
return {
|
|
1617
|
-
type: "template",
|
|
1618
|
-
parts: parseConfigValueTemplate(config)
|
|
1619
|
-
};
|
|
1620
|
-
}
|
|
1621
|
-
function resolveEnvConfigValue(name, env) {
|
|
1622
|
-
return env?.[name] || process.env[name] || void 0;
|
|
1623
|
-
}
|
|
1624
|
-
function getTemplateEnvVarNames(parts) {
|
|
1625
|
-
const names = [];
|
|
1626
|
-
for (const part of parts) {
|
|
1627
|
-
if (part.type !== "env" || names.includes(part.name)) continue;
|
|
1628
|
-
names.push(part.name);
|
|
1629
|
-
}
|
|
1630
|
-
return names;
|
|
1631
|
-
}
|
|
1632
|
-
function resolveTemplate(parts, env) {
|
|
1633
|
-
let resolved = "";
|
|
1634
|
-
for (const part of parts) {
|
|
1635
|
-
if (part.type === "literal") {
|
|
1636
|
-
resolved += part.value;
|
|
1637
|
-
continue;
|
|
1638
|
-
}
|
|
1639
|
-
const envValue = resolveEnvConfigValue(part.name, env);
|
|
1640
|
-
if (envValue === void 0) return void 0;
|
|
1641
|
-
resolved += envValue;
|
|
1642
|
-
}
|
|
1643
|
-
return resolved;
|
|
1644
|
-
}
|
|
1645
|
-
function getConfigValueEnvVarNames(config) {
|
|
1646
|
-
const reference = parseConfigValueReference(config);
|
|
1647
|
-
return reference.type === "template" ? getTemplateEnvVarNames(reference.parts) : [];
|
|
1648
|
-
}
|
|
1649
|
-
function getMissingConfigValueEnvVarNames(config, env) {
|
|
1650
|
-
return getConfigValueEnvVarNames(config).filter((name) => resolveEnvConfigValue(name, env) === void 0);
|
|
1651
|
-
}
|
|
1652
|
-
function isCommandConfigValue(config) {
|
|
1653
|
-
return parseConfigValueReference(config).type === "command";
|
|
1654
|
-
}
|
|
1655
|
-
function isConfigValueConfigured(config, env) {
|
|
1656
|
-
return getMissingConfigValueEnvVarNames(config, env).length === 0;
|
|
1657
|
-
}
|
|
1658
|
-
function executeWithConfiguredShell(command) {
|
|
1659
|
-
try {
|
|
1660
|
-
const { shell, args, commandTransport } = getShellConfig();
|
|
1661
|
-
const commandFromStdin = commandTransport === "stdin";
|
|
1662
|
-
const result = spawnSync(shell, commandFromStdin ? args : [...args, command], {
|
|
1663
|
-
encoding: "utf-8",
|
|
1664
|
-
input: commandFromStdin ? command : void 0,
|
|
1665
|
-
timeout: 1e4,
|
|
1666
|
-
stdio: [
|
|
1667
|
-
commandFromStdin ? "pipe" : "ignore",
|
|
1668
|
-
"pipe",
|
|
1669
|
-
"ignore"
|
|
1670
|
-
],
|
|
1671
|
-
shell: false,
|
|
1672
|
-
windowsHide: true
|
|
1673
|
-
});
|
|
1674
|
-
if (result.error) {
|
|
1675
|
-
if (result.error.code === "ENOENT") return {
|
|
1676
|
-
executed: false,
|
|
1677
|
-
value: void 0
|
|
1678
|
-
};
|
|
1679
|
-
return {
|
|
1680
|
-
executed: true,
|
|
1681
|
-
value: void 0
|
|
1682
|
-
};
|
|
1683
|
-
}
|
|
1684
|
-
if (result.status !== 0) return {
|
|
1685
|
-
executed: true,
|
|
1686
|
-
value: void 0
|
|
1687
|
-
};
|
|
1688
|
-
return {
|
|
1689
|
-
executed: true,
|
|
1690
|
-
value: (result.stdout ?? "").trim() || void 0
|
|
1691
|
-
};
|
|
1692
|
-
} catch {
|
|
1693
|
-
return {
|
|
1694
|
-
executed: false,
|
|
1695
|
-
value: void 0
|
|
1696
|
-
};
|
|
1697
|
-
}
|
|
1698
|
-
}
|
|
1699
|
-
function executeWithDefaultShell(command) {
|
|
1700
|
-
try {
|
|
1701
|
-
return execSync(command, {
|
|
1702
|
-
encoding: "utf-8",
|
|
1703
|
-
timeout: 1e4,
|
|
1704
|
-
stdio: [
|
|
1705
|
-
"ignore",
|
|
1706
|
-
"pipe",
|
|
1707
|
-
"ignore"
|
|
1708
|
-
]
|
|
1709
|
-
}).trim() || void 0;
|
|
1710
|
-
} catch {
|
|
1711
|
-
return;
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
function executeCommandUncached(commandConfig) {
|
|
1715
|
-
const command = commandConfig.slice(1);
|
|
1716
|
-
return process.platform === "win32" ? (() => {
|
|
1717
|
-
const configuredResult = executeWithConfiguredShell(command);
|
|
1718
|
-
return configuredResult.executed ? configuredResult.value : executeWithDefaultShell(command);
|
|
1719
|
-
})() : executeWithDefaultShell(command);
|
|
1720
|
-
}
|
|
1721
|
-
/**
|
|
1722
|
-
* Resolve all header values using the same resolution logic as API keys.
|
|
1723
|
-
*/
|
|
1724
|
-
function resolveConfigValueUncached(config, env) {
|
|
1725
|
-
const reference = parseConfigValueReference(config);
|
|
1726
|
-
if (reference.type === "command") return executeCommandUncached(reference.config);
|
|
1727
|
-
return resolveTemplate(reference.parts, env);
|
|
1728
|
-
}
|
|
1729
|
-
function resolveConfigValueOrThrow(config, description, env) {
|
|
1730
|
-
const resolvedValue = resolveConfigValueUncached(config, env);
|
|
1731
|
-
if (resolvedValue !== void 0) return resolvedValue;
|
|
1732
|
-
const reference = parseConfigValueReference(config);
|
|
1733
|
-
if (reference.type === "command") throw new Error(`Failed to resolve ${description} from shell command: ${reference.config.slice(1)}`);
|
|
1734
|
-
if (reference.type === "template") {
|
|
1735
|
-
const missingEnvVars = getMissingConfigValueEnvVarNames(config, env);
|
|
1736
|
-
if (missingEnvVars.length === 1) throw new Error(`Failed to resolve ${description} from environment variable: ${missingEnvVars[0]}`);
|
|
1737
|
-
if (missingEnvVars.length > 1) throw new Error(`Failed to resolve ${description} from environment variables: ${missingEnvVars.join(", ")}`);
|
|
1738
|
-
}
|
|
1739
|
-
throw new Error(`Failed to resolve ${description}`);
|
|
1740
|
-
}
|
|
1741
|
-
function resolveHeadersOrThrow(headers, description, env) {
|
|
1742
|
-
if (!headers) return void 0;
|
|
1743
|
-
const resolved = {};
|
|
1744
|
-
for (const [key, value] of Object.entries(headers)) resolved[key] = resolveConfigValueOrThrow(value, `${description} header "${key}"`, env);
|
|
1745
|
-
return Object.keys(resolved).length > 0 ? resolved : void 0;
|
|
1746
|
-
}
|
|
1747
|
-
//#endregion
|
|
1748
|
-
//#region src/models-json.ts
|
|
1749
|
-
function modelsJsonPath() {
|
|
1750
|
-
return join(getAgentDir(), "models.json");
|
|
1751
|
-
}
|
|
1752
|
-
const emptySnapshot = () => ({
|
|
1753
|
-
models: [],
|
|
1754
|
-
providers: /* @__PURE__ */ new Map(),
|
|
1755
|
-
errors: [],
|
|
1756
|
-
companions: /* @__PURE__ */ new Map()
|
|
1757
|
-
});
|
|
1758
|
-
function companionImageModels(provider) {
|
|
1759
|
-
if (provider.baseUrl !== void 0 || provider.models !== void 0 || provider.apiKey !== void 0) return void 0;
|
|
1760
|
-
const overrides = provider.modelOverrides;
|
|
1761
|
-
if (overrides === void 0) return void 0;
|
|
1762
|
-
const imageModels = /* @__PURE__ */ new Set();
|
|
1763
|
-
for (const [modelId, override] of Object.entries(overrides)) if (Array.isArray(override.input) && override.input.includes("image")) imageModels.add(modelId);
|
|
1764
|
-
return imageModels.size > 0 ? imageModels : void 0;
|
|
1765
|
-
}
|
|
1766
|
-
/**
|
|
1767
|
-
* One load of models.json projected to Pi Model objects. Mirrors Pi's
|
|
1768
|
-
* composeModelProvider composition order: base (builtin) models →
|
|
1769
|
-
* applyModelsJson upserts → modelOverrides as the topmost user-config layer.
|
|
1770
|
-
* A provider that fails composition contributes an error and is skipped —
|
|
1771
|
-
* the same per-entry isolation the rest of the bridge applies to extensions.
|
|
1772
|
-
*/
|
|
1773
|
-
async function loadModelsJsonSnapshot(baseProviderOf) {
|
|
1774
|
-
const snapshot = emptySnapshot();
|
|
1775
|
-
const config = await ModelConfig.load(modelsJsonPath());
|
|
1776
|
-
const loadError = config.getError();
|
|
1777
|
-
if (loadError !== void 0) {
|
|
1778
|
-
snapshot.errors.push(loadError);
|
|
1779
|
-
return snapshot;
|
|
1780
|
-
}
|
|
1781
|
-
for (const providerId of config.getProviderIds()) {
|
|
1782
|
-
const providerConfig = config.getProvider(providerId);
|
|
1783
|
-
if (providerConfig === void 0) continue;
|
|
1784
|
-
const imageModels = companionImageModels(providerConfig);
|
|
1785
|
-
if (imageModels !== void 0) {
|
|
1786
|
-
snapshot.companions.set(providerId, imageModels);
|
|
1787
|
-
continue;
|
|
1788
|
-
}
|
|
1789
|
-
try {
|
|
1790
|
-
const base = await baseProviderOf?.(providerId);
|
|
1791
|
-
const composed = applyModelsJson(providerId, typeof base?.getModels === "function" ? base.getModels() : [], providerConfig).map((model) => {
|
|
1792
|
-
const override = providerConfig.modelOverrides?.[String(model.id)];
|
|
1793
|
-
return override ? applyModelOverride(model, override) : model;
|
|
1794
|
-
});
|
|
1795
|
-
snapshot.models.push(...composed);
|
|
1796
|
-
snapshot.providers.set(providerId, providerConfig);
|
|
1797
|
-
} catch (error) {
|
|
1798
|
-
snapshot.errors.push(error instanceof Error ? error.message : String(error));
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
return snapshot;
|
|
1802
|
-
}
|
|
1803
|
-
/**
|
|
1804
|
-
* Pi's configured-auth answer for a models.json provider, following
|
|
1805
|
-
* composeApiKeyAuth.resolve + withConfiguredAuth and the registry's
|
|
1806
|
-
* translation of their failures:
|
|
1807
|
-
* - a configured apiKey resolves through $ENV/!command or fails loudly with
|
|
1808
|
-
* the resolver's message;
|
|
1809
|
-
* - authHeader folds the resolved key into an Authorization header and,
|
|
1810
|
-
* without a key, becomes Pi's "No API key found" answer;
|
|
1811
|
-
* - a provider with neither key nor authHeader answers with its resolved
|
|
1812
|
-
* headers only (the registry's compatibility path).
|
|
1813
|
-
*/
|
|
1814
|
-
function resolveModelsJsonAuth(providerId, provider) {
|
|
1815
|
-
try {
|
|
1816
|
-
const headers = resolveHeadersOrThrow(provider.headers, `provider "${providerId}"`);
|
|
1817
|
-
const rawKey = provider.apiKey;
|
|
1818
|
-
if (rawKey === void 0) {
|
|
1819
|
-
if (provider.authHeader ?? false) return {
|
|
1820
|
-
ok: false,
|
|
1821
|
-
error: `No API key found for "${providerId}"`
|
|
1822
|
-
};
|
|
1823
|
-
return {
|
|
1824
|
-
ok: true,
|
|
1825
|
-
...headers === void 0 ? {} : { headers }
|
|
1826
|
-
};
|
|
1827
|
-
}
|
|
1828
|
-
const apiKey = resolveConfigValueOrThrow(rawKey, `API key for provider "${providerId}"`);
|
|
1829
|
-
const merged = provider.authHeader ?? false ? {
|
|
1830
|
-
...headers,
|
|
1831
|
-
Authorization: `Bearer ${apiKey}`
|
|
1832
|
-
} : headers;
|
|
1833
|
-
return {
|
|
1834
|
-
ok: true,
|
|
1835
|
-
apiKey,
|
|
1836
|
-
...merged === void 0 ? {} : { headers: merged }
|
|
1837
|
-
};
|
|
1838
|
-
} catch (error) {
|
|
1839
|
-
return {
|
|
1840
|
-
ok: false,
|
|
1841
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1842
|
-
};
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
/**
|
|
1846
|
-
* Pi's withConfiguredAuth layered over an auth resolved by another family
|
|
1847
|
-
* (an extension-registered provider or a builtin): models.json headers
|
|
1848
|
-
* override the resolved ones, and authHeader folds the resolved key into
|
|
1849
|
-
* an Authorization header — failing loudly without one, as Pi does.
|
|
1850
|
-
*/
|
|
1851
|
-
function applyModelsJsonConfiguredAuth(providerId, provider, auth) {
|
|
1852
|
-
try {
|
|
1853
|
-
const configured = resolveHeadersOrThrow(provider.headers, `provider "${providerId}"`);
|
|
1854
|
-
let headers = auth.headers || configured ? {
|
|
1855
|
-
...auth.headers,
|
|
1856
|
-
...configured
|
|
1857
|
-
} : void 0;
|
|
1858
|
-
if (provider.authHeader ?? false) {
|
|
1859
|
-
if (auth.apiKey === void 0) return {
|
|
1860
|
-
ok: false,
|
|
1861
|
-
error: `No API key found for "${providerId}"`
|
|
1862
|
-
};
|
|
1863
|
-
headers = {
|
|
1864
|
-
...headers,
|
|
1865
|
-
Authorization: `Bearer ${auth.apiKey}`
|
|
1866
|
-
};
|
|
1867
|
-
}
|
|
1868
|
-
return {
|
|
1869
|
-
ok: true,
|
|
1870
|
-
...auth.apiKey === void 0 ? {} : { apiKey: auth.apiKey },
|
|
1871
|
-
...headers === void 0 ? {} : { headers }
|
|
1872
|
-
};
|
|
1873
|
-
} catch (error) {
|
|
1874
|
-
return {
|
|
1875
|
-
ok: false,
|
|
1876
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
/**
|
|
1881
|
-
* One models.json provider as a Pi Provider object: the composed Pi Models
|
|
1882
|
-
* as its catalog and Pi's per-api wire clients as its transport (attached by
|
|
1883
|
-
* the provider adapter's synthesis). Registered as a DSH llm route, this
|
|
1884
|
-
* makes the DSH directory the single model directory — the Pi registry
|
|
1885
|
-
* projects it back, and registry.getProvider() answers a genuine Pi
|
|
1886
|
-
* Provider, so packages that stream through the provider surface work
|
|
1887
|
-
* unchanged.
|
|
1888
|
-
*/
|
|
1889
|
-
function modelsJsonPiProvider(providerId, provider, models) {
|
|
1890
|
-
return {
|
|
1891
|
-
id: providerId,
|
|
1892
|
-
name: provider.name ?? providerId,
|
|
1893
|
-
...provider.baseUrl === void 0 ? {} : { baseUrl: provider.baseUrl },
|
|
1894
|
-
...provider.api === void 0 ? {} : { api: provider.api },
|
|
1895
|
-
getModels: () => models
|
|
1896
|
-
};
|
|
1897
|
-
}
|
|
1898
|
-
/**
|
|
1899
|
-
* Pi's configured-check (not liveness) for a models.json provider — the
|
|
1900
|
-
* configuredRequestAuthStatus semantics: a literal or !command key counts as
|
|
1901
|
-
* configured, a templated key counts only when its env vars are present, and
|
|
1902
|
-
* a provider without an apiKey defers to the other credential families.
|
|
1903
|
-
*/
|
|
1904
|
-
function modelsJsonAuthConfigured(provider) {
|
|
1905
|
-
const value = provider.apiKey;
|
|
1906
|
-
if (value === void 0) return void 0;
|
|
1907
|
-
if (isCommandConfigValue(value)) return true;
|
|
1908
|
-
if (getConfigValueEnvVarNames(value).length > 0) return isConfigValueConfigured(value);
|
|
1909
|
-
return true;
|
|
1910
|
-
}
|
|
1911
|
-
//#endregion
|
|
1912
1114
|
//#region src/provider-adapter.ts
|
|
1913
|
-
const PI_API_FACTORIES = {
|
|
1914
|
-
"openai-completions": openAICompletionsApi,
|
|
1915
|
-
"openai-responses": openAIResponsesApi,
|
|
1916
|
-
"anthropic-messages": anthropicMessagesApi
|
|
1917
|
-
};
|
|
1918
|
-
function piApiClientFor(api) {
|
|
1919
|
-
const factory = typeof api === "string" ? PI_API_FACTORIES[api] : void 0;
|
|
1920
|
-
return factory === void 0 ? void 0 : factory();
|
|
1921
|
-
}
|
|
1922
|
-
/**
|
|
1923
|
-
* Give a config-only Pi provider (models.json entries, packages that declare
|
|
1924
|
-
* models without a stream) Pi's own transport: each call dispatches on the
|
|
1925
|
-
* model's declared `api` to the real pi-ai client, exactly as Pi's
|
|
1926
|
-
* provider composer does. Undispatchable providers (no models, or an api
|
|
1927
|
-
* with no client) answer undefined and stay off the directory.
|
|
1928
|
-
*/
|
|
1929
|
-
function withSynthesizedTransport(provider) {
|
|
1930
|
-
const models = typeof provider.getModels === "function" ? provider.getModels() : Array.isArray(provider.models) ? provider.models : [];
|
|
1931
|
-
if (models.length === 0) return void 0;
|
|
1932
|
-
if (!models.some((model) => piApiClientFor(model.api) !== void 0)) return void 0;
|
|
1933
|
-
const dispatch = (simple) => (model, context, options) => {
|
|
1934
|
-
const client = piApiClientFor(model.api);
|
|
1935
|
-
if (client === void 0) throw new Error(`pi2dsh: no wire-protocol client for api ${JSON.stringify(String(model.api))} (model ${String(model.provider)}/${String(model.id)})`);
|
|
1936
|
-
return (simple ? client.streamSimple ?? client.stream : client.stream).call(client, model, context, options);
|
|
1937
|
-
};
|
|
1938
|
-
return {
|
|
1939
|
-
...provider,
|
|
1940
|
-
getModels: () => models,
|
|
1941
|
-
stream: dispatch(false),
|
|
1942
|
-
streamSimple: dispatch(true)
|
|
1943
|
-
};
|
|
1944
|
-
}
|
|
1945
1115
|
function providerCarriesTransport(value) {
|
|
1946
1116
|
return typeof value?.stream === "function";
|
|
1947
1117
|
}
|
|
@@ -2044,7 +1214,7 @@ async function textOnlyMessages(messages, materialize) {
|
|
|
2044
1214
|
}
|
|
2045
1215
|
/**
|
|
2046
1216
|
* A DSH route that admits images on behalf of a text-only route — the
|
|
2047
|
-
* single-directory answer to
|
|
1217
|
+
* single-directory answer to host configuration declaring
|
|
2048
1218
|
* `input: ["text", "image"]` for models of a route this bridge does not own.
|
|
2049
1219
|
* The companion honestly declares image input (so the host's admission and
|
|
2050
1220
|
* model-switch checks pass), replaces image blocks with an explicit notice,
|
|
@@ -2053,7 +1223,8 @@ async function textOnlyMessages(messages, materialize) {
|
|
|
2053
1223
|
* extension handles the turn's entering messages.
|
|
2054
1224
|
*/
|
|
2055
1225
|
function imageAdmissionCompanionAdapter(options) {
|
|
2056
|
-
const { originalId,
|
|
1226
|
+
const { originalId, llm } = options;
|
|
1227
|
+
const admits = (modelId) => options.imageModels === void 0 || options.imageModels.has(modelId);
|
|
2057
1228
|
const materialize = options.materializeImage ?? (async () => void 0);
|
|
2058
1229
|
const admitImage = (info, id) => {
|
|
2059
1230
|
const modalities = Array.isArray(info.inputModalities) ? info.inputModalities.slice() : ["text"];
|
|
@@ -2072,10 +1243,10 @@ function imageAdmissionCompanionAdapter(options) {
|
|
|
2072
1243
|
};
|
|
2073
1244
|
},
|
|
2074
1245
|
providerRetryPolicy: () => void 0,
|
|
2075
|
-
listModels: async (id) => (await llm.listModels(originalId)).filter((model) =>
|
|
1246
|
+
listModels: async (id) => (await llm.listModels(originalId)).filter((model) => admits(String(model.id))).map((model) => admitImage(model, id)),
|
|
2076
1247
|
resolveModel: async (id, modelId) => {
|
|
2077
1248
|
const info = await llm.resolveModelInfo(originalId, modelId);
|
|
2078
|
-
return
|
|
1249
|
+
return admits(modelId) ? admitImage(info, id) : {
|
|
2079
1250
|
...info,
|
|
2080
1251
|
provider: id
|
|
2081
1252
|
};
|
|
@@ -2100,10 +1271,12 @@ function imageAdmissionCompanionAdapter(options) {
|
|
|
2100
1271
|
function registerPiProviderRoute(options) {
|
|
2101
1272
|
const { llm, providerId, provider, host } = options;
|
|
2102
1273
|
if (llm === void 0) return void 0;
|
|
2103
|
-
|
|
2104
|
-
|
|
1274
|
+
if (!providerCarriesTransport(provider)) {
|
|
1275
|
+
host.warn(`[pi2dsh] Pi provider ${JSON.stringify(providerId)} declares a model catalog but no transport; it was not added as a DSH llm route — configure the gateway in the host's llm settings instead`);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
2105
1278
|
try {
|
|
2106
|
-
return llm.registerAdapter([providerId], piProviderDshAdapter(providerId,
|
|
1279
|
+
return llm.registerAdapter([providerId], piProviderDshAdapter(providerId, provider, host));
|
|
2107
1280
|
} catch (error) {
|
|
2108
1281
|
host.warn(`[pi2dsh] Pi provider ${JSON.stringify(providerId)} was not added as a DSH llm route (${error instanceof Error ? error.message : String(error)}); the existing route keeps the name`);
|
|
2109
1282
|
return;
|
|
@@ -2457,7 +1630,7 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
2457
1630
|
const providerConfig = (name) => state.providers.get(name);
|
|
2458
1631
|
const catalog = state.modelCatalog;
|
|
2459
1632
|
const piNativeEntry = (provider, id) => {
|
|
2460
|
-
const source = state.
|
|
1633
|
+
const source = state.providerRouteDisposers.has(provider) ? state.providers.get(provider) : void 0;
|
|
2461
1634
|
if (source === void 0) return void 0;
|
|
2462
1635
|
let models;
|
|
2463
1636
|
try {
|
|
@@ -2509,24 +1682,15 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
2509
1682
|
const entry = catalog?.find(provider, modelId);
|
|
2510
1683
|
return entry === void 0 ? void 0 : restorePiShape(entry);
|
|
2511
1684
|
},
|
|
2512
|
-
getError: () =>
|
|
2513
|
-
const problems = state.modelsJson?.errors ?? [];
|
|
2514
|
-
return problems.length > 0 ? problems.join("\n") : void 0;
|
|
2515
|
-
},
|
|
1685
|
+
getError: () => void 0,
|
|
2516
1686
|
hasConfiguredAuth: (model) => {
|
|
2517
1687
|
const provider = String(model?.provider ?? "");
|
|
2518
1688
|
if (provider.length === 0) return false;
|
|
2519
|
-
|
|
2520
|
-
if (jsonProvider !== void 0) {
|
|
2521
|
-
const configured = modelsJsonAuthConfigured(jsonProvider);
|
|
2522
|
-
if (configured !== void 0) return configured;
|
|
2523
|
-
}
|
|
2524
|
-
return state.providers.has(provider) || (catalog?.all() ?? []).some((entry) => entry.provider === provider) || jsonProvider !== void 0;
|
|
1689
|
+
return state.providers.has(provider) || (catalog?.all() ?? []).some((entry) => entry.provider === provider);
|
|
2525
1690
|
},
|
|
2526
1691
|
getApiKeyAndHeaders: async (model) => {
|
|
2527
1692
|
const provider = String(model?.provider ?? "");
|
|
2528
|
-
const
|
|
2529
|
-
const config = providerConfig(provider) ?? (jsonProvider?.apiKey !== void 0 ? void 0 : await realBuiltinProvider(provider));
|
|
1693
|
+
const config = providerConfig(provider);
|
|
2530
1694
|
if (config !== void 0) {
|
|
2531
1695
|
const auth = (await resolvePiProviderAuth({
|
|
2532
1696
|
providerId: provider,
|
|
@@ -2534,34 +1698,13 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
2534
1698
|
store: oauthStoreOf(state)
|
|
2535
1699
|
}))?.auth;
|
|
2536
1700
|
if (auth?.apiKey === void 0) return { ok: false };
|
|
2537
|
-
const configured = jsonProvider === void 0 ? void 0 : applyModelsJsonConfiguredAuth(provider, jsonProvider, {
|
|
2538
|
-
apiKey: auth.apiKey,
|
|
2539
|
-
...auth.headers === void 0 ? {} : { headers: auth.headers }
|
|
2540
|
-
});
|
|
2541
|
-
if (configured !== void 0 && !configured.ok) return {
|
|
2542
|
-
ok: false,
|
|
2543
|
-
error: configured.error
|
|
2544
|
-
};
|
|
2545
|
-
const headers = configured?.ok ? configured.headers : auth.headers;
|
|
2546
1701
|
return {
|
|
2547
1702
|
ok: true,
|
|
2548
1703
|
apiKey: auth.apiKey,
|
|
2549
|
-
...headers === void 0 ? {} : { headers },
|
|
1704
|
+
...auth.headers === void 0 ? {} : { headers: auth.headers },
|
|
2550
1705
|
...auth.baseUrl === void 0 && config.baseUrl === void 0 ? {} : { baseUrl: auth.baseUrl ?? config.baseUrl }
|
|
2551
1706
|
};
|
|
2552
1707
|
}
|
|
2553
|
-
if (jsonProvider !== void 0) {
|
|
2554
|
-
const resolved = resolveModelsJsonAuth(provider, jsonProvider);
|
|
2555
|
-
if (!resolved.ok) return {
|
|
2556
|
-
ok: false,
|
|
2557
|
-
error: resolved.error
|
|
2558
|
-
};
|
|
2559
|
-
return {
|
|
2560
|
-
ok: true,
|
|
2561
|
-
...resolved.apiKey === void 0 ? {} : { apiKey: resolved.apiKey },
|
|
2562
|
-
...resolved.headers === void 0 ? {} : { headers: resolved.headers }
|
|
2563
|
-
};
|
|
2564
|
-
}
|
|
2565
1708
|
try {
|
|
2566
1709
|
const entry = llmOf(ctx)?.listConfigurableProviders?.()?.find((candidate) => candidate.provider === provider);
|
|
2567
1710
|
if (entry === void 0) return { ok: false };
|
|
@@ -2580,8 +1723,8 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
2580
1723
|
},
|
|
2581
1724
|
getProviderAuthStatus: (provider) => providerSupportsOAuth(providerConfig(provider)) ? "oauth" : "none",
|
|
2582
1725
|
getProvider: (provider) => {
|
|
2583
|
-
const base = providerConfig(provider)
|
|
2584
|
-
if (base !== void 0) return state.providerRouteDisposers.has(provider)
|
|
1726
|
+
const base = providerConfig(provider);
|
|
1727
|
+
if (base !== void 0) return state.providerRouteDisposers.has(provider) ? dshRoutedPiProvider(base, provider) : base;
|
|
2585
1728
|
if ((catalog?.all() ?? []).some((entry) => entry.provider === provider)) return dshRoutedPiProvider({
|
|
2586
1729
|
id: provider,
|
|
2587
1730
|
name: provider
|
|
@@ -2595,7 +1738,7 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
2595
1738
|
getRegisteredProviderConfig: (provider) => providerConfig(provider),
|
|
2596
1739
|
getRegisteredProviderIds: () => [...state.providers.keys()],
|
|
2597
1740
|
getProviderDisplayName: (provider) => {
|
|
2598
|
-
const config = providerConfig(provider)
|
|
1741
|
+
const config = providerConfig(provider);
|
|
2599
1742
|
return typeof config?.name === "string" ? config.name : provider;
|
|
2600
1743
|
},
|
|
2601
1744
|
getProviderAuth: async (provider) => {
|
|
@@ -2629,11 +1772,10 @@ function contextFor(ctx, state, agent, signal, command = false) {
|
|
|
2629
1772
|
return provider.length > 0 && providerSupportsOAuth(providerConfig(provider));
|
|
2630
1773
|
},
|
|
2631
1774
|
refresh: async () => {
|
|
2632
|
-
await reloadModelsJson(ctx, state, true);
|
|
2633
1775
|
await catalog?.refresh();
|
|
2634
1776
|
return {
|
|
2635
1777
|
models: allModels(),
|
|
2636
|
-
errors: [
|
|
1778
|
+
errors: []
|
|
2637
1779
|
};
|
|
2638
1780
|
}
|
|
2639
1781
|
},
|
|
@@ -3170,14 +2312,13 @@ function oauthStoreOf(state) {
|
|
|
3170
2312
|
state.shared.oauthStore ??= new FileCredentialStore(join(getAgentDir(), "auth.json"));
|
|
3171
2313
|
return state.shared.oauthStore;
|
|
3172
2314
|
}
|
|
3173
|
-
const
|
|
2315
|
+
const COMPANION_ROUTE_PREFIX = "pi2dsh-companion/";
|
|
3174
2316
|
const SHARED_HOST_STATE = /* @__PURE__ */ new WeakMap();
|
|
3175
2317
|
function sharedHostStateOf(ctx) {
|
|
3176
2318
|
const key = ctx.root ?? ctx;
|
|
3177
2319
|
let shared = SHARED_HOST_STATE.get(key);
|
|
3178
2320
|
if (shared === void 0) {
|
|
3179
2321
|
shared = {
|
|
3180
|
-
modelsJsonProviders: /* @__PURE__ */ new Map(),
|
|
3181
2322
|
companionRoutes: /* @__PURE__ */ new Map(),
|
|
3182
2323
|
providerRouteDisposers: /* @__PURE__ */ new Map(),
|
|
3183
2324
|
providers: /* @__PURE__ */ new Map()
|
|
@@ -3187,87 +2328,102 @@ function sharedHostStateOf(ctx) {
|
|
|
3187
2328
|
return shared;
|
|
3188
2329
|
}
|
|
3189
2330
|
/**
|
|
3190
|
-
*
|
|
3191
|
-
*
|
|
3192
|
-
*
|
|
3193
|
-
*
|
|
2331
|
+
* Image-admission companion routes: for every text-only route in the DSH
|
|
2332
|
+
* llm directory, a `<route>-vision` route that admits images at the host's
|
|
2333
|
+
* admission checks, replaces image blocks with explicit path-carrying
|
|
2334
|
+
* notices, and forwards text-only to the original route. What happens to
|
|
2335
|
+
* an admitted image is decided at run time by whatever is mounted — a
|
|
2336
|
+
* vision extension analyzes it through the turn's entering messages, and
|
|
2337
|
+
* without one the notice's file path lets any image-capable tool read it —
|
|
2338
|
+
* so companions need no knowledge of any particular plugin and are
|
|
2339
|
+
* registered AUTOMATICALLY (zero configuration). `visionCompanions: false`
|
|
2340
|
+
* turns them off; an explicit `{ <route>: [modelIds] }` map narrows them.
|
|
2341
|
+
* The directory is live: adapters-updated re-sweeps, adding companions for
|
|
2342
|
+
* new text-only routes and disposing companions whose original vanished.
|
|
2343
|
+
* The companion is an ordinary directory entry (single-directory
|
|
2344
|
+
* contract); Pi's ctx.model reports the original route for it
|
|
2345
|
+
* (companionRoutes). Idempotent per host.
|
|
3194
2346
|
*/
|
|
3195
|
-
|
|
2347
|
+
function registerVisionCompanions(ctx, config) {
|
|
2348
|
+
if (config === false) return;
|
|
3196
2349
|
const shared = sharedHostStateOf(ctx);
|
|
3197
|
-
await reloadModelsJson(ctx, {
|
|
3198
|
-
shared,
|
|
3199
|
-
modelsJsonProviders: shared.modelsJsonProviders,
|
|
3200
|
-
companionRoutes: shared.companionRoutes,
|
|
3201
|
-
providerRouteDisposers: shared.providerRouteDisposers
|
|
3202
|
-
});
|
|
3203
|
-
}
|
|
3204
|
-
async function reloadModelsJson(ctx, state, force = false) {
|
|
3205
|
-
if (!force && state.shared.modelsJson !== void 0) {
|
|
3206
|
-
state.modelsJson = state.shared.modelsJson;
|
|
3207
|
-
return;
|
|
3208
|
-
}
|
|
3209
|
-
for (const [key, dispose] of state.providerRouteDisposers) {
|
|
3210
|
-
if (!key.startsWith(MODELS_JSON_ROUTE_PREFIX)) continue;
|
|
3211
|
-
state.providerRouteDisposers.delete(key);
|
|
3212
|
-
dispose();
|
|
3213
|
-
}
|
|
3214
|
-
state.modelsJsonProviders.clear();
|
|
3215
|
-
state.companionRoutes.clear();
|
|
3216
|
-
state.modelsJson = await loadModelsJsonSnapshot(realBuiltinProvider);
|
|
3217
|
-
state.shared.modelsJson = state.modelsJson;
|
|
3218
|
-
for (const problem of state.modelsJson.errors) logger(ctx).warn(`[pi2dsh] models.json: ${problem}`);
|
|
3219
|
-
for (const [providerId, providerConfig] of state.modelsJson.providers) {
|
|
3220
|
-
const models = state.modelsJson.models.filter((model) => model.provider === providerId);
|
|
3221
|
-
const provider = modelsJsonPiProvider(providerId, providerConfig, models);
|
|
3222
|
-
const routeDisposer = registerPiProviderRoute({
|
|
3223
|
-
llm: llmOf(ctx),
|
|
3224
|
-
providerId,
|
|
3225
|
-
provider,
|
|
3226
|
-
host: {
|
|
3227
|
-
resolveAuth: async () => {
|
|
3228
|
-
const resolved = resolveModelsJsonAuth(providerId, providerConfig);
|
|
3229
|
-
if (!resolved.ok) return void 0;
|
|
3230
|
-
return { auth: {
|
|
3231
|
-
...resolved.apiKey === void 0 ? {} : { apiKey: resolved.apiKey },
|
|
3232
|
-
...resolved.headers === void 0 ? {} : { headers: resolved.headers },
|
|
3233
|
-
...providerConfig.baseUrl === void 0 ? {} : { baseUrl: providerConfig.baseUrl }
|
|
3234
|
-
} };
|
|
3235
|
-
},
|
|
3236
|
-
warn: (message) => logger(ctx).warn(message)
|
|
3237
|
-
}
|
|
3238
|
-
});
|
|
3239
|
-
if (routeDisposer !== void 0) {
|
|
3240
|
-
state.modelsJsonProviders.set(providerId, provider);
|
|
3241
|
-
state.providerRouteDisposers.set(`${MODELS_JSON_ROUTE_PREFIX}${providerId}`, routeDisposer);
|
|
3242
|
-
logger(ctx).info(`[pi2dsh] models.json provider ${JSON.stringify(providerId)} registered as a native DSH llm route (${models.length} models)`);
|
|
3243
|
-
}
|
|
3244
|
-
}
|
|
3245
|
-
registerCompanionRoutes(ctx, state);
|
|
3246
|
-
}
|
|
3247
|
-
function registerCompanionRoutes(ctx, state) {
|
|
3248
|
-
const companions = state.modelsJson?.companions;
|
|
3249
|
-
if (companions === void 0 || companions.size === 0) return;
|
|
3250
2350
|
const llm = llmOf(ctx);
|
|
3251
2351
|
if (llm === void 0) return;
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
2352
|
+
const sweep = async () => {
|
|
2353
|
+
const explicit = config;
|
|
2354
|
+
const providers = llm.listProviders();
|
|
2355
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
2356
|
+
if (explicit !== void 0) for (const [originalId, modelIds] of Object.entries(explicit)) {
|
|
2357
|
+
if (!Array.isArray(modelIds) || modelIds.length === 0) continue;
|
|
2358
|
+
if (!providers.some((provider) => provider.id === originalId)) {
|
|
2359
|
+
logger(ctx).warn(`[pi2dsh] visionCompanions names route ${JSON.stringify(originalId)}, but no such llm route exists; no companion route was registered`);
|
|
2360
|
+
continue;
|
|
2361
|
+
}
|
|
2362
|
+
wanted.set(`${originalId}-vision`, {
|
|
3261
2363
|
originalId,
|
|
3262
|
-
imageModels
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
2364
|
+
imageModels: new Set(modelIds.map(String))
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2367
|
+
else for (const provider of providers) {
|
|
2368
|
+
if (shared.companionRoutes.has(provider.id)) continue;
|
|
2369
|
+
try {
|
|
2370
|
+
const models = await llm.listModels(provider.id);
|
|
2371
|
+
if (models.length === 0) continue;
|
|
2372
|
+
if (models.every((model) => !(Array.isArray(model.inputModalities) && model.inputModalities.includes("image")))) wanted.set(`${provider.id}-vision`, {
|
|
2373
|
+
originalId: provider.id,
|
|
2374
|
+
imageModels: void 0
|
|
2375
|
+
});
|
|
2376
|
+
} catch {}
|
|
2377
|
+
}
|
|
2378
|
+
for (const [key, dispose] of shared.providerRouteDisposers) {
|
|
2379
|
+
if (!key.startsWith(COMPANION_ROUTE_PREFIX)) continue;
|
|
2380
|
+
const companionId = key.slice(17);
|
|
2381
|
+
const originalId = shared.companionRoutes.get(companionId);
|
|
2382
|
+
if (originalId !== void 0 && !providers.some((provider) => provider.id === originalId)) {
|
|
2383
|
+
shared.providerRouteDisposers.delete(key);
|
|
2384
|
+
dispose();
|
|
2385
|
+
logger(ctx).info(`[pi2dsh] companion route ${JSON.stringify(companionId)} disposed (its original route ${JSON.stringify(originalId)} left the directory)`);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
for (const [companionId, spec] of wanted) {
|
|
2389
|
+
shared.companionRoutes.set(companionId, spec.originalId);
|
|
2390
|
+
if (shared.providerRouteDisposers.has(`${COMPANION_ROUTE_PREFIX}${companionId}`)) continue;
|
|
2391
|
+
try {
|
|
2392
|
+
const dispose = llm.registerAdapter([companionId], imageAdmissionCompanionAdapter({
|
|
2393
|
+
originalId: spec.originalId,
|
|
2394
|
+
...spec.imageModels === void 0 ? {} : { imageModels: spec.imageModels },
|
|
2395
|
+
llm,
|
|
2396
|
+
materializeImage: (attachment) => materializeAttachmentImage(ctx, attachment)
|
|
2397
|
+
}));
|
|
2398
|
+
shared.providerRouteDisposers.set(`${COMPANION_ROUTE_PREFIX}${companionId}`, dispose);
|
|
2399
|
+
logger(ctx).info(`[pi2dsh] image-admission companion route ${JSON.stringify(companionId)} registered for ${JSON.stringify(spec.originalId)}`);
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
logger(ctx).warn(`[pi2dsh] companion route ${JSON.stringify(companionId)} already has a live adapter in this host (${error instanceof Error ? error.message : String(error)}); reusing it`);
|
|
2402
|
+
}
|
|
3270
2403
|
}
|
|
2404
|
+
};
|
|
2405
|
+
let sweeping = false;
|
|
2406
|
+
let queued = false;
|
|
2407
|
+
const runSweep = () => {
|
|
2408
|
+
if (sweeping) {
|
|
2409
|
+
queued = true;
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
sweeping = true;
|
|
2413
|
+
sweep().finally(() => {
|
|
2414
|
+
sweeping = false;
|
|
2415
|
+
if (queued) {
|
|
2416
|
+
queued = false;
|
|
2417
|
+
runSweep();
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
};
|
|
2421
|
+
runSweep();
|
|
2422
|
+
if (shared.companionSweepSubscribed !== true) {
|
|
2423
|
+
shared.companionSweepSubscribed = true;
|
|
2424
|
+
ctx.on("llm/adapters-updated", () => {
|
|
2425
|
+
runSweep();
|
|
2426
|
+
});
|
|
3271
2427
|
}
|
|
3272
2428
|
}
|
|
3273
2429
|
const IMAGE_EXTENSIONS = {
|
|
@@ -3874,7 +3030,6 @@ async function applyPiPackage(ctx, options) {
|
|
|
3874
3030
|
toolRestrictions: /* @__PURE__ */ new WeakMap(),
|
|
3875
3031
|
commands: /* @__PURE__ */ new Map(),
|
|
3876
3032
|
commandDisposers: /* @__PURE__ */ new Map(),
|
|
3877
|
-
modelsJsonProviders: shared.modelsJsonProviders,
|
|
3878
3033
|
companionRoutes: shared.companionRoutes,
|
|
3879
3034
|
flags: /* @__PURE__ */ new Map(),
|
|
3880
3035
|
notifications: [],
|
|
@@ -3924,7 +3079,7 @@ async function applyPiPackage(ctx, options) {
|
|
|
3924
3079
|
}
|
|
3925
3080
|
const llm = llmOf(ctx);
|
|
3926
3081
|
state.modelCatalog = shared.modelCatalog ??= new ModelCatalog(llm);
|
|
3927
|
-
|
|
3082
|
+
registerVisionCompanions(ctx, options.config?.visionCompanions);
|
|
3928
3083
|
if (llm !== void 0) {
|
|
3929
3084
|
if (shared.catalogSubscribed !== true) {
|
|
3930
3085
|
shared.catalogSubscribed = true;
|
|
@@ -3971,6 +3126,6 @@ const runtimeInternals = {
|
|
|
3971
3126
|
textBlocks
|
|
3972
3127
|
};
|
|
3973
3128
|
//#endregion
|
|
3974
|
-
export { runtimeInternals as i,
|
|
3129
|
+
export { runtimeInternals as i, normalizeToolSchema as n, registerVisionCompanions as r, applyPiPackage as t };
|
|
3975
3130
|
|
|
3976
|
-
//# sourceMappingURL=runtime-
|
|
3131
|
+
//# sourceMappingURL=runtime-DMEJknhK.mjs.map
|