@ai-setting/roy-agent-core 1.7.55 → 1.7.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/index.js +7 -7
- package/dist/env/agent/agent-component.d.ts +30 -0
- package/dist/env/agent/index.js +13 -12
- package/dist/env/commands/index.js +7 -7
- package/dist/env/context/index.js +3 -3
- package/dist/env/debug/index.js +7 -7
- package/dist/env/environment.d.ts +7 -0
- package/dist/env/event-source/index.js +16 -16
- package/dist/env/hook/index.js +5 -5
- package/dist/env/index.js +40 -39
- package/dist/env/llm/adapter-ai-sdk.d.ts +6 -0
- package/dist/env/llm/adapter-pi-ai.d.ts +5 -0
- package/dist/env/llm/index.js +10 -10
- package/dist/env/llm/invoke.d.ts +33 -1
- package/dist/env/llm/llm.d.ts +13 -0
- package/dist/env/llm/outbound-defense.d.ts +140 -0
- package/dist/env/log-trace/decorator.d.ts +46 -3
- package/dist/env/log-trace/index.d.ts +1 -0
- package/dist/env/log-trace/index.js +33 -7
- package/dist/env/log-trace/trace-filters.d.ts +127 -0
- package/dist/env/mcp/index.js +7 -7
- package/dist/env/memory/index.js +7 -7
- package/dist/env/plugin/index.js +7 -7
- package/dist/env/prompt/index.js +7 -7
- package/dist/env/session/index.js +11 -10
- package/dist/env/session/session-component.d.ts +4 -0
- package/dist/env/session/storage/index.js +5 -3
- package/dist/env/skill/index.js +7 -7
- package/dist/env/task/delegate/index.js +7 -7
- package/dist/env/task/index.js +12 -12
- package/dist/env/task/plugins/index.js +9 -9
- package/dist/env/task/storage/index.js +2 -2
- package/dist/env/task/tools/index.js +3 -3
- package/dist/env/tool/built-in/index.js +3 -3
- package/dist/env/tool/index.js +9 -9
- package/dist/env/workflow/engine/index.js +12 -12
- package/dist/env/workflow/hooks/index.js +7 -7
- package/dist/env/workflow/index.js +23 -22
- package/dist/env/workflow/nodes/index.js +2 -2
- package/dist/env/workflow/plugins/index.js +8 -8
- package/dist/env/workflow/service/index.js +2 -2
- package/dist/env/workflow/storage/index.js +3 -3
- package/dist/env/workflow/tools/index.js +15 -14
- package/dist/env/workflow/utils/index.js +5 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +81 -55
- package/dist/shared/@ai-setting/{roy-agent-core-k25gv86p.js → roy-agent-core-0b3fdnn3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-6qxh4xrm.js → roy-agent-core-0bmk44ye.js} +6 -6
- package/dist/shared/@ai-setting/{roy-agent-core-k8b440n3.js → roy-agent-core-16t0zfbk.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-4z3rq5gw.js → roy-agent-core-1pnfkspm.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-a4vzsn0g.js → roy-agent-core-202v00x4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-j2nvedt6.js → roy-agent-core-2044fbsd.js} +9 -9
- package/dist/shared/@ai-setting/{roy-agent-core-fs36xgcs.js → roy-agent-core-3dkzqqsd.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qm9jda8p.js → roy-agent-core-3kz6934b.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-tjg71zqp.js → roy-agent-core-3mc6vmvj.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-tdbv7ff4.js → roy-agent-core-40xzbjtm.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-athdr2m0.js → roy-agent-core-4zy2v3br.js} +10 -2
- package/dist/shared/@ai-setting/{roy-agent-core-t37wbysj.js → roy-agent-core-5gw6w424.js} +4 -4
- package/dist/shared/@ai-setting/{roy-agent-core-978b6wfx.js → roy-agent-core-5psjwd08.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-7evvpfwc.js → roy-agent-core-5rxs0ra3.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-12d2xap8.js → roy-agent-core-6qgq7h1g.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-6167phd4.js → roy-agent-core-833t16n1.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-2k4xffaa.js → roy-agent-core-8cgtwytv.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-rx16h2m1.js → roy-agent-core-97eyghsp.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-fvs48d38.js → roy-agent-core-98aa9r0x.js} +12 -7
- package/dist/shared/@ai-setting/{roy-agent-core-ytr2w0vb.js → roy-agent-core-9nv586rw.js} +57 -19
- package/dist/shared/@ai-setting/roy-agent-core-b599jgyd.js +578 -0
- package/dist/shared/@ai-setting/{roy-agent-core-zvemdfx5.js → roy-agent-core-bwzy00t2.js} +12 -5
- package/dist/shared/@ai-setting/{roy-agent-core-dwx9hjwx.js → roy-agent-core-c6at31ra.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-1cv9j4dg.js → roy-agent-core-d14h726p.js} +3 -3
- package/dist/shared/@ai-setting/{roy-agent-core-cd62e9p1.js → roy-agent-core-d4jpn06f.js} +22 -9
- package/dist/shared/@ai-setting/{roy-agent-core-e2z1etk4.js → roy-agent-core-e0wbq0qq.js} +7 -7
- package/dist/shared/@ai-setting/{roy-agent-core-vrsxhw9e.js → roy-agent-core-e677am8e.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-g36jevrk.js → roy-agent-core-ezsyyjxw.js} +9 -9
- package/dist/shared/@ai-setting/{roy-agent-core-ne2t1pay.js → roy-agent-core-f8sp1txg.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-ak4dc3kg.js → roy-agent-core-gn8f4rhs.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qcspfszy.js → roy-agent-core-gngjkw3b.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9jzysq50.js → roy-agent-core-heft3j4c.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-5apmk4qw.js → roy-agent-core-jszgaevt.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-nde5gzta.js → roy-agent-core-m8n0dffe.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-2bscfrr1.js → roy-agent-core-n3b7ztms.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-8eqngasy.js → roy-agent-core-nhk0dsxr.js} +13 -13
- package/dist/shared/@ai-setting/{roy-agent-core-b5ew8t0k.js → roy-agent-core-nntphgww.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-8s9jd2qy.js → roy-agent-core-nra8vfrs.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-2b8jtv7m.js → roy-agent-core-ns3pbyz4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-7g03zkkb.js → roy-agent-core-pemxr5n4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-skzjs973.js → roy-agent-core-pjxa78mp.js} +5 -5
- package/dist/shared/@ai-setting/{roy-agent-core-w527pcpe.js → roy-agent-core-q7zksbw0.js} +44 -493
- package/dist/shared/@ai-setting/{roy-agent-core-zd3ddm4k.js → roy-agent-core-qjjf4tjb.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-qamktze3.js → roy-agent-core-qyqnmz8c.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9csa89jx.js → roy-agent-core-r9qe0ghe.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-7qy3nffr.js → roy-agent-core-tc7gw38m.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-aer5kw1j.js → roy-agent-core-v2188rnk.js} +16 -16
- package/dist/shared/@ai-setting/{roy-agent-core-mxdc756a.js → roy-agent-core-vmg30n5x.js} +7 -7
- package/dist/shared/@ai-setting/{roy-agent-core-rnwr39b0.js → roy-agent-core-w7hnjhvx.js} +5 -3
- package/dist/shared/@ai-setting/{roy-agent-core-dv714bqh.js → roy-agent-core-wafk25ff.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-9h72sk1n.js → roy-agent-core-xcq04fq0.js} +5 -5
- package/dist/shared/@ai-setting/{roy-agent-core-2gay3sjt.js → roy-agent-core-yg247mk4.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-0hq21px2.js → roy-agent-core-yv9x4wmg.js} +631 -35
- package/dist/shared/@ai-setting/{roy-agent-core-yhsssn86.js → roy-agent-core-zmeka63k.js} +1 -1
- package/package.json +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-dsevjbck.js +0 -245
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sniffImageMime
|
|
3
|
+
} from "./roy-agent-core-zjdzk1gg.js";
|
|
4
|
+
import {
|
|
5
|
+
TracedAs,
|
|
6
|
+
embedParamFilter,
|
|
7
|
+
embedResultFilter,
|
|
8
|
+
init_decorator,
|
|
9
|
+
init_trace_filters
|
|
10
|
+
} from "./roy-agent-core-b599jgyd.js";
|
|
1
11
|
import {
|
|
2
12
|
createLogger,
|
|
3
13
|
init_logger
|
|
4
14
|
} from "./roy-agent-core-8seq65bp.js";
|
|
5
15
|
import {
|
|
16
|
+
__legacyDecorateClassTS,
|
|
6
17
|
__require
|
|
7
18
|
} from "./roy-agent-core-fs0mn2jk.js";
|
|
8
19
|
|
|
@@ -59,13 +70,15 @@ function buildOpencodeHeaders(providerId, conversationId, baseURL) {
|
|
|
59
70
|
return { "x-opencode-session": getOrCreateOpencodeSessionId(conversationId) };
|
|
60
71
|
}
|
|
61
72
|
|
|
62
|
-
// src/env/llm/
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
import {
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
// src/env/llm/adapter-pi-ai.ts
|
|
74
|
+
init_decorator();
|
|
75
|
+
init_trace_filters();
|
|
76
|
+
init_logger();
|
|
77
|
+
import {
|
|
78
|
+
complete,
|
|
79
|
+
stream as piStream,
|
|
80
|
+
getModel
|
|
81
|
+
} from "@earendil-works/pi-ai";
|
|
69
82
|
|
|
70
83
|
// src/env/llm/tool-schema.ts
|
|
71
84
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
@@ -152,12 +165,479 @@ function toolParametersToJsonSchema(parameters) {
|
|
|
152
165
|
return deref;
|
|
153
166
|
}
|
|
154
167
|
|
|
168
|
+
// src/env/llm/reasoning-defense.ts
|
|
169
|
+
var DEEPSEEK_PROVIDER_PREFIX = "deepseek";
|
|
170
|
+
var DEEPSEEK_MODEL_MARKER = "deepseek";
|
|
171
|
+
function shouldInjectReasoningContent(provider, modelId) {
|
|
172
|
+
const p = (provider ?? "").toLowerCase();
|
|
173
|
+
const m = (modelId ?? "").toLowerCase();
|
|
174
|
+
if (p === DEEPSEEK_PROVIDER_PREFIX || p.startsWith(`${DEEPSEEK_PROVIDER_PREFIX}-`)) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
return m.includes(DEEPSEEK_MODEL_MARKER);
|
|
178
|
+
}
|
|
179
|
+
function injectReasoningContent(messages) {
|
|
180
|
+
return messages.map((msg) => {
|
|
181
|
+
if (msg?.role !== "assistant")
|
|
182
|
+
return msg;
|
|
183
|
+
if ("reasoning_content" in msg)
|
|
184
|
+
return msg;
|
|
185
|
+
return { ...msg, reasoning_content: "" };
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function applyReasoningDefense(payload, target) {
|
|
189
|
+
if (!payload || typeof payload !== "object")
|
|
190
|
+
return payload;
|
|
191
|
+
if (!shouldInjectReasoningContent(target.provider, target.modelId))
|
|
192
|
+
return payload;
|
|
193
|
+
const messages = payload.messages;
|
|
194
|
+
if (!Array.isArray(messages))
|
|
195
|
+
return payload;
|
|
196
|
+
return { ...payload, messages: injectReasoningContent(messages) };
|
|
197
|
+
}
|
|
198
|
+
function createReasoningDefenseHook(target) {
|
|
199
|
+
return (payload) => applyReasoningDefense(payload, target);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/env/llm/adapter-pi-ai.ts
|
|
203
|
+
var logger = createLogger("llm:adapter-pi-ai");
|
|
204
|
+
var KNOWN_PROVIDER_APIS = {
|
|
205
|
+
anthropic: {
|
|
206
|
+
api: "anthropic-messages",
|
|
207
|
+
apiProtocol: "anthropic-messages",
|
|
208
|
+
contextLimit: 200000,
|
|
209
|
+
maxTokensLimit: 8192
|
|
210
|
+
},
|
|
211
|
+
openai: {
|
|
212
|
+
api: "openai-responses",
|
|
213
|
+
apiProtocol: "openai-responses",
|
|
214
|
+
contextLimit: 128000,
|
|
215
|
+
maxTokensLimit: 16384
|
|
216
|
+
},
|
|
217
|
+
google: {
|
|
218
|
+
api: "google-generative-ai",
|
|
219
|
+
apiProtocol: "google-generative-ai",
|
|
220
|
+
contextLimit: 1e6,
|
|
221
|
+
maxTokensLimit: 8192
|
|
222
|
+
},
|
|
223
|
+
"google-vertex": {
|
|
224
|
+
api: "google-vertex",
|
|
225
|
+
apiProtocol: "google-vertex",
|
|
226
|
+
contextLimit: 1e6,
|
|
227
|
+
maxTokensLimit: 8192
|
|
228
|
+
},
|
|
229
|
+
deepseek: {
|
|
230
|
+
api: "openai-completions",
|
|
231
|
+
apiProtocol: "openai-completions",
|
|
232
|
+
contextLimit: 64000,
|
|
233
|
+
maxTokensLimit: 8192
|
|
234
|
+
},
|
|
235
|
+
glm: {
|
|
236
|
+
api: "openai-completions",
|
|
237
|
+
apiProtocol: "openai-completions",
|
|
238
|
+
contextLimit: 128000,
|
|
239
|
+
maxTokensLimit: 8192
|
|
240
|
+
},
|
|
241
|
+
xai: {
|
|
242
|
+
api: "openai-completions",
|
|
243
|
+
apiProtocol: "openai-completions",
|
|
244
|
+
contextLimit: 131072,
|
|
245
|
+
maxTokensLimit: 8192
|
|
246
|
+
},
|
|
247
|
+
groq: {
|
|
248
|
+
api: "openai-completions",
|
|
249
|
+
apiProtocol: "openai-completions",
|
|
250
|
+
contextLimit: 32768,
|
|
251
|
+
maxTokensLimit: 8192
|
|
252
|
+
},
|
|
253
|
+
minimax: {
|
|
254
|
+
api: "anthropic-messages",
|
|
255
|
+
apiProtocol: "anthropic-messages",
|
|
256
|
+
contextLimit: 128000,
|
|
257
|
+
maxTokensLimit: 8192
|
|
258
|
+
},
|
|
259
|
+
"minimax-cn": {
|
|
260
|
+
api: "anthropic-messages",
|
|
261
|
+
apiProtocol: "anthropic-messages",
|
|
262
|
+
contextLimit: 128000,
|
|
263
|
+
maxTokensLimit: 8192
|
|
264
|
+
},
|
|
265
|
+
moonshotai: {
|
|
266
|
+
api: "openai-completions",
|
|
267
|
+
apiProtocol: "openai-completions",
|
|
268
|
+
contextLimit: 128000,
|
|
269
|
+
maxTokensLimit: 8192
|
|
270
|
+
},
|
|
271
|
+
"kimi-coding": {
|
|
272
|
+
api: "openai-completions",
|
|
273
|
+
apiProtocol: "openai-completions",
|
|
274
|
+
contextLimit: 128000,
|
|
275
|
+
maxTokensLimit: 8192
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
var PROVIDER_ENDPOINT_APIS = {
|
|
279
|
+
minimax: [
|
|
280
|
+
{ pathIncludes: "/anthropic", apiProtocol: "anthropic-messages" },
|
|
281
|
+
{ pathIncludes: "/v1", apiProtocol: "openai-completions" }
|
|
282
|
+
],
|
|
283
|
+
"minimax-cn": [
|
|
284
|
+
{ pathIncludes: "/anthropic", apiProtocol: "anthropic-messages" },
|
|
285
|
+
{ pathIncludes: "/v1", apiProtocol: "openai-completions" }
|
|
286
|
+
]
|
|
287
|
+
};
|
|
288
|
+
var ENDPOINT_RULES_SORTED = Object.fromEntries(Object.entries(PROVIDER_ENDPOINT_APIS).map(([provider, rules]) => [
|
|
289
|
+
provider,
|
|
290
|
+
[...rules].sort((a, b) => b.pathIncludes.length - a.pathIncludes.length)
|
|
291
|
+
]));
|
|
292
|
+
var PROVIDER_API_OVERRIDES = {
|
|
293
|
+
"minimax-responses": "openai-responses",
|
|
294
|
+
"minimax-openai": "openai-completions"
|
|
295
|
+
};
|
|
296
|
+
function resolveApiProtocol(provider, baseURL, explicitProtocol) {
|
|
297
|
+
if (explicitProtocol)
|
|
298
|
+
return explicitProtocol;
|
|
299
|
+
if (PROVIDER_API_OVERRIDES[provider])
|
|
300
|
+
return PROVIDER_API_OVERRIDES[provider];
|
|
301
|
+
const endpointRules = ENDPOINT_RULES_SORTED[provider];
|
|
302
|
+
if (endpointRules) {
|
|
303
|
+
for (const rule of endpointRules) {
|
|
304
|
+
if (baseURL?.includes(rule.pathIncludes))
|
|
305
|
+
return rule.apiProtocol;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const declared = KNOWN_PROVIDER_APIS[provider]?.apiProtocol;
|
|
309
|
+
if (declared)
|
|
310
|
+
return declared;
|
|
311
|
+
if (baseURL?.includes("/responses"))
|
|
312
|
+
return "openai-responses";
|
|
313
|
+
if (baseURL?.includes("anthropic")) {
|
|
314
|
+
logger.warn(`[adapter-pi-ai] provider "${provider}" 的协议靠 baseURL 含 "anthropic" 推断为 anthropic-messages;` + `建议在 KNOWN_PROVIDER_APIS 或调用处显式声明 apiProtocol(Task #4383 P1)`, { provider, inferred_api_protocol: "anthropic-messages", base_url: baseURL });
|
|
315
|
+
return "anthropic-messages";
|
|
316
|
+
}
|
|
317
|
+
return KNOWN_PROVIDER_APIS[provider]?.api || "openai-completions";
|
|
318
|
+
}
|
|
319
|
+
var REASONING_COMPAT_PIN = {
|
|
320
|
+
supportsDeveloperRole: false,
|
|
321
|
+
thinkingFormat: "openai",
|
|
322
|
+
supportsReasoningEffort: false
|
|
323
|
+
};
|
|
324
|
+
var REASONING_SAFE_PROTOCOLS = new Set([
|
|
325
|
+
"openai-completions",
|
|
326
|
+
"anthropic-messages"
|
|
327
|
+
]);
|
|
328
|
+
var warnedReasoningRefusals = new Set;
|
|
329
|
+
function resolveModelReasoning(provider, modelId, capabilities, apiProtocol) {
|
|
330
|
+
const requested = capabilities?.reasoning === true;
|
|
331
|
+
if (!requested)
|
|
332
|
+
return false;
|
|
333
|
+
if (!apiProtocol || !REASONING_SAFE_PROTOCOLS.has(apiProtocol)) {
|
|
334
|
+
const key = `${provider}:${apiProtocol ?? "<unresolved>"}`;
|
|
335
|
+
if (!warnedReasoningRefusals.has(key)) {
|
|
336
|
+
warnedReasoningRefusals.add(key);
|
|
337
|
+
logger.warn(`[adapter-pi-ai] provider "${provider}" (${apiProtocol ?? "unresolved"}) 的 capabilities.reasoning=true 被忽略:` + `该协议无法通过 compat pin 保持请求体字节一致,打开会破坏 prompt cache 前缀(Task #4403)`, { provider, model_id: modelId, api_protocol: apiProtocol });
|
|
338
|
+
}
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
function buildPiCacheOptions(args) {
|
|
344
|
+
const out = {};
|
|
345
|
+
if (args.sessionId)
|
|
346
|
+
out.sessionId = args.sessionId;
|
|
347
|
+
if (args.apiProtocol === "openai-completions") {
|
|
348
|
+
out.cacheRetention = args.cacheRetention ?? "long";
|
|
349
|
+
} else if (args.cacheRetention) {
|
|
350
|
+
out.cacheRetention = args.cacheRetention;
|
|
351
|
+
}
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
function resolvePiModel(provider, modelId, baseURL, capabilities) {
|
|
355
|
+
const builtIn = getModel(provider, modelId);
|
|
356
|
+
if (builtIn)
|
|
357
|
+
return baseURL ? { ...builtIn, baseUrl: baseURL } : builtIn;
|
|
358
|
+
const configuredApi = resolveApiProtocol(provider, baseURL);
|
|
359
|
+
const reasoning = resolveModelReasoning(provider, modelId, capabilities, configuredApi);
|
|
360
|
+
return {
|
|
361
|
+
id: modelId,
|
|
362
|
+
name: modelId,
|
|
363
|
+
api: configuredApi,
|
|
364
|
+
provider,
|
|
365
|
+
baseUrl: baseURL || "",
|
|
366
|
+
reasoning,
|
|
367
|
+
...reasoning ? { compat: { ...REASONING_COMPAT_PIN } } : {},
|
|
368
|
+
input: ["text", "image"],
|
|
369
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
370
|
+
contextWindow: KNOWN_PROVIDER_APIS[provider]?.contextLimit || 128000,
|
|
371
|
+
maxTokens: KNOWN_PROVIDER_APIS[provider]?.maxTokensLimit || 8192
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function toPiMessages(messages) {
|
|
375
|
+
const out = [];
|
|
376
|
+
const ts = Date.now();
|
|
377
|
+
for (const m of messages) {
|
|
378
|
+
if (m.role === "system")
|
|
379
|
+
continue;
|
|
380
|
+
if (m.role === "user") {
|
|
381
|
+
if (typeof m.content === "string") {
|
|
382
|
+
out.push({ role: "user", content: m.content, timestamp: ts });
|
|
383
|
+
} else {
|
|
384
|
+
out.push({ role: "user", content: m.content.flatMap((c) => {
|
|
385
|
+
if (c.type === "text")
|
|
386
|
+
return [{ type: "text", text: c.text }];
|
|
387
|
+
if (c.type === "image")
|
|
388
|
+
return [{ type: "image", data: c.image instanceof Uint8Array ? Buffer.from(c.image).toString("base64") : c.image, mimeType: sniffImageMime(c.image, c.mediaType) }];
|
|
389
|
+
return [{ type: "text", text: `[file:${c.mediaType}] ${typeof c.file === "string" ? c.file : Buffer.from(c.file).toString("base64")}` }];
|
|
390
|
+
}), timestamp: ts });
|
|
391
|
+
}
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
if (m.role === "tool") {
|
|
395
|
+
out.push({ role: "toolResult", toolCallId: m.toolCallId || "", toolName: m.name || "", content: [{ type: "text", text: typeof m.content === "string" ? m.content : JSON.stringify(m.content) }], isError: false, timestamp: ts });
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const content = [];
|
|
399
|
+
if (typeof m.content === "string" && m.content)
|
|
400
|
+
content.push({ type: "text", text: m.content });
|
|
401
|
+
else if (Array.isArray(m.content))
|
|
402
|
+
for (const c of m.content) {
|
|
403
|
+
if (c.type === "text")
|
|
404
|
+
content.push({ type: "text", text: c.text });
|
|
405
|
+
else if (c.type === "image")
|
|
406
|
+
content.push({ type: "image", data: c.image instanceof Uint8Array ? Buffer.from(c.image).toString("base64") : c.image, mimeType: sniffImageMime(c.image, c.mediaType) });
|
|
407
|
+
else
|
|
408
|
+
content.push({ type: "text", text: `[file:${c.mediaType}] ${typeof c.file === "string" ? c.file : Buffer.from(c.file).toString("base64")}` });
|
|
409
|
+
}
|
|
410
|
+
for (const tc of m.toolCalls || [])
|
|
411
|
+
content.push({ type: "toolCall", id: tc.id, name: tc.name, arguments: safeJson(tc.arguments) });
|
|
412
|
+
out.push({ role: "assistant", content, api: "openai-completions", model: "", provider: "", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }, stopReason: "stop", timestamp: ts });
|
|
413
|
+
}
|
|
414
|
+
return out;
|
|
415
|
+
}
|
|
416
|
+
function extractSystemPrompt(messages) {
|
|
417
|
+
const prompts = messages.filter((m) => m.role === "system").map((m) => typeof m.content === "string" ? m.content : m.content.filter((c) => c.type === "text").map((c) => c.text).join(""));
|
|
418
|
+
return prompts.length ? prompts.join(`
|
|
419
|
+
|
|
420
|
+
`) : undefined;
|
|
421
|
+
}
|
|
422
|
+
function safeJson(s) {
|
|
423
|
+
try {
|
|
424
|
+
return JSON.parse(s);
|
|
425
|
+
} catch (e) {
|
|
426
|
+
logger.warn(`[safeJson] JSON.parse failed`, {
|
|
427
|
+
parse_failed: true,
|
|
428
|
+
input_length: s.length,
|
|
429
|
+
error: e.message
|
|
430
|
+
});
|
|
431
|
+
return {};
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function convertToolParameters(parameters) {
|
|
435
|
+
return toolParametersToJsonSchema(parameters);
|
|
436
|
+
}
|
|
437
|
+
function toPiTools(tools) {
|
|
438
|
+
if (!tools)
|
|
439
|
+
return;
|
|
440
|
+
return tools.map((t) => ({
|
|
441
|
+
name: t.name,
|
|
442
|
+
description: t.description || "",
|
|
443
|
+
parameters: convertToolParameters(t.parameters)
|
|
444
|
+
}));
|
|
445
|
+
}
|
|
446
|
+
function piRequestExtras(req) {
|
|
447
|
+
const headers = buildOpencodeHeaders(req.provider, req.sessionId, req.baseURL);
|
|
448
|
+
return Object.keys(headers).length > 0 ? { headers } : {};
|
|
449
|
+
}
|
|
450
|
+
function fromAssistantMessage(msg) {
|
|
451
|
+
let text = "";
|
|
452
|
+
const toolCalls = [];
|
|
453
|
+
for (const block of msg.content) {
|
|
454
|
+
const b = block;
|
|
455
|
+
if (b.type === "text") {
|
|
456
|
+
text += b.text || "";
|
|
457
|
+
} else if (b.type === "toolCall") {
|
|
458
|
+
toolCalls.push({
|
|
459
|
+
id: b.id || "",
|
|
460
|
+
name: b.name || "",
|
|
461
|
+
arguments: typeof b.arguments === "string" ? b.arguments : JSON.stringify(b.arguments ?? {})
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
text,
|
|
467
|
+
finishReason: msg.stopReason || "stop",
|
|
468
|
+
toolCalls: toolCalls.length ? toolCalls : undefined,
|
|
469
|
+
usage: {
|
|
470
|
+
promptTokens: msg.usage?.input ?? 0,
|
|
471
|
+
completionTokens: msg.usage?.output ?? 0,
|
|
472
|
+
totalTokens: msg.usage?.totalTokens ?? 0,
|
|
473
|
+
cacheRead: msg.usage?.cacheRead,
|
|
474
|
+
cacheWrite: msg.usage?.cacheWrite
|
|
475
|
+
},
|
|
476
|
+
raw: msg
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
class EmbeddingNotSupportedError extends Error {
|
|
481
|
+
constructor(adapter) {
|
|
482
|
+
super(`Embedding is not supported by ${adapter}; use AiSdkAdapter.embed() or apply at higher layer.`);
|
|
483
|
+
this.name = "EmbeddingNotSupportedError";
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
class PiAiAdapter {
|
|
488
|
+
name = "pi-ai";
|
|
489
|
+
async chat(req) {
|
|
490
|
+
const model = resolvePiModel(req.provider, req.modelId, req.baseURL, req.capabilities);
|
|
491
|
+
const ctx = {
|
|
492
|
+
systemPrompt: [req.systemPrompt, extractSystemPrompt(req.messages)].filter(Boolean).join(`
|
|
493
|
+
|
|
494
|
+
`) || undefined,
|
|
495
|
+
messages: toPiMessages(req.messages),
|
|
496
|
+
tools: toPiTools(req.tools)
|
|
497
|
+
};
|
|
498
|
+
const msg = await complete(model, ctx, {
|
|
499
|
+
apiKey: req.apiKey,
|
|
500
|
+
baseURL: req.baseURL,
|
|
501
|
+
temperature: req.temperature,
|
|
502
|
+
maxTokens: req.maxTokens,
|
|
503
|
+
signal: req.signal,
|
|
504
|
+
...buildPiCacheOptions({
|
|
505
|
+
apiProtocol: model.api,
|
|
506
|
+
sessionId: req.sessionId,
|
|
507
|
+
cacheRetention: req.cacheRetention
|
|
508
|
+
}),
|
|
509
|
+
onPayload: createReasoningDefenseHook({
|
|
510
|
+
provider: req.provider,
|
|
511
|
+
modelId: req.modelId
|
|
512
|
+
}),
|
|
513
|
+
...piRequestExtras(req)
|
|
514
|
+
});
|
|
515
|
+
return fromAssistantMessage(msg);
|
|
516
|
+
}
|
|
517
|
+
async stream(req) {
|
|
518
|
+
const model = resolvePiModel(req.provider, req.modelId, req.baseURL, req.capabilities);
|
|
519
|
+
const ctx = {
|
|
520
|
+
systemPrompt: [req.systemPrompt, extractSystemPrompt(req.messages)].filter(Boolean).join(`
|
|
521
|
+
|
|
522
|
+
`) || undefined,
|
|
523
|
+
messages: toPiMessages(req.messages),
|
|
524
|
+
tools: toPiTools(req.tools)
|
|
525
|
+
};
|
|
526
|
+
const eventStream = piStream(model, ctx, {
|
|
527
|
+
apiKey: req.apiKey,
|
|
528
|
+
baseURL: req.baseURL,
|
|
529
|
+
temperature: req.temperature,
|
|
530
|
+
maxTokens: req.maxTokens,
|
|
531
|
+
signal: req.signal,
|
|
532
|
+
...buildPiCacheOptions({
|
|
533
|
+
apiProtocol: model.api,
|
|
534
|
+
sessionId: req.sessionId,
|
|
535
|
+
cacheRetention: req.cacheRetention
|
|
536
|
+
}),
|
|
537
|
+
onPayload: createReasoningDefenseHook({
|
|
538
|
+
provider: req.provider,
|
|
539
|
+
modelId: req.modelId
|
|
540
|
+
}),
|
|
541
|
+
...piRequestExtras(req)
|
|
542
|
+
});
|
|
543
|
+
return { events: eventStream, result: async () => fromAssistantMessage(await eventStream.result()) };
|
|
544
|
+
}
|
|
545
|
+
async embed(_req) {
|
|
546
|
+
throw new EmbeddingNotSupportedError(this.name);
|
|
547
|
+
}
|
|
548
|
+
async resolveProvider(provider, modelId) {
|
|
549
|
+
const model = resolvePiModel(provider, modelId);
|
|
550
|
+
const caps = KNOWN_PROVIDER_APIS[provider] || {
|
|
551
|
+
api: model.api,
|
|
552
|
+
contextLimit: model.contextWindow,
|
|
553
|
+
maxTokensLimit: model.maxTokens
|
|
554
|
+
};
|
|
555
|
+
return {
|
|
556
|
+
api: model.api,
|
|
557
|
+
provider,
|
|
558
|
+
modelId,
|
|
559
|
+
contextLimit: caps.contextLimit,
|
|
560
|
+
maxTokensLimit: caps.maxTokensLimit
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
__legacyDecorateClassTS([
|
|
565
|
+
TracedAs("llm.adapter.chat", {
|
|
566
|
+
recordParams: true,
|
|
567
|
+
recordResult: true,
|
|
568
|
+
log: true,
|
|
569
|
+
paramFilter: (args) => {
|
|
570
|
+
const req = args[0];
|
|
571
|
+
return {
|
|
572
|
+
provider: req?.provider,
|
|
573
|
+
model_id: req?.modelId,
|
|
574
|
+
messages_count: req?.messages?.length,
|
|
575
|
+
tools_count: req?.tools?.length,
|
|
576
|
+
temperature: req?.temperature,
|
|
577
|
+
max_tokens: req?.maxTokens
|
|
578
|
+
};
|
|
579
|
+
},
|
|
580
|
+
resultFilter: (r) => ({
|
|
581
|
+
finish_reason: r?.finishReason,
|
|
582
|
+
usage_input: r?.usage?.promptTokens,
|
|
583
|
+
usage_output: r?.usage?.completionTokens,
|
|
584
|
+
usage_total: r?.usage?.totalTokens,
|
|
585
|
+
content_length: r?.text?.length
|
|
586
|
+
})
|
|
587
|
+
})
|
|
588
|
+
], PiAiAdapter.prototype, "chat", null);
|
|
589
|
+
__legacyDecorateClassTS([
|
|
590
|
+
TracedAs("llm.adapter.stream", {
|
|
591
|
+
recordParams: true,
|
|
592
|
+
recordResult: false,
|
|
593
|
+
log: true,
|
|
594
|
+
paramFilter: (args) => {
|
|
595
|
+
const req = args[0];
|
|
596
|
+
return {
|
|
597
|
+
provider: req?.provider,
|
|
598
|
+
model_id: req?.modelId,
|
|
599
|
+
messages_count: req?.messages?.length
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
})
|
|
603
|
+
], PiAiAdapter.prototype, "stream", null);
|
|
604
|
+
__legacyDecorateClassTS([
|
|
605
|
+
TracedAs("llm.adapter.embed", {
|
|
606
|
+
paramFilter: embedParamFilter,
|
|
607
|
+
resultFilter: embedResultFilter,
|
|
608
|
+
recordResult: false,
|
|
609
|
+
log: true
|
|
610
|
+
})
|
|
611
|
+
], PiAiAdapter.prototype, "embed", null);
|
|
612
|
+
__legacyDecorateClassTS([
|
|
613
|
+
TracedAs("llm.adapter.resolve_provider", {
|
|
614
|
+
recordParams: true,
|
|
615
|
+
recordResult: true,
|
|
616
|
+
log: true,
|
|
617
|
+
paramFilter: (args) => ({
|
|
618
|
+
provider: args[0],
|
|
619
|
+
model_id: args[1]
|
|
620
|
+
}),
|
|
621
|
+
resultFilter: (r) => ({
|
|
622
|
+
api: r?.api,
|
|
623
|
+
context_limit: r?.contextLimit,
|
|
624
|
+
max_tokens_limit: r?.maxTokensLimit
|
|
625
|
+
})
|
|
626
|
+
})
|
|
627
|
+
], PiAiAdapter.prototype, "resolveProvider", null);
|
|
628
|
+
|
|
155
629
|
// src/env/llm/invoke.ts
|
|
630
|
+
import { randomUUID } from "crypto";
|
|
631
|
+
import { streamText, jsonSchema } from "ai";
|
|
632
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
633
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
634
|
+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
635
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
156
636
|
init_logger();
|
|
157
637
|
|
|
158
638
|
// src/env/llm/tool-args-parser.ts
|
|
159
639
|
init_logger();
|
|
160
|
-
var
|
|
640
|
+
var logger2 = createLogger("llm:tool-args-parser");
|
|
161
641
|
var ARG_KEY_RE = /<arg_key>([\s\S]*?)<\/arg_key>\s*<arg_value>([\s\S]*?)<\/arg_value>/g;
|
|
162
642
|
function salvageTextualToolArgs(raw) {
|
|
163
643
|
if (!raw.includes("<arg_key>"))
|
|
@@ -201,7 +681,7 @@ function parseToolArgsStrict(args) {
|
|
|
201
681
|
const direct = tryParseObject(raw);
|
|
202
682
|
if (direct.ok) {
|
|
203
683
|
if (raw.includes("<arg_key>") || raw.includes("<arg_value>") || raw.includes("<tool_call>")) {
|
|
204
|
-
|
|
684
|
+
logger2.warn("[parseToolArgsStrict] arguments 里出现上游文本工具调用标记,已清洗值中的 marker");
|
|
205
685
|
return { ok: true, args: sanitizeArgValues(direct.args), salvaged: true };
|
|
206
686
|
}
|
|
207
687
|
return { ok: true, args: direct.args };
|
|
@@ -210,12 +690,12 @@ function parseToolArgsStrict(args) {
|
|
|
210
690
|
const stripped = stripTextualToolArgsMarkers(raw);
|
|
211
691
|
const afterStrip = tryParseObject(stripped);
|
|
212
692
|
if (afterStrip.ok) {
|
|
213
|
-
|
|
693
|
+
logger2.warn("[parseToolArgsStrict] 上游把文本工具调用标记混进了 arguments,已剥离 marker 后恢复");
|
|
214
694
|
return { ok: true, args: afterStrip.args, salvaged: true };
|
|
215
695
|
}
|
|
216
696
|
const salvaged = salvageTextualToolArgs(raw);
|
|
217
697
|
if (salvaged) {
|
|
218
|
-
|
|
698
|
+
logger2.warn("[parseToolArgsStrict] 上游输出的是文本工具调用格式,已按 <arg_key>/<arg_value> 重建参数", { keys: Object.keys(salvaged).join(",") });
|
|
219
699
|
return { ok: true, args: salvaged, salvaged: true };
|
|
220
700
|
}
|
|
221
701
|
}
|
|
@@ -269,33 +749,33 @@ function tryParseObject(raw) {
|
|
|
269
749
|
var MAX_SAFE_DEPTH = 5;
|
|
270
750
|
function safeParseToolArgs(args, depth = 0) {
|
|
271
751
|
if (depth >= MAX_SAFE_DEPTH) {
|
|
272
|
-
|
|
752
|
+
logger2.warn(`[safeParseToolArgs] Max recursion depth (${MAX_SAFE_DEPTH}) exceeded at depth ${depth}. ` + "Input may be malicious or abnormally deeply nested. Returning empty object.", { depth, argsType: typeof args });
|
|
273
753
|
return {};
|
|
274
754
|
}
|
|
275
755
|
if (typeof args === "object" && args !== null && !Array.isArray(args)) {
|
|
276
756
|
return args;
|
|
277
757
|
}
|
|
278
758
|
if (Array.isArray(args)) {
|
|
279
|
-
|
|
759
|
+
logger2.debug(`[safeParseToolArgs] Input is an array. Tool arguments should be objects. Returning empty object.`, { depth, arrayLength: args.length });
|
|
280
760
|
return {};
|
|
281
761
|
}
|
|
282
762
|
if (typeof args === "string") {
|
|
283
763
|
try {
|
|
284
764
|
const parsed = JSON.parse(args);
|
|
285
765
|
if (typeof parsed === "string") {
|
|
286
|
-
|
|
766
|
+
logger2.debug(`[safeParseToolArgs] Detected double/multi-stringified JSON at depth ${depth}. ` + "LLM may be double-stringifying tool arguments. Parsing recursively.", { depth, parsedLength: parsed.length });
|
|
287
767
|
return safeParseToolArgs(parsed, depth + 1);
|
|
288
768
|
}
|
|
289
769
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
290
770
|
return parsed;
|
|
291
771
|
}
|
|
292
772
|
if (Array.isArray(parsed)) {
|
|
293
|
-
|
|
773
|
+
logger2.debug(`[safeParseToolArgs] Parsed result is an array at depth ${depth}. ` + "Tool arguments should be objects, not arrays. Returning empty object.", { depth, parsedLength: parsed.length });
|
|
294
774
|
return {};
|
|
295
775
|
}
|
|
296
776
|
return { value: parsed };
|
|
297
777
|
} catch (e) {
|
|
298
|
-
|
|
778
|
+
logger2.warn(`[safeParseToolArgs] JSON.parse failed at depth ${depth}`, {
|
|
299
779
|
parse_failed: true,
|
|
300
780
|
input_length: args.length,
|
|
301
781
|
depth,
|
|
@@ -366,8 +846,98 @@ function canonicalizeToolCallArgsInPlace(value) {
|
|
|
366
846
|
}
|
|
367
847
|
}
|
|
368
848
|
}
|
|
849
|
+
|
|
850
|
+
// src/env/llm/outbound-defense.ts
|
|
851
|
+
var CONVERSATION_LIST_KEYS = ["messages", "input"];
|
|
852
|
+
function findConversationList(body, wireProtocol) {
|
|
853
|
+
if (wireProtocol === "openai-responses") {
|
|
854
|
+
return Array.isArray(body.input) ? { key: "input", list: body.input } : null;
|
|
855
|
+
}
|
|
856
|
+
if (wireProtocol === "openai-chat" || wireProtocol === "anthropic") {
|
|
857
|
+
return Array.isArray(body.messages) ? { key: "messages", list: body.messages } : null;
|
|
858
|
+
}
|
|
859
|
+
for (const key of CONVERSATION_LIST_KEYS) {
|
|
860
|
+
if (Array.isArray(body[key])) {
|
|
861
|
+
return { key, list: body[key] };
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
function protocolSupportsCacheKeys(wireProtocol) {
|
|
867
|
+
return wireProtocol === undefined || wireProtocol === "openai-chat" || wireProtocol === "openai-responses";
|
|
868
|
+
}
|
|
869
|
+
function protocolSupportsReasoningField(wireProtocol) {
|
|
870
|
+
return wireProtocol === undefined || wireProtocol === "openai-chat" || wireProtocol === "openai-responses";
|
|
871
|
+
}
|
|
872
|
+
function applyOutboundDefense(body, target) {
|
|
873
|
+
let next = null;
|
|
874
|
+
const writable = () => {
|
|
875
|
+
if (!next)
|
|
876
|
+
next = { ...body };
|
|
877
|
+
return next;
|
|
878
|
+
};
|
|
879
|
+
if (protocolSupportsReasoningField(target.wireProtocol) && shouldInjectReasoningContent(target.provider, target.modelId)) {
|
|
880
|
+
const found = findConversationList(body, target.wireProtocol);
|
|
881
|
+
if (found) {
|
|
882
|
+
const injected = injectReasoningContent(found.list);
|
|
883
|
+
const changed = injected.some((m, i) => m !== found.list[i]);
|
|
884
|
+
if (changed)
|
|
885
|
+
writable()[found.key] = injected;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (protocolSupportsCacheKeys(target.wireProtocol) && target.sessionId) {
|
|
889
|
+
const retention = target.cacheRetention ?? "long";
|
|
890
|
+
if (retention !== "none") {
|
|
891
|
+
if (body.prompt_cache_key === undefined) {
|
|
892
|
+
writable().prompt_cache_key = clampOpenAIPromptCacheKey(target.sessionId);
|
|
893
|
+
}
|
|
894
|
+
if (retention === "long" && body.prompt_cache_retention === undefined) {
|
|
895
|
+
writable().prompt_cache_retention = "24h";
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
return next ?? body;
|
|
900
|
+
}
|
|
901
|
+
function createOutboundDefenseFetch(target, inner = (...args) => globalThis.fetch(...args)) {
|
|
902
|
+
return async (input, init) => {
|
|
903
|
+
if (!init || typeof init.body !== "string" || init.body.length === 0) {
|
|
904
|
+
return inner(input, init);
|
|
905
|
+
}
|
|
906
|
+
let parsed;
|
|
907
|
+
try {
|
|
908
|
+
parsed = JSON.parse(init.body);
|
|
909
|
+
} catch {
|
|
910
|
+
return inner(input, init);
|
|
911
|
+
}
|
|
912
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
913
|
+
return inner(input, init);
|
|
914
|
+
}
|
|
915
|
+
let patched;
|
|
916
|
+
try {
|
|
917
|
+
patched = applyOutboundDefense(parsed, target);
|
|
918
|
+
} catch {
|
|
919
|
+
return inner(input, init);
|
|
920
|
+
}
|
|
921
|
+
if (patched === parsed) {
|
|
922
|
+
return inner(input, init);
|
|
923
|
+
}
|
|
924
|
+
return inner(input, { ...init, body: JSON.stringify(patched) });
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
function wireProtocolFromApiProtocol(apiProtocol) {
|
|
928
|
+
switch (apiProtocol) {
|
|
929
|
+
case "openai-responses":
|
|
930
|
+
return "openai-responses";
|
|
931
|
+
case "anthropic-messages":
|
|
932
|
+
return "anthropic";
|
|
933
|
+
case "openai-completions":
|
|
934
|
+
return "openai-chat";
|
|
935
|
+
default:
|
|
936
|
+
return "openai-chat";
|
|
937
|
+
}
|
|
938
|
+
}
|
|
369
939
|
// src/env/llm/invoke.ts
|
|
370
|
-
var
|
|
940
|
+
var logger3 = createLogger("llm:invoke");
|
|
371
941
|
var MAX_RETRIES = 3;
|
|
372
942
|
var DEFAULT_TIMEOUT = 60000;
|
|
373
943
|
|
|
@@ -587,7 +1157,7 @@ function convertToSDKMessages(messages, options) {
|
|
|
587
1157
|
}
|
|
588
1158
|
if (msg.role === "tool") {
|
|
589
1159
|
if (!msg.toolCallId) {
|
|
590
|
-
|
|
1160
|
+
logger3.warn("Tool message missing toolCallId", { msg });
|
|
591
1161
|
}
|
|
592
1162
|
let outputValue = "";
|
|
593
1163
|
const content = msg.content;
|
|
@@ -675,7 +1245,7 @@ function convertToolChoice(toolChoice) {
|
|
|
675
1245
|
}
|
|
676
1246
|
function createEnvEventEmitter(env, context) {
|
|
677
1247
|
if (!env || typeof env.pushEnvEvent !== "function") {
|
|
678
|
-
|
|
1248
|
+
logger3.warn("[invoke] createEnvEventEmitter: env is null/undefined or pushEnvEvent is unavailable, streaming events will be disabled");
|
|
679
1249
|
return null;
|
|
680
1250
|
}
|
|
681
1251
|
return (event) => {
|
|
@@ -693,7 +1263,7 @@ function createEnvEventEmitter(env, context) {
|
|
|
693
1263
|
payload: event
|
|
694
1264
|
});
|
|
695
1265
|
} catch (err) {
|
|
696
|
-
|
|
1266
|
+
logger3.warn("Failed to push env event", { error: String(err), eventType: event.type });
|
|
697
1267
|
}
|
|
698
1268
|
};
|
|
699
1269
|
}
|
|
@@ -776,12 +1346,12 @@ function processThinkingStream(textDelta, config, state) {
|
|
|
776
1346
|
async function invoke(config, options, ctx) {
|
|
777
1347
|
try {
|
|
778
1348
|
const emitToEnv = createEnvEventEmitter(options.env, options.context);
|
|
779
|
-
|
|
1349
|
+
logger3.info("[invoke] Created event emitter", {
|
|
780
1350
|
envAvailable: !!options.env,
|
|
781
1351
|
hasContext: options.context !== undefined
|
|
782
1352
|
});
|
|
783
1353
|
const { providerId, modelId } = parseModelString(options.model || config.model);
|
|
784
|
-
const provider = createProviderInstance(providerId, config.apiKey, config.baseURL, options.context?.sessionId);
|
|
1354
|
+
const provider = createProviderInstance(providerId, config.apiKey, config.baseURL, options.context?.sessionId, options.cacheRetention, modelId);
|
|
785
1355
|
if (!provider) {
|
|
786
1356
|
throw new Error(`Failed to create provider for ${providerId}`);
|
|
787
1357
|
}
|
|
@@ -815,7 +1385,7 @@ async function invoke(config, options, ctx) {
|
|
|
815
1385
|
const missingToolResults = assistantToolCalls.filter((tc) => !toolResultIds.includes(tc.id));
|
|
816
1386
|
const orphanedToolResults = toolResultIds.filter((id) => !toolCallIds.has(id));
|
|
817
1387
|
if (assistantToolCalls.length > 0 || toolResultIds.length > 0) {
|
|
818
|
-
|
|
1388
|
+
logger3.debug("[invoke] ToolCallId matching analysis:", {
|
|
819
1389
|
assistantToolCalls,
|
|
820
1390
|
toolResultIds,
|
|
821
1391
|
missingToolResults: missingToolResults.length > 0 ? missingToolResults : "none",
|
|
@@ -829,10 +1399,10 @@ async function invoke(config, options, ctx) {
|
|
|
829
1399
|
});
|
|
830
1400
|
const model = `${providerId}/${modelId}`;
|
|
831
1401
|
if (emitToEnv) {
|
|
832
|
-
|
|
1402
|
+
logger3.info("[invoke] Emitting llm.start event");
|
|
833
1403
|
emitToEnv({ type: "start", metadata: { model } });
|
|
834
1404
|
} else {
|
|
835
|
-
|
|
1405
|
+
logger3.warn("[invoke] emitToEnv is null, skipping llm.start event");
|
|
836
1406
|
}
|
|
837
1407
|
const streamTextOptions = {
|
|
838
1408
|
model: provider.languageModel(modelId),
|
|
@@ -954,7 +1524,7 @@ async function invoke(config, options, ctx) {
|
|
|
954
1524
|
`).filter(Boolean);
|
|
955
1525
|
const uniqueLines = new Set(outputLines);
|
|
956
1526
|
if (outputLines.length !== uniqueLines.size) {
|
|
957
|
-
|
|
1527
|
+
logger3.warn("[DEBUG_DUPLICATE] outputContent has duplicate lines", {
|
|
958
1528
|
fullContent,
|
|
959
1529
|
rawFullContent,
|
|
960
1530
|
outputContent,
|
|
@@ -962,7 +1532,7 @@ async function invoke(config, options, ctx) {
|
|
|
962
1532
|
textDeltas: textDeltaLog
|
|
963
1533
|
});
|
|
964
1534
|
} else {
|
|
965
|
-
|
|
1535
|
+
logger3.info("[DEBUG_DUPLICATE] outputContent OK", {
|
|
966
1536
|
charCount: outputContent.length,
|
|
967
1537
|
lineCount: outputLines.length,
|
|
968
1538
|
uniqueLineCount: uniqueLines.size,
|
|
@@ -1000,7 +1570,7 @@ async function invoke(config, options, ctx) {
|
|
|
1000
1570
|
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
1001
1571
|
const rawStack = error instanceof Error ? error.stack : undefined;
|
|
1002
1572
|
const safeMessage = redactSecrets(rawMessage, [config.apiKey]);
|
|
1003
|
-
|
|
1573
|
+
logger3.error("Error during invocation", {
|
|
1004
1574
|
error: safeMessage,
|
|
1005
1575
|
stack: rawStack ? redactSecrets(rawStack, [config.apiKey]) : undefined
|
|
1006
1576
|
});
|
|
@@ -1023,18 +1593,37 @@ function redactSecrets(text, knownSecrets = []) {
|
|
|
1023
1593
|
out = out.replace(/\bBearer\s+[A-Za-z0-9._\-+/=]{12,}\b/g, "<redacted>");
|
|
1024
1594
|
return out;
|
|
1025
1595
|
}
|
|
1026
|
-
function
|
|
1596
|
+
function resolveDefenseProtocol(providerId, baseURL) {
|
|
1597
|
+
if (providerId === "openai")
|
|
1598
|
+
return "openai-responses";
|
|
1599
|
+
return wireProtocolFromApiProtocol(resolveApiProtocol(providerId, baseURL));
|
|
1600
|
+
}
|
|
1601
|
+
function createProviderInstance(providerId, apiKey, baseURL, conversationId, cacheRetention, modelId) {
|
|
1027
1602
|
try {
|
|
1028
1603
|
if (providerId === "openai") {
|
|
1029
1604
|
const openai = createOpenAI({
|
|
1030
1605
|
apiKey,
|
|
1031
|
-
baseURL
|
|
1606
|
+
baseURL,
|
|
1607
|
+
fetch: createOutboundDefenseFetch({
|
|
1608
|
+
provider: providerId,
|
|
1609
|
+
modelId,
|
|
1610
|
+
sessionId: conversationId,
|
|
1611
|
+
cacheRetention,
|
|
1612
|
+
wireProtocol: resolveDefenseProtocol(providerId, baseURL)
|
|
1613
|
+
})
|
|
1032
1614
|
});
|
|
1033
1615
|
return openai;
|
|
1034
1616
|
}
|
|
1035
1617
|
if (providerId === "anthropic") {
|
|
1036
1618
|
const anthropic = createAnthropic({
|
|
1037
|
-
apiKey
|
|
1619
|
+
apiKey,
|
|
1620
|
+
fetch: createOutboundDefenseFetch({
|
|
1621
|
+
provider: providerId,
|
|
1622
|
+
modelId,
|
|
1623
|
+
sessionId: conversationId,
|
|
1624
|
+
cacheRetention,
|
|
1625
|
+
wireProtocol: resolveDefenseProtocol(providerId, baseURL)
|
|
1626
|
+
})
|
|
1038
1627
|
});
|
|
1039
1628
|
return anthropic;
|
|
1040
1629
|
}
|
|
@@ -1050,17 +1639,24 @@ function createProviderInstance(providerId, apiKey, baseURL, conversationId) {
|
|
|
1050
1639
|
apiKey,
|
|
1051
1640
|
baseURL: baseURL || "",
|
|
1052
1641
|
includeUsage: true,
|
|
1053
|
-
headers: providerHeaders
|
|
1642
|
+
headers: providerHeaders,
|
|
1643
|
+
fetch: createOutboundDefenseFetch({
|
|
1644
|
+
provider: providerId,
|
|
1645
|
+
modelId,
|
|
1646
|
+
sessionId: conversationId,
|
|
1647
|
+
cacheRetention,
|
|
1648
|
+
wireProtocol: resolveDefenseProtocol(providerId, baseURL)
|
|
1649
|
+
})
|
|
1054
1650
|
});
|
|
1055
1651
|
return defaultProvider;
|
|
1056
1652
|
} catch (error) {
|
|
1057
|
-
|
|
1653
|
+
logger3.error(`Failed to create provider ${providerId}`, { error: String(error) });
|
|
1058
1654
|
return null;
|
|
1059
1655
|
}
|
|
1060
1656
|
}
|
|
1061
1657
|
async function invokeNonStream(config, options, ctx) {
|
|
1062
1658
|
const { providerId, modelId } = parseModelString(options.model || config.model);
|
|
1063
|
-
const provider = createProviderInstance(providerId, config.apiKey, config.baseURL, options.context?.sessionId);
|
|
1659
|
+
const provider = createProviderInstance(providerId, config.apiKey, config.baseURL, options.context?.sessionId, options.cacheRetention, modelId);
|
|
1064
1660
|
if (!provider) {
|
|
1065
1661
|
throw new Error(`Failed to create provider for ${providerId}`);
|
|
1066
1662
|
}
|
|
@@ -1126,4 +1722,4 @@ function createInvokeConfig(model, apiKey, baseURL) {
|
|
|
1126
1722
|
};
|
|
1127
1723
|
}
|
|
1128
1724
|
|
|
1129
|
-
export {
|
|
1725
|
+
export { parseToolArgsStrict, safeParseToolArgs, generateOpencodeSessionId, isOpencodeProvider, isOpencodeEndpoint, getOrCreateOpencodeSessionId, buildOpencodeHeaders, canonicalizeParts, PiAiAdapter, withTimeout, parseModelString, convertToSDKMessages, invoke, invokeNonStream, createInvokeConfig };
|