dominds 1.13.2 → 1.15.2
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/bootstrap/global-dialog-event-broadcaster.d.ts +18 -0
- package/dist/bootstrap/global-dialog-event-broadcaster.js +81 -0
- package/dist/dialog-fork.js +13 -12
- package/dist/dialog.d.ts +61 -50
- package/dist/dialog.js +284 -78
- package/dist/docs/dialog-system.md +12 -0
- package/dist/docs/dialog-system.zh.md +12 -0
- package/dist/docs/dominds-terminology.md +17 -0
- package/dist/docs/issues/global-dialog-event-broadcaster-missing.md +128 -0
- package/dist/docs/llm-provider-isolation.md +35 -0
- package/dist/docs/llm-provider-isolation.zh.md +35 -0
- package/dist/llm/client.d.ts +2 -1
- package/dist/llm/defaults.yaml +118 -4
- package/dist/llm/gen/anthropic.js +2 -4
- package/dist/llm/gen/codex.d.ts +11 -0
- package/dist/llm/gen/codex.js +41 -31
- package/dist/llm/gen/failure-classifier.js +17 -0
- package/dist/llm/gen/mock.js +45 -21
- package/dist/llm/gen/openai-compatible.d.ts +2 -0
- package/dist/llm/gen/openai-compatible.js +43 -38
- package/dist/llm/gen/openai.d.ts +3 -1
- package/dist/llm/gen/openai.js +888 -71
- package/dist/llm/gen/tool-call-context.d.ts +7 -2
- package/dist/llm/gen/tool-call-context.js +55 -13
- package/dist/llm/gen.d.ts +60 -3
- package/dist/llm/kernel-driver/context.js +1 -1
- package/dist/llm/kernel-driver/drive.js +374 -348
- package/dist/llm/kernel-driver/flow.js +3 -3
- package/dist/llm/kernel-driver/guardrails.d.ts +1 -1
- package/dist/llm/kernel-driver/guardrails.js +4 -4
- package/dist/llm/kernel-driver/runtime.js +11 -29
- package/dist/llm/kernel-driver/subdialog.js +56 -5
- package/dist/llm/kernel-driver/tellask-special.d.ts +38 -12
- package/dist/llm/kernel-driver/tellask-special.js +489 -180
- package/dist/llm/kernel-driver/types.d.ts +1 -1
- package/dist/persistence.d.ts +30 -62
- package/dist/persistence.js +978 -986
- package/dist/priming.js +398 -365
- package/dist/recovery/reply-special.js +3 -3
- package/dist/runtime/inter-dialog-format.d.ts +1 -1
- package/dist/runtime/inter-dialog-format.js +1 -1
- package/dist/runtime/reply-prompt-copy.js +4 -4
- package/dist/server/setup-routes.js +26 -5
- package/dist/server/snippets-routes.d.ts +1 -0
- package/dist/server/snippets-routes.js +20 -9
- package/dist/server/websocket-handler.js +58 -25
- package/dist/shared/utils/fbr.js +12 -8
- package/dist/shared/utils/inter-dialog-format.js +6 -4
- package/dist/team.d.ts +24 -13
- package/dist/team.js +123 -32
- package/dist/tool.d.ts +26 -0
- package/dist/tool.js +97 -0
- package/dist/tools/team_mgmt.js +18 -0
- package/package.json +2 -2
- package/webapp/dist/assets/{_basePickBy-CBOtd63g.js → _basePickBy-DsirmCgI.js} +3 -3
- package/webapp/dist/assets/_basePickBy-DsirmCgI.js.map +1 -0
- package/webapp/dist/assets/{_baseUniq-mfoKz4Wm.js → _baseUniq-tR6G8loB.js} +2 -2
- package/webapp/dist/assets/_baseUniq-tR6G8loB.js.map +1 -0
- package/webapp/dist/assets/{arc-Dq0WZLyu.js → arc-CzxpASkZ.js} +2 -2
- package/webapp/dist/assets/arc-CzxpASkZ.js.map +1 -0
- package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-CNmygmp3.js → architectureDiagram-2XIMDMQ5-BSH7H5oI.js} +26 -8
- package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-BSH7H5oI.js.map +1 -0
- package/webapp/dist/assets/{blockDiagram-VD42YOAC-DvE0lybt.js → blockDiagram-WCTKOSBZ-DpLIr7yO.js} +187 -170
- package/webapp/dist/assets/blockDiagram-WCTKOSBZ-DpLIr7yO.js.map +1 -0
- package/webapp/dist/assets/{c4Diagram-YG6GDRKO-CR7zJ2_u.js → c4Diagram-IC4MRINW-WuYKgWfY.js} +4 -4
- package/webapp/dist/assets/c4Diagram-IC4MRINW-WuYKgWfY.js.map +1 -0
- package/webapp/dist/assets/{channel-DrTrnYx4.js → channel-B-v9dqLN.js} +2 -2
- package/webapp/dist/assets/channel-B-v9dqLN.js.map +1 -0
- package/webapp/dist/assets/{chunk-4BX2VUAB-CVuJEIeN.js → chunk-4BX2VUAB-MtFUfKZy.js} +2 -2
- package/webapp/dist/assets/chunk-4BX2VUAB-MtFUfKZy.js.map +1 -0
- package/webapp/dist/assets/{chunk-55IACEB6-BxUoXApB.js → chunk-55IACEB6-rY9AJdzj.js} +2 -2
- package/webapp/dist/assets/chunk-55IACEB6-rY9AJdzj.js.map +1 -0
- package/webapp/dist/assets/{chunk-FMBD7UC4-TX-LVAaV.js → chunk-FMBD7UC4-B-RtOs7e.js} +2 -2
- package/webapp/dist/assets/chunk-FMBD7UC4-B-RtOs7e.js.map +1 -0
- package/webapp/dist/assets/{chunk-TZMSLE5B-Cw689yRl.js → chunk-JSJVCQXG-Da1d3uS4.js} +14 -6
- package/webapp/dist/assets/chunk-JSJVCQXG-Da1d3uS4.js.map +1 -0
- package/webapp/dist/assets/{chunk-QN33PNHL-D1uiKlOO.js → chunk-KX2RTZJC-DH9UrpuG.js} +2 -2
- package/webapp/dist/assets/chunk-KX2RTZJC-DH9UrpuG.js.map +1 -0
- package/webapp/dist/assets/{chunk-DI55MBZ5-SAhxUTqQ.js → chunk-NQ4KR5QH-CK365lrr.js} +9 -7
- package/webapp/dist/assets/chunk-NQ4KR5QH-CK365lrr.js.map +1 -0
- package/webapp/dist/assets/{chunk-QZHKN3VN-BxuV0Oba.js → chunk-QZHKN3VN-BCaWPGDm.js} +2 -2
- package/webapp/dist/assets/chunk-QZHKN3VN-BCaWPGDm.js.map +1 -0
- package/webapp/dist/assets/{chunk-B4BG7PRW-DpMa3-9L.js → chunk-WL4C6EOR-DDCnEwft.js} +171 -121
- package/webapp/dist/assets/chunk-WL4C6EOR-DDCnEwft.js.map +1 -0
- package/webapp/dist/assets/{classDiagram-2ON5EDUG-BTTGianr.js → classDiagram-VBA2DB6C-CvMBU4WA.js} +7 -6
- package/webapp/dist/assets/classDiagram-VBA2DB6C-CvMBU4WA.js.map +1 -0
- package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-BTTGianr.js → classDiagram-v2-RAHNMMFH-CvMBU4WA.js} +7 -6
- package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-CvMBU4WA.js.map +1 -0
- package/webapp/dist/assets/{clone-Dk8cAI3I.js → clone-r98jR0MC.js} +2 -2
- package/webapp/dist/assets/clone-r98jR0MC.js.map +1 -0
- package/webapp/dist/assets/{cose-bilkent-S5V4N54A-BjJnzB2N.js → cose-bilkent-S5V4N54A-t6J60Ogk.js} +2 -2
- package/webapp/dist/assets/cose-bilkent-S5V4N54A-t6J60Ogk.js.map +1 -0
- package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
- package/webapp/dist/assets/{dagre-6UL2VRFP-VF-xGhAf.js → dagre-KLK3FWXG-BlqmY2DV.js} +7 -7
- package/webapp/dist/assets/dagre-KLK3FWXG-BlqmY2DV.js.map +1 -0
- package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
- package/webapp/dist/assets/{diagram-PSM6KHXK-Ba5U0oRY.js → diagram-E7M64L7V-FwCHeIUD.js} +10 -10
- package/webapp/dist/assets/diagram-E7M64L7V-FwCHeIUD.js.map +1 -0
- package/webapp/dist/assets/{diagram-QEK2KX5R-DoYCnEw_.js → diagram-IFDJBPK2-NhtmkuZG.js} +9 -8
- package/webapp/dist/assets/diagram-IFDJBPK2-NhtmkuZG.js.map +1 -0
- package/webapp/dist/assets/{diagram-S2PKOQOG-CkK4SRyE.js → diagram-P4PSJMXO-B9FcmokX.js} +8 -8
- package/webapp/dist/assets/diagram-P4PSJMXO-B9FcmokX.js.map +1 -0
- package/webapp/dist/assets/{erDiagram-Q2GNP2WA-DkI5eYww.js → erDiagram-INFDFZHY-DHKmWvtB.js} +96 -75
- package/webapp/dist/assets/erDiagram-INFDFZHY-DHKmWvtB.js.map +1 -0
- package/webapp/dist/assets/{flowDiagram-NV44I4VS-wOdPUQ7Y.js → flowDiagram-PKNHOUZH-C7Zi8I7T.js} +98 -81
- package/webapp/dist/assets/flowDiagram-PKNHOUZH-C7Zi8I7T.js.map +1 -0
- package/webapp/dist/assets/{ganttDiagram-JELNMOA3-BtRWgkUH.js → ganttDiagram-A5KZAMGK-Cv2T8tz_.js} +28 -3
- package/webapp/dist/assets/ganttDiagram-A5KZAMGK-Cv2T8tz_.js.map +1 -0
- package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js → gitGraphDiagram-K3NZZRJ6-DztaipJU.js} +38 -46
- package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-DztaipJU.js.map +1 -0
- package/webapp/dist/assets/graph-C5yf62Vs.js +782 -0
- package/webapp/dist/assets/graph-C5yf62Vs.js.map +1 -0
- package/webapp/dist/assets/{index-xvYYeHuy.css → index-YaxF76or.css} +1 -1
- package/webapp/dist/assets/{index-rYmIohM_.js → index-hve5MWPs.js} +1603 -1415
- package/webapp/dist/assets/index-hve5MWPs.js.map +1 -0
- package/webapp/dist/assets/{infoDiagram-HS3SLOUP-BMaxCvH5.js → infoDiagram-LFFYTUFH-VgsbBPZP.js} +7 -7
- package/webapp/dist/assets/infoDiagram-LFFYTUFH-VgsbBPZP.js.map +1 -0
- package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
- package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js +966 -0
- package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js.map +1 -0
- package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-ejyerzmG.js → journeyDiagram-4ABVD52K-OO8sev-Y.js} +5 -5
- package/webapp/dist/assets/journeyDiagram-4ABVD52K-OO8sev-Y.js.map +1 -0
- package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-CYj35TEs.js → kanban-definition-K7BYSVSG-DiYCC1Ig.js} +5 -3
- package/webapp/dist/assets/kanban-definition-K7BYSVSG-DiYCC1Ig.js.map +1 -0
- package/webapp/dist/assets/{layout-7Ql4zmuL.js → layout-DdZSgGdu.js} +5 -5
- package/webapp/dist/assets/layout-DdZSgGdu.js.map +1 -0
- package/webapp/dist/assets/{linear-CVmgVPuZ.js → linear-7-aHtaFi.js} +2 -2
- package/webapp/dist/assets/linear-7-aHtaFi.js.map +1 -0
- package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-DOpxjGVo.js → mindmap-definition-YRQLILUH-IG3I-RdD.js} +7 -5
- package/webapp/dist/assets/mindmap-definition-YRQLILUH-IG3I-RdD.js.map +1 -0
- package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
- package/webapp/dist/assets/{pieDiagram-ADFJNKIX-CLQjpmAG.js → pieDiagram-SKSYHLDU-z68KJT5r.js} +8 -8
- package/webapp/dist/assets/pieDiagram-SKSYHLDU-z68KJT5r.js.map +1 -0
- package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-ClD_bz7z.js → quadrantDiagram-337W2JSQ-DaENWdO6.js} +3 -3
- package/webapp/dist/assets/quadrantDiagram-337W2JSQ-DaENWdO6.js.map +1 -0
- package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-DOpb-TWH.js → requirementDiagram-Z7DCOOCP-ROTFv4sa.js} +16 -6
- package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-ROTFv4sa.js.map +1 -0
- package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-D8Hsj3yx.js → sankeyDiagram-WA2Y5GQK-CK7qtpzw.js} +2 -2
- package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CK7qtpzw.js.map +1 -0
- package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-CFMNjBER.js → sequenceDiagram-2WXFIKYE-R5lDySeI.js} +601 -201
- package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-R5lDySeI.js.map +1 -0
- package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-BQeDlw0P.js → stateDiagram-RAJIS63D-sr7msF5U.js} +9 -9
- package/webapp/dist/assets/stateDiagram-RAJIS63D-sr7msF5U.js.map +1 -0
- package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-DscX61Rs.js → stateDiagram-v2-FVOUBMTO-X663liwS.js} +5 -5
- package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-X663liwS.js.map +1 -0
- package/webapp/dist/assets/{timeline-definition-IT6M3QCI-BcXPSTiw.js → timeline-definition-YZTLITO2-Bw0TdG26.js} +3 -3
- package/webapp/dist/assets/timeline-definition-YZTLITO2-Bw0TdG26.js.map +1 -0
- package/webapp/dist/assets/{treemap-GDKQZRPO-BBr4UV0Z.js → treemap-KZPCXAKY-D_sjKwI7.js} +37 -24
- package/webapp/dist/assets/treemap-KZPCXAKY-D_sjKwI7.js.map +1 -0
- package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js +2487 -0
- package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js.map +1 -0
- package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-CS5RAtQE.js → xychartDiagram-JWTSCODW-C65ESjTc.js} +4 -4
- package/webapp/dist/assets/xychartDiagram-JWTSCODW-C65ESjTc.js.map +1 -0
- package/webapp/dist/index.html +2 -2
- package/webapp/dist/assets/_basePickBy-CBOtd63g.js.map +0 -1
- package/webapp/dist/assets/_baseUniq-mfoKz4Wm.js.map +0 -1
- package/webapp/dist/assets/arc-Dq0WZLyu.js.map +0 -1
- package/webapp/dist/assets/architectureDiagram-VXUJARFQ-CNmygmp3.js.map +0 -1
- package/webapp/dist/assets/blockDiagram-VD42YOAC-DvE0lybt.js.map +0 -1
- package/webapp/dist/assets/c4Diagram-YG6GDRKO-CR7zJ2_u.js.map +0 -1
- package/webapp/dist/assets/channel-DrTrnYx4.js.map +0 -1
- package/webapp/dist/assets/chunk-4BX2VUAB-CVuJEIeN.js.map +0 -1
- package/webapp/dist/assets/chunk-55IACEB6-BxUoXApB.js.map +0 -1
- package/webapp/dist/assets/chunk-B4BG7PRW-DpMa3-9L.js.map +0 -1
- package/webapp/dist/assets/chunk-DI55MBZ5-SAhxUTqQ.js.map +0 -1
- package/webapp/dist/assets/chunk-FMBD7UC4-TX-LVAaV.js.map +0 -1
- package/webapp/dist/assets/chunk-QN33PNHL-D1uiKlOO.js.map +0 -1
- package/webapp/dist/assets/chunk-QZHKN3VN-BxuV0Oba.js.map +0 -1
- package/webapp/dist/assets/chunk-TZMSLE5B-Cw689yRl.js.map +0 -1
- package/webapp/dist/assets/classDiagram-2ON5EDUG-BTTGianr.js.map +0 -1
- package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-BTTGianr.js.map +0 -1
- package/webapp/dist/assets/clone-Dk8cAI3I.js.map +0 -1
- package/webapp/dist/assets/cose-bilkent-S5V4N54A-BjJnzB2N.js.map +0 -1
- package/webapp/dist/assets/dagre-6UL2VRFP-VF-xGhAf.js.map +0 -1
- package/webapp/dist/assets/diagram-PSM6KHXK-Ba5U0oRY.js.map +0 -1
- package/webapp/dist/assets/diagram-QEK2KX5R-DoYCnEw_.js.map +0 -1
- package/webapp/dist/assets/diagram-S2PKOQOG-CkK4SRyE.js.map +0 -1
- package/webapp/dist/assets/erDiagram-Q2GNP2WA-DkI5eYww.js.map +0 -1
- package/webapp/dist/assets/flowDiagram-NV44I4VS-wOdPUQ7Y.js.map +0 -1
- package/webapp/dist/assets/ganttDiagram-JELNMOA3-BtRWgkUH.js.map +0 -1
- package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js.map +0 -1
- package/webapp/dist/assets/graph-DAMkuTbn.js +0 -425
- package/webapp/dist/assets/graph-DAMkuTbn.js.map +0 -1
- package/webapp/dist/assets/index-rYmIohM_.js.map +0 -1
- package/webapp/dist/assets/infoDiagram-HS3SLOUP-BMaxCvH5.js.map +0 -1
- package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-ejyerzmG.js.map +0 -1
- package/webapp/dist/assets/kanban-definition-3W4ZIXB7-CYj35TEs.js.map +0 -1
- package/webapp/dist/assets/layout-7Ql4zmuL.js.map +0 -1
- package/webapp/dist/assets/linear-CVmgVPuZ.js.map +0 -1
- package/webapp/dist/assets/mindmap-definition-VGOIOE7T-DOpxjGVo.js.map +0 -1
- package/webapp/dist/assets/pieDiagram-ADFJNKIX-CLQjpmAG.js.map +0 -1
- package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-ClD_bz7z.js.map +0 -1
- package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-DOpb-TWH.js.map +0 -1
- package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-D8Hsj3yx.js.map +0 -1
- package/webapp/dist/assets/sequenceDiagram-WL72ISMW-CFMNjBER.js.map +0 -1
- package/webapp/dist/assets/stateDiagram-FKZM4ZOC-BQeDlw0P.js.map +0 -1
- package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-DscX61Rs.js.map +0 -1
- package/webapp/dist/assets/timeline-definition-IT6M3QCI-BcXPSTiw.js.map +0 -1
- package/webapp/dist/assets/treemap-GDKQZRPO-BBr4UV0Z.js.map +0 -1
- package/webapp/dist/assets/xychartDiagram-PRI3JC2R-CS5RAtQE.js.map +0 -1
package/dist/llm/gen/openai.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Module: llm/gen/openai
|
|
4
4
|
*
|
|
5
|
-
* OpenAI
|
|
5
|
+
* OpenAI Responses API integration implementing streaming and batch generation.
|
|
6
|
+
* Isolation principle: this wrapper owns OpenAI Responses request/stream semantics and must not
|
|
7
|
+
* inherit Codex-specific abstractions or parameter aliases.
|
|
6
8
|
*/
|
|
7
9
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
10
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -19,6 +21,19 @@ const failure_classifier_1 = require("./failure-classifier");
|
|
|
19
21
|
const tool_call_context_1 = require("./tool-call-context");
|
|
20
22
|
const tool_output_limit_1 = require("./tool-output-limit");
|
|
21
23
|
const log = (0, log_1.createLogger)('llm/openai');
|
|
24
|
+
const OPENAI_API_QUIRK_VENDOR_HEARTBEAT_EVENT_TYPES = {
|
|
25
|
+
'xcode.best': ['keepalive'],
|
|
26
|
+
};
|
|
27
|
+
const OPENAI_NATIVE_TOOL_ITEM_TYPES = new Set([
|
|
28
|
+
'file_search_call',
|
|
29
|
+
'code_interpreter_call',
|
|
30
|
+
'image_generation_call',
|
|
31
|
+
'mcp_call',
|
|
32
|
+
'mcp_list_tools',
|
|
33
|
+
'mcp_approval_request',
|
|
34
|
+
'custom_tool_call',
|
|
35
|
+
]);
|
|
36
|
+
const OPENAI_MALFORMED_BATCH_OUTPUT_ITEM_ERROR_CODE = 'OPENAI_MALFORMED_BATCH_OUTPUT_ITEM';
|
|
22
37
|
function limitOpenAiToolOutputText(text, msg, limitChars) {
|
|
23
38
|
const limited = (0, tool_output_limit_1.truncateProviderToolOutputText)(text, limitChars);
|
|
24
39
|
if (limited.truncated) {
|
|
@@ -65,6 +80,59 @@ function limitOpenAiToolOutputItems(output, msg, limitChars) {
|
|
|
65
80
|
function isRecord(value) {
|
|
66
81
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
67
82
|
}
|
|
83
|
+
function readEventType(value) {
|
|
84
|
+
if (!isRecord(value) || typeof value.type !== 'string') {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
return value.type;
|
|
88
|
+
}
|
|
89
|
+
function normalizeProviderApiQuirks(providerConfig) {
|
|
90
|
+
const raw = providerConfig.apiQuirks;
|
|
91
|
+
if (typeof raw === 'string') {
|
|
92
|
+
return raw.trim().length > 0 ? new Set([raw.trim()]) : new Set();
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(raw)) {
|
|
95
|
+
return new Set(raw.filter((entry) => typeof entry === 'string' && entry.trim() !== ''));
|
|
96
|
+
}
|
|
97
|
+
return new Set();
|
|
98
|
+
}
|
|
99
|
+
function resolveOpenAiVendorHeartbeatEventTypes(providerConfig) {
|
|
100
|
+
const eventTypes = new Set();
|
|
101
|
+
for (const quirk of normalizeProviderApiQuirks(providerConfig)) {
|
|
102
|
+
const vendorEventTypes = OPENAI_API_QUIRK_VENDOR_HEARTBEAT_EVENT_TYPES[quirk];
|
|
103
|
+
if (!vendorEventTypes)
|
|
104
|
+
continue;
|
|
105
|
+
for (const eventType of vendorEventTypes) {
|
|
106
|
+
eventTypes.add(eventType);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return eventTypes;
|
|
110
|
+
}
|
|
111
|
+
function maybeAnnotateOpenAiQuirkFailure(providerConfig, error) {
|
|
112
|
+
const quirks = normalizeProviderApiQuirks(providerConfig);
|
|
113
|
+
const message = error instanceof Error
|
|
114
|
+
? error.message
|
|
115
|
+
: typeof error === 'string'
|
|
116
|
+
? error
|
|
117
|
+
: isRecord(error) && typeof error.message === 'string'
|
|
118
|
+
? error.message
|
|
119
|
+
: '';
|
|
120
|
+
const lowerMessage = message.toLowerCase();
|
|
121
|
+
if (quirks.has('xcode.best') &&
|
|
122
|
+
lowerMessage.includes('stream error:') &&
|
|
123
|
+
lowerMessage.includes('internal_error') &&
|
|
124
|
+
lowerMessage.includes('received from peer')) {
|
|
125
|
+
if (error instanceof Error) {
|
|
126
|
+
const out = error;
|
|
127
|
+
out.code = 'XCODE_BEST_STREAM_INTERNAL_ERROR';
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
const out = new Error(message.length > 0 ? message : 'xcode.best stream internal error');
|
|
131
|
+
out.code = 'XCODE_BEST_STREAM_INTERNAL_ERROR';
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
return error;
|
|
135
|
+
}
|
|
68
136
|
function tryExtractApiReturnedModel(value) {
|
|
69
137
|
// NOTE: External API payload; a runtime check is unavoidable.
|
|
70
138
|
if (!isRecord(value))
|
|
@@ -116,7 +184,6 @@ function chatMessageToOpenAiInputItem(msg) {
|
|
|
116
184
|
};
|
|
117
185
|
case 'transient_guide_msg':
|
|
118
186
|
case 'saying_msg':
|
|
119
|
-
case 'ui_only_markdown_msg':
|
|
120
187
|
return {
|
|
121
188
|
type: 'message',
|
|
122
189
|
role: 'assistant',
|
|
@@ -125,7 +192,7 @@ function chatMessageToOpenAiInputItem(msg) {
|
|
|
125
192
|
case 'thinking_msg':
|
|
126
193
|
return thinkingMessageToOpenAiReasoningItem(msg);
|
|
127
194
|
case 'tellask_result_msg':
|
|
128
|
-
case '
|
|
195
|
+
case 'tellask_carryover_msg':
|
|
129
196
|
return {
|
|
130
197
|
type: 'message',
|
|
131
198
|
role: 'user',
|
|
@@ -157,24 +224,15 @@ function buildReasoningPayloadFromText(text) {
|
|
|
157
224
|
summary: [{ type: 'summary_text', text }],
|
|
158
225
|
};
|
|
159
226
|
}
|
|
160
|
-
function buildReasoningItemId(genseq, text) {
|
|
161
|
-
let hash = 0;
|
|
162
|
-
for (let i = 0; i < text.length; i += 1) {
|
|
163
|
-
hash = (hash * 31 + text.charCodeAt(i)) >>> 0;
|
|
164
|
-
}
|
|
165
|
-
return `dominds_reasoning_${genseq}_${hash.toString(16)}`;
|
|
166
|
-
}
|
|
167
227
|
function thinkingMessageToOpenAiReasoningItem(msg) {
|
|
168
228
|
const reasoning = msg.reasoning ?? buildReasoningPayloadFromText(msg.content);
|
|
169
229
|
if (!reasoning) {
|
|
170
|
-
return {
|
|
171
|
-
id: buildReasoningItemId(msg.genseq, msg.content),
|
|
172
|
-
type: 'reasoning',
|
|
173
|
-
summary: [],
|
|
174
|
-
};
|
|
230
|
+
return { type: 'reasoning', summary: [] };
|
|
175
231
|
}
|
|
232
|
+
// Some Responses-compatible gateways reject client-fabricated reasoning IDs and only accept
|
|
233
|
+
// provider-issued IDs (for example `rs_*`). We therefore omit `id` when replaying persisted
|
|
234
|
+
// reasoning and rely on encrypted_content when available.
|
|
176
235
|
const out = {
|
|
177
|
-
id: buildReasoningItemId(msg.genseq, `${msg.content}\n${reasoning.encrypted_content ?? ''}`),
|
|
178
236
|
type: 'reasoning',
|
|
179
237
|
summary: reasoning.summary.map((part) => ({ type: 'summary_text', text: part.text })),
|
|
180
238
|
};
|
|
@@ -271,6 +329,11 @@ function mergeAdjacentOpenAiMessages(input) {
|
|
|
271
329
|
}
|
|
272
330
|
return merged;
|
|
273
331
|
}
|
|
332
|
+
function shouldIncludeOpenAiEncryptedReasoning(input, reasoning) {
|
|
333
|
+
if (reasoning !== undefined)
|
|
334
|
+
return true;
|
|
335
|
+
return input.some((item) => isRecord(item) && item.type === 'reasoning');
|
|
336
|
+
}
|
|
274
337
|
async function buildOpenAiRequestInput(context, providerConfig) {
|
|
275
338
|
const normalized = (0, tool_call_context_1.normalizeToolCallPairs)(context);
|
|
276
339
|
const violation = (0, tool_call_context_1.findFirstToolCallAdjacencyViolation)(normalized);
|
|
@@ -308,13 +371,40 @@ function parseOpenAiUsage(usage) {
|
|
|
308
371
|
totalTokens: typeof totalTokens === 'number' ? totalTokens : inputTokens + outputTokens,
|
|
309
372
|
};
|
|
310
373
|
}
|
|
311
|
-
function buildOpenAiTextConfig(openAiParams
|
|
374
|
+
function buildOpenAiTextConfig(openAiParams) {
|
|
312
375
|
const textConfig = {};
|
|
313
376
|
if (openAiParams.verbosity !== undefined) {
|
|
314
377
|
textConfig.verbosity = openAiParams.verbosity;
|
|
315
378
|
}
|
|
316
|
-
|
|
317
|
-
|
|
379
|
+
const textFormat = openAiParams.text_format;
|
|
380
|
+
if (textFormat === 'text' || textFormat === 'json_object') {
|
|
381
|
+
textConfig.format = { type: textFormat };
|
|
382
|
+
}
|
|
383
|
+
else if (textFormat === 'json_schema') {
|
|
384
|
+
const schemaName = openAiParams.text_format_json_schema_name?.trim();
|
|
385
|
+
const rawSchema = openAiParams.text_format_json_schema?.trim();
|
|
386
|
+
if (!schemaName || !rawSchema) {
|
|
387
|
+
throw new Error('Invalid openai text_format=json_schema: text_format_json_schema_name and text_format_json_schema are required.');
|
|
388
|
+
}
|
|
389
|
+
let parsedSchema;
|
|
390
|
+
try {
|
|
391
|
+
parsedSchema = JSON.parse(rawSchema);
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
395
|
+
throw new Error(`Invalid openai text_format_json_schema: ${message}`);
|
|
396
|
+
}
|
|
397
|
+
if (!isRecord(parsedSchema)) {
|
|
398
|
+
throw new Error('Invalid openai text_format_json_schema: expected a JSON object at the top level.');
|
|
399
|
+
}
|
|
400
|
+
textConfig.format = {
|
|
401
|
+
type: 'json_schema',
|
|
402
|
+
name: schemaName,
|
|
403
|
+
schema: parsedSchema,
|
|
404
|
+
...(openAiParams.text_format_json_schema_strict !== undefined
|
|
405
|
+
? { strict: openAiParams.text_format_json_schema_strict }
|
|
406
|
+
: {}),
|
|
407
|
+
};
|
|
318
408
|
}
|
|
319
409
|
return textConfig.verbosity !== undefined || textConfig.format !== undefined
|
|
320
410
|
? textConfig
|
|
@@ -332,10 +422,389 @@ function buildOpenAiReasoning(openAiParams) {
|
|
|
332
422
|
...(summary !== undefined ? { summary } : {}),
|
|
333
423
|
};
|
|
334
424
|
}
|
|
335
|
-
function
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
425
|
+
function buildOpenAiNativeTools(openAiParams) {
|
|
426
|
+
const enabled = openAiParams.web_search_tool === true ||
|
|
427
|
+
openAiParams.web_search_context_size !== undefined ||
|
|
428
|
+
(Array.isArray(openAiParams.web_search_allowed_domains) &&
|
|
429
|
+
openAiParams.web_search_allowed_domains.length > 0);
|
|
430
|
+
if (!enabled)
|
|
431
|
+
return [];
|
|
432
|
+
const webSearchTool = {
|
|
433
|
+
type: 'web_search',
|
|
434
|
+
...(openAiParams.web_search_context_size !== undefined
|
|
435
|
+
? { search_context_size: openAiParams.web_search_context_size }
|
|
436
|
+
: {}),
|
|
437
|
+
...(Array.isArray(openAiParams.web_search_allowed_domains) &&
|
|
438
|
+
openAiParams.web_search_allowed_domains.length > 0
|
|
439
|
+
? { filters: { allowed_domains: openAiParams.web_search_allowed_domains } }
|
|
440
|
+
: {}),
|
|
441
|
+
};
|
|
442
|
+
return [webSearchTool];
|
|
443
|
+
}
|
|
444
|
+
function buildOpenAiInclude(input, reasoning, openAiParams) {
|
|
445
|
+
const include = new Set();
|
|
446
|
+
if (shouldIncludeOpenAiEncryptedReasoning(input, reasoning)) {
|
|
447
|
+
include.add('reasoning.encrypted_content');
|
|
448
|
+
}
|
|
449
|
+
if (openAiParams.web_search_include_sources === true) {
|
|
450
|
+
include.add('web_search_call.action.sources');
|
|
451
|
+
}
|
|
452
|
+
return include.size > 0 ? Array.from(include) : undefined;
|
|
453
|
+
}
|
|
454
|
+
function toOpenAiLlmWebSearchCall(item, itemId, phase) {
|
|
455
|
+
const rawAction = isRecord(item.action) ? item.action : null;
|
|
456
|
+
let action;
|
|
457
|
+
if (rawAction?.type === 'search') {
|
|
458
|
+
const queries = Array.isArray(rawAction.queries) &&
|
|
459
|
+
rawAction.queries.every((entry) => typeof entry === 'string')
|
|
460
|
+
? rawAction.queries
|
|
461
|
+
: undefined;
|
|
462
|
+
action = {
|
|
463
|
+
type: 'search',
|
|
464
|
+
...(typeof rawAction.query === 'string' ? { query: rawAction.query } : {}),
|
|
465
|
+
...(queries && queries.length > 0 ? { queries } : {}),
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
else if (rawAction?.type === 'open_page') {
|
|
469
|
+
action = {
|
|
470
|
+
type: 'open_page',
|
|
471
|
+
...(typeof rawAction.url === 'string' ? { url: rawAction.url } : {}),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
else if (rawAction?.type === 'find_in_page') {
|
|
475
|
+
action = {
|
|
476
|
+
type: 'find_in_page',
|
|
477
|
+
...(typeof rawAction.url === 'string' ? { url: rawAction.url } : {}),
|
|
478
|
+
...(typeof rawAction.pattern === 'string' ? { pattern: rawAction.pattern } : {}),
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
action = undefined;
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
source: 'openai_responses',
|
|
486
|
+
phase,
|
|
487
|
+
itemId,
|
|
488
|
+
...(typeof item.status === 'string' ? { status: item.status } : {}),
|
|
489
|
+
...(action !== undefined ? { action } : {}),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function buildOpenAiWebSearchProgressCall(itemId, phase, status, action) {
|
|
493
|
+
return {
|
|
494
|
+
source: 'openai_responses',
|
|
495
|
+
phase,
|
|
496
|
+
itemId,
|
|
497
|
+
status,
|
|
498
|
+
...(action !== undefined ? { action } : {}),
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function throwOpenAiMalformedBatchOutputItem(itemType, detail) {
|
|
502
|
+
const message = `OPENAI malformed batch output item ${itemType}: ${detail}`;
|
|
503
|
+
const error = new Error(message);
|
|
504
|
+
error.code = OPENAI_MALFORMED_BATCH_OUTPUT_ITEM_ERROR_CODE;
|
|
505
|
+
log.error(message, error);
|
|
506
|
+
throw error;
|
|
507
|
+
}
|
|
508
|
+
async function throwOpenAiMalformedStreamEvent(receiver, eventType, detail) {
|
|
509
|
+
const message = `OPENAI malformed stream event ${eventType}: ${detail}`;
|
|
510
|
+
log.error(message, new Error('openai_malformed_stream_event'));
|
|
511
|
+
if (receiver.streamError) {
|
|
512
|
+
await receiver.streamError(message);
|
|
513
|
+
}
|
|
514
|
+
throw new Error(message);
|
|
515
|
+
}
|
|
516
|
+
function buildOpenAiNativeToolCallFromState(state, phase) {
|
|
517
|
+
if (state.itemType === 'custom_tool_call') {
|
|
518
|
+
return {
|
|
519
|
+
source: 'openai_responses',
|
|
520
|
+
itemType: state.itemType,
|
|
521
|
+
phase,
|
|
522
|
+
callId: state.callId,
|
|
523
|
+
...(state.itemId !== undefined ? { itemId: state.itemId } : {}),
|
|
524
|
+
...(state.status !== undefined ? { status: state.status } : {}),
|
|
525
|
+
...(state.title !== undefined ? { title: state.title } : {}),
|
|
526
|
+
...(state.summary !== undefined ? { summary: state.summary } : {}),
|
|
527
|
+
...(state.detail !== undefined ? { detail: state.detail } : {}),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
source: 'openai_responses',
|
|
532
|
+
itemType: state.itemType,
|
|
533
|
+
phase,
|
|
534
|
+
itemId: state.itemId,
|
|
535
|
+
...(state.status !== undefined ? { status: state.status } : {}),
|
|
536
|
+
...(state.title !== undefined ? { title: state.title } : {}),
|
|
537
|
+
...(state.summary !== undefined ? { summary: state.summary } : {}),
|
|
538
|
+
...(state.detail !== undefined ? { detail: state.detail } : {}),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function mergeOpenAiNativeToolSeedIntoState(state, seed) {
|
|
542
|
+
if (state.itemType !== seed.itemType) {
|
|
543
|
+
throw new Error(`OPENAI native tool tracker invariant violation: itemType mismatch (${state.itemType} !== ${seed.itemType})`);
|
|
544
|
+
}
|
|
545
|
+
if (state.itemType === 'custom_tool_call' && seed.itemType === 'custom_tool_call') {
|
|
546
|
+
if (state.callId !== seed.callId) {
|
|
547
|
+
throw new Error(`OPENAI native tool tracker invariant violation: custom callId mismatch (${state.callId} !== ${seed.callId})`);
|
|
548
|
+
}
|
|
549
|
+
if (seed.itemId !== undefined && seed.itemId.trim() !== '') {
|
|
550
|
+
const normalizedItemId = seed.itemId.trim();
|
|
551
|
+
if (state.itemId !== undefined && state.itemId !== normalizedItemId) {
|
|
552
|
+
throw new Error(`OPENAI native tool tracker invariant violation: custom itemId mismatch (${state.itemId} !== ${normalizedItemId})`);
|
|
553
|
+
}
|
|
554
|
+
state.itemId = normalizedItemId;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
else if (state.itemType !== 'custom_tool_call' && seed.itemType !== 'custom_tool_call') {
|
|
558
|
+
if (state.itemId !== seed.itemId) {
|
|
559
|
+
throw new Error(`OPENAI native tool tracker invariant violation: item tool itemId mismatch (${state.itemId} !== ${seed.itemId})`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (seed.status !== undefined) {
|
|
563
|
+
state.status = seed.status;
|
|
564
|
+
}
|
|
565
|
+
if (seed.title !== undefined) {
|
|
566
|
+
state.title = seed.title;
|
|
567
|
+
}
|
|
568
|
+
if (seed.summary !== undefined) {
|
|
569
|
+
state.summary = seed.summary;
|
|
570
|
+
}
|
|
571
|
+
if (seed.detail !== undefined) {
|
|
572
|
+
state.detail = seed.detail;
|
|
573
|
+
}
|
|
574
|
+
return state;
|
|
575
|
+
}
|
|
576
|
+
class OpenAiNativeToolTracker {
|
|
577
|
+
constructor() {
|
|
578
|
+
this.itemToolByItemId = new Map();
|
|
579
|
+
this.customToolByCallId = new Map();
|
|
580
|
+
this.customCallIdByItemId = new Map();
|
|
581
|
+
}
|
|
582
|
+
upsertItemTool(seed) {
|
|
583
|
+
const existing = this.itemToolByItemId.get(seed.itemId);
|
|
584
|
+
const state = existing ??
|
|
585
|
+
{
|
|
586
|
+
itemType: seed.itemType,
|
|
587
|
+
itemId: seed.itemId,
|
|
588
|
+
};
|
|
589
|
+
mergeOpenAiNativeToolSeedIntoState(state, seed);
|
|
590
|
+
if (!existing) {
|
|
591
|
+
this.itemToolByItemId.set(seed.itemId, state);
|
|
592
|
+
}
|
|
593
|
+
return state;
|
|
594
|
+
}
|
|
595
|
+
upsertCustomTool(seed) {
|
|
596
|
+
const existing = this.customToolByCallId.get(seed.callId);
|
|
597
|
+
const state = existing ??
|
|
598
|
+
{
|
|
599
|
+
itemType: 'custom_tool_call',
|
|
600
|
+
callId: seed.callId,
|
|
601
|
+
};
|
|
602
|
+
if (seed.itemId !== undefined && seed.itemId.trim() !== '') {
|
|
603
|
+
const normalizedItemId = seed.itemId.trim();
|
|
604
|
+
const existingCallId = this.customCallIdByItemId.get(normalizedItemId);
|
|
605
|
+
if (existingCallId !== undefined && existingCallId !== seed.callId) {
|
|
606
|
+
throw new Error(`OPENAI native tool tracker invariant violation: custom itemId ${normalizedItemId} already bound to a different callId`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
mergeOpenAiNativeToolSeedIntoState(state, seed);
|
|
610
|
+
if (!existing) {
|
|
611
|
+
this.customToolByCallId.set(seed.callId, state);
|
|
612
|
+
}
|
|
613
|
+
if (state.itemId !== undefined) {
|
|
614
|
+
this.customCallIdByItemId.set(state.itemId, state.callId);
|
|
615
|
+
}
|
|
616
|
+
return state;
|
|
617
|
+
}
|
|
618
|
+
claimCustomToolByItemId(itemId) {
|
|
619
|
+
const mappedCallId = this.customCallIdByItemId.get(itemId);
|
|
620
|
+
if (mappedCallId !== undefined) {
|
|
621
|
+
const existing = this.customToolByCallId.get(mappedCallId);
|
|
622
|
+
if (!existing) {
|
|
623
|
+
throw new Error(`OPENAI native tool tracker invariant violation: missing custom tool state for mapped itemId=${itemId}`);
|
|
624
|
+
}
|
|
625
|
+
return existing;
|
|
626
|
+
}
|
|
627
|
+
const unresolved = Array.from(this.customToolByCallId.values()).filter((state) => state.itemId === undefined);
|
|
628
|
+
if (unresolved.length === 1) {
|
|
629
|
+
const claimed = unresolved[0];
|
|
630
|
+
claimed.itemId = itemId;
|
|
631
|
+
this.customCallIdByItemId.set(itemId, claimed.callId);
|
|
632
|
+
return claimed;
|
|
633
|
+
}
|
|
634
|
+
if (unresolved.length === 0) {
|
|
635
|
+
throw new Error(`OPENAI native tool tracker invariant violation: missing unresolved custom tool state for itemId=${itemId}`);
|
|
636
|
+
}
|
|
637
|
+
throw new Error(`OPENAI native tool tracker invariant violation: ambiguous custom tool itemId correlation for itemId=${itemId}`);
|
|
638
|
+
}
|
|
639
|
+
upsert(seed) {
|
|
640
|
+
return seed.itemType === 'custom_tool_call'
|
|
641
|
+
? this.upsertCustomTool(seed)
|
|
642
|
+
: this.upsertItemTool(seed);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function summarizeCodeInterpreterOutputs(value) {
|
|
646
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
647
|
+
return undefined;
|
|
648
|
+
const parts = [];
|
|
649
|
+
let imageCount = 0;
|
|
650
|
+
for (const output of value) {
|
|
651
|
+
if (!isRecord(output) || typeof output.type !== 'string')
|
|
652
|
+
continue;
|
|
653
|
+
if (output.type === 'logs' && typeof output.logs === 'string' && output.logs.trim() !== '') {
|
|
654
|
+
parts.push(output.logs.trim());
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (output.type === 'image') {
|
|
658
|
+
imageCount += 1;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (imageCount > 0) {
|
|
662
|
+
parts.push(`images=${String(imageCount)}`);
|
|
663
|
+
}
|
|
664
|
+
return parts.length > 0 ? parts.join('\n') : undefined;
|
|
665
|
+
}
|
|
666
|
+
function buildOpenAiNativeToolSeed(item, itemId) {
|
|
667
|
+
if (typeof item.type !== 'string' ||
|
|
668
|
+
!OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
671
|
+
const itemType = item.type;
|
|
672
|
+
if (itemType === 'custom_tool_call') {
|
|
673
|
+
const callId = typeof item.call_id === 'string' ? item.call_id.trim() : '';
|
|
674
|
+
if (callId === '') {
|
|
675
|
+
throwOpenAiMalformedBatchOutputItem(itemType, 'missing call_id');
|
|
676
|
+
}
|
|
677
|
+
const base = {
|
|
678
|
+
itemType,
|
|
679
|
+
callId,
|
|
680
|
+
...(typeof itemId === 'string' && itemId.trim() !== '' ? { itemId: itemId.trim() } : {}),
|
|
681
|
+
};
|
|
682
|
+
const namespace = typeof item.namespace === 'string' ? item.namespace.trim() : '';
|
|
683
|
+
const name = typeof item.name === 'string' ? item.name.trim() : '';
|
|
684
|
+
const summary = namespace !== '' && name !== ''
|
|
685
|
+
? `${namespace}:${name}`
|
|
686
|
+
: namespace !== ''
|
|
687
|
+
? namespace
|
|
688
|
+
: name;
|
|
689
|
+
return {
|
|
690
|
+
...base,
|
|
691
|
+
title: 'Custom Tool Call',
|
|
692
|
+
...(summary !== '' ? { summary } : {}),
|
|
693
|
+
...(typeof item.input === 'string' && item.input.trim() !== ''
|
|
694
|
+
? { detail: item.input.trim() }
|
|
695
|
+
: {}),
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
if (typeof itemId !== 'string' || itemId.trim() === '') {
|
|
699
|
+
throwOpenAiMalformedBatchOutputItem(itemType, 'missing itemId');
|
|
700
|
+
}
|
|
701
|
+
const base = {
|
|
702
|
+
itemType,
|
|
703
|
+
itemId: itemId.trim(),
|
|
704
|
+
};
|
|
705
|
+
switch (itemType) {
|
|
706
|
+
case 'file_search_call': {
|
|
707
|
+
const queries = Array.isArray(item.queries) && item.queries.every((entry) => typeof entry === 'string')
|
|
708
|
+
? item.queries
|
|
709
|
+
: [];
|
|
710
|
+
const results = Array.isArray(item.results) ? item.results.length : 0;
|
|
711
|
+
return {
|
|
712
|
+
...base,
|
|
713
|
+
...(typeof item.status === 'string' ? { status: item.status } : {}),
|
|
714
|
+
title: 'File Search',
|
|
715
|
+
...(queries.length > 0 ? { summary: queries.join('\n') } : {}),
|
|
716
|
+
...(results > 0 ? { detail: `results=${String(results)}` } : {}),
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
case 'code_interpreter_call': {
|
|
720
|
+
return {
|
|
721
|
+
...base,
|
|
722
|
+
...(typeof item.status === 'string' ? { status: item.status } : {}),
|
|
723
|
+
title: 'Code Interpreter',
|
|
724
|
+
...(typeof item.code === 'string' && item.code.trim() !== ''
|
|
725
|
+
? { summary: item.code.trim() }
|
|
726
|
+
: {}),
|
|
727
|
+
...(typeof item.container_id === 'string' && item.container_id.trim() !== ''
|
|
728
|
+
? {
|
|
729
|
+
detail: `container=${item.container_id.trim()}${summarizeCodeInterpreterOutputs(item.outputs) ? `\n${summarizeCodeInterpreterOutputs(item.outputs)}` : ''}`,
|
|
730
|
+
}
|
|
731
|
+
: summarizeCodeInterpreterOutputs(item.outputs)
|
|
732
|
+
? { detail: summarizeCodeInterpreterOutputs(item.outputs) }
|
|
733
|
+
: {}),
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
case 'image_generation_call': {
|
|
737
|
+
return {
|
|
738
|
+
...base,
|
|
739
|
+
...(typeof item.status === 'string' ? { status: item.status } : {}),
|
|
740
|
+
title: 'Image Generation',
|
|
741
|
+
summary: typeof item.result === 'string' && item.result.length > 0
|
|
742
|
+
? 'image_ready'
|
|
743
|
+
: 'image_pending',
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
case 'mcp_call': {
|
|
747
|
+
const serverLabel = typeof item.server_label === 'string' ? item.server_label.trim() : '';
|
|
748
|
+
const name = typeof item.name === 'string' ? item.name.trim() : '';
|
|
749
|
+
const summary = serverLabel !== '' && name !== ''
|
|
750
|
+
? `${serverLabel}: ${name}`
|
|
751
|
+
: serverLabel !== ''
|
|
752
|
+
? serverLabel
|
|
753
|
+
: name;
|
|
754
|
+
const detailParts = [
|
|
755
|
+
typeof item.arguments === 'string' && item.arguments.trim() !== ''
|
|
756
|
+
? item.arguments.trim()
|
|
757
|
+
: '',
|
|
758
|
+
typeof item.output === 'string' && item.output.trim() !== '' ? item.output.trim() : '',
|
|
759
|
+
typeof item.error === 'string' && item.error.trim() !== ''
|
|
760
|
+
? `error=${item.error.trim()}`
|
|
761
|
+
: '',
|
|
762
|
+
].filter((part) => part.length > 0);
|
|
763
|
+
return {
|
|
764
|
+
...base,
|
|
765
|
+
...(typeof item.status === 'string' ? { status: item.status } : {}),
|
|
766
|
+
title: 'MCP Tool Call',
|
|
767
|
+
...(summary !== '' ? { summary } : {}),
|
|
768
|
+
...(detailParts.length > 0 ? { detail: detailParts.join('\n') } : {}),
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
case 'mcp_list_tools': {
|
|
772
|
+
const serverLabel = typeof item.server_label === 'string' ? item.server_label.trim() : '';
|
|
773
|
+
const toolNames = Array.isArray(item.tools)
|
|
774
|
+
? item.tools
|
|
775
|
+
.filter((entry) => isRecord(entry))
|
|
776
|
+
.map((entry) => (typeof entry.name === 'string' ? entry.name.trim() : ''))
|
|
777
|
+
.filter((entry) => entry.length > 0)
|
|
778
|
+
: [];
|
|
779
|
+
const error = typeof item.error === 'string' ? item.error.trim() : '';
|
|
780
|
+
return {
|
|
781
|
+
...base,
|
|
782
|
+
title: 'MCP Tool Discovery',
|
|
783
|
+
...(serverLabel !== '' ? { summary: serverLabel } : {}),
|
|
784
|
+
...(toolNames.length > 0 || error !== ''
|
|
785
|
+
? { detail: [...toolNames, ...(error !== '' ? [`error=${error}`] : [])].join('\n') }
|
|
786
|
+
: {}),
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
case 'mcp_approval_request': {
|
|
790
|
+
const serverLabel = typeof item.server_label === 'string' ? item.server_label.trim() : '';
|
|
791
|
+
const name = typeof item.name === 'string' ? item.name.trim() : '';
|
|
792
|
+
return {
|
|
793
|
+
...base,
|
|
794
|
+
title: 'MCP Approval Request',
|
|
795
|
+
...(serverLabel !== '' || name !== ''
|
|
796
|
+
? { summary: [serverLabel, name].filter(Boolean).join(': ') }
|
|
797
|
+
: {}),
|
|
798
|
+
...(typeof item.arguments === 'string' && item.arguments.trim() !== ''
|
|
799
|
+
? { detail: item.arguments.trim() }
|
|
800
|
+
: {}),
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
default: {
|
|
804
|
+
const _exhaustive = itemType;
|
|
805
|
+
return _exhaustive;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
339
808
|
}
|
|
340
809
|
async function buildOpenAiRequestInputWrapper(context, providerConfig) {
|
|
341
810
|
return await buildOpenAiRequestInput(context, providerConfig);
|
|
@@ -347,16 +816,27 @@ function extractOutputMessageText(item) {
|
|
|
347
816
|
if (!Array.isArray(content))
|
|
348
817
|
return '';
|
|
349
818
|
let text = '';
|
|
350
|
-
|
|
351
|
-
|
|
819
|
+
let transcript = '';
|
|
820
|
+
let sawOutputText = false;
|
|
821
|
+
for (const rawPart of content) {
|
|
822
|
+
if (!isRecord(rawPart) || typeof rawPart.type !== 'string')
|
|
352
823
|
continue;
|
|
353
|
-
if (
|
|
354
|
-
|
|
824
|
+
if (rawPart.type === 'output_text' && typeof rawPart.text === 'string') {
|
|
825
|
+
sawOutputText = true;
|
|
826
|
+
text += rawPart.text;
|
|
827
|
+
}
|
|
828
|
+
if (rawPart.type === 'output_audio' && typeof rawPart.transcript === 'string') {
|
|
829
|
+
transcript += rawPart.transcript;
|
|
355
830
|
}
|
|
356
|
-
if (
|
|
357
|
-
text +=
|
|
831
|
+
if (rawPart.type === 'refusal' && typeof rawPart.refusal === 'string') {
|
|
832
|
+
text += rawPart.refusal;
|
|
358
833
|
}
|
|
359
834
|
}
|
|
835
|
+
// Keep batch-mode message extraction aligned with streaming semantics: transcript text is only a
|
|
836
|
+
// fallback when the response item did not already carry canonical output_text content.
|
|
837
|
+
if (!sawOutputText && transcript.length > 0) {
|
|
838
|
+
text += transcript;
|
|
839
|
+
}
|
|
360
840
|
return text;
|
|
361
841
|
}
|
|
362
842
|
function extractReasoningText(item) {
|
|
@@ -407,11 +887,12 @@ function extractReasoningPayload(item) {
|
|
|
407
887
|
out.encrypted_content = encrypted;
|
|
408
888
|
return out;
|
|
409
889
|
}
|
|
410
|
-
function
|
|
411
|
-
const
|
|
890
|
+
function openAiResponseToBatchOutputs(response, genseq) {
|
|
891
|
+
const outputs = [];
|
|
412
892
|
const output = response.output;
|
|
413
893
|
if (!Array.isArray(output))
|
|
414
|
-
return
|
|
894
|
+
return outputs;
|
|
895
|
+
const nativeToolTracker = new OpenAiNativeToolTracker();
|
|
415
896
|
for (const item of output) {
|
|
416
897
|
if (!isRecord(item) || typeof item.type !== 'string')
|
|
417
898
|
continue;
|
|
@@ -419,12 +900,15 @@ function openAiResponseToChatMessages(response, genseq) {
|
|
|
419
900
|
const reasoning = extractReasoningPayload(item);
|
|
420
901
|
const content = extractReasoningText(item);
|
|
421
902
|
if (content.length > 0 || reasoning !== null) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
903
|
+
outputs.push({
|
|
904
|
+
kind: 'message',
|
|
905
|
+
message: {
|
|
906
|
+
type: 'thinking_msg',
|
|
907
|
+
role: 'assistant',
|
|
908
|
+
genseq,
|
|
909
|
+
content,
|
|
910
|
+
reasoning: reasoning ?? undefined,
|
|
911
|
+
},
|
|
428
912
|
});
|
|
429
913
|
}
|
|
430
914
|
continue;
|
|
@@ -432,11 +916,14 @@ function openAiResponseToChatMessages(response, genseq) {
|
|
|
432
916
|
if (item.type === 'message') {
|
|
433
917
|
const content = extractOutputMessageText(item);
|
|
434
918
|
if (content.length > 0) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
919
|
+
outputs.push({
|
|
920
|
+
kind: 'message',
|
|
921
|
+
message: {
|
|
922
|
+
type: 'saying_msg',
|
|
923
|
+
role: 'assistant',
|
|
924
|
+
genseq,
|
|
925
|
+
content,
|
|
926
|
+
},
|
|
440
927
|
});
|
|
441
928
|
}
|
|
442
929
|
continue;
|
|
@@ -446,19 +933,44 @@ function openAiResponseToChatMessages(response, genseq) {
|
|
|
446
933
|
const name = typeof item.name === 'string' ? item.name : '';
|
|
447
934
|
const args = typeof item.arguments === 'string' ? item.arguments : '';
|
|
448
935
|
if (callId.length > 0 && name.length > 0) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
936
|
+
outputs.push({
|
|
937
|
+
kind: 'message',
|
|
938
|
+
message: {
|
|
939
|
+
type: 'func_call_msg',
|
|
940
|
+
role: 'assistant',
|
|
941
|
+
genseq,
|
|
942
|
+
id: callId,
|
|
943
|
+
name,
|
|
944
|
+
arguments: args,
|
|
945
|
+
},
|
|
456
946
|
});
|
|
457
947
|
}
|
|
458
948
|
continue;
|
|
459
949
|
}
|
|
950
|
+
if (item.type === 'web_search_call') {
|
|
951
|
+
const itemId = typeof item.id === 'string' ? item.id.trim() : '';
|
|
952
|
+
if (itemId.length === 0) {
|
|
953
|
+
throwOpenAiMalformedBatchOutputItem(item.type, 'missing itemId');
|
|
954
|
+
}
|
|
955
|
+
outputs.push({
|
|
956
|
+
kind: 'web_search_call',
|
|
957
|
+
call: toOpenAiLlmWebSearchCall(item, itemId, 'done'),
|
|
958
|
+
});
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
if (OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
|
|
962
|
+
const itemId = typeof item.id === 'string' ? item.id.trim() : undefined;
|
|
963
|
+
const seed = buildOpenAiNativeToolSeed(item, itemId);
|
|
964
|
+
if (seed) {
|
|
965
|
+
const state = nativeToolTracker.upsert(seed);
|
|
966
|
+
outputs.push({
|
|
967
|
+
kind: 'native_tool_call',
|
|
968
|
+
call: buildOpenAiNativeToolCallFromState(state, 'done'),
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}
|
|
460
972
|
}
|
|
461
|
-
return
|
|
973
|
+
return outputs;
|
|
462
974
|
}
|
|
463
975
|
class OpenAiGen {
|
|
464
976
|
get apiType() {
|
|
@@ -478,13 +990,16 @@ class OpenAiGen {
|
|
|
478
990
|
const requestInput = await buildOpenAiRequestInput(context, providerConfig);
|
|
479
991
|
const openAiParams = agent.model_params?.openai || {};
|
|
480
992
|
const maxTokens = agent.model_params?.max_tokens;
|
|
481
|
-
const jsonResponseEnabled = resolveOpenAiJsonResponseEnabled(agent, openAiParams);
|
|
482
993
|
const modelInfo = providerConfig.models[agent.model];
|
|
483
994
|
const outputLength = modelInfo?.output_length;
|
|
484
995
|
const maxOutputTokens = maxTokens ?? openAiParams.max_tokens ?? outputLength ?? 1024;
|
|
485
996
|
const parallelToolCalls = openAiParams.parallel_tool_calls ?? true;
|
|
486
|
-
const textConfig = buildOpenAiTextConfig(openAiParams
|
|
997
|
+
const textConfig = buildOpenAiTextConfig(openAiParams);
|
|
487
998
|
const reasoning = buildOpenAiReasoning(openAiParams);
|
|
999
|
+
const nativeTools = buildOpenAiNativeTools(openAiParams);
|
|
1000
|
+
const include = buildOpenAiInclude(requestInput, reasoning, openAiParams);
|
|
1001
|
+
const tools = [...funcTools.map(funcToolToOpenAiTool), ...nativeTools];
|
|
1002
|
+
const vendorHeartbeatEventTypes = resolveOpenAiVendorHeartbeatEventTypes(providerConfig);
|
|
488
1003
|
const payload = {
|
|
489
1004
|
model: agent.model,
|
|
490
1005
|
input: requestInput,
|
|
@@ -494,12 +1009,16 @@ class OpenAiGen {
|
|
|
494
1009
|
store: false,
|
|
495
1010
|
stream: true,
|
|
496
1011
|
...(openAiParams.service_tier !== undefined && { service_tier: openAiParams.service_tier }),
|
|
1012
|
+
...(openAiParams.safety_identifier !== undefined && {
|
|
1013
|
+
safety_identifier: openAiParams.safety_identifier,
|
|
1014
|
+
}),
|
|
497
1015
|
...(openAiParams.temperature !== undefined && { temperature: openAiParams.temperature }),
|
|
498
1016
|
...(openAiParams.top_p !== undefined && { top_p: openAiParams.top_p }),
|
|
499
1017
|
...(reasoning !== undefined && { reasoning }),
|
|
1018
|
+
...(include !== undefined ? { include } : {}),
|
|
500
1019
|
...(textConfig !== undefined && { text: textConfig }),
|
|
501
|
-
...(
|
|
502
|
-
? { tools
|
|
1020
|
+
...(tools.length > 0
|
|
1021
|
+
? { tools, tool_choice: 'auto' }
|
|
503
1022
|
: { tool_choice: 'none' }),
|
|
504
1023
|
};
|
|
505
1024
|
let sayingStarted = false;
|
|
@@ -507,9 +1026,33 @@ class OpenAiGen {
|
|
|
507
1026
|
let currentThinkingContent = '';
|
|
508
1027
|
let finishedThinkingFromDelta = false;
|
|
509
1028
|
let sawOutputText = false;
|
|
1029
|
+
let currentAudioTranscript = '';
|
|
510
1030
|
let activeStream = 'idle';
|
|
511
1031
|
let usage = { kind: 'unavailable' };
|
|
512
1032
|
let returnedModel;
|
|
1033
|
+
const nativeToolTracker = new OpenAiNativeToolTracker();
|
|
1034
|
+
function claimNativeToolStateByEventItemId(itemId, itemType, title) {
|
|
1035
|
+
try {
|
|
1036
|
+
if (itemType === 'custom_tool_call') {
|
|
1037
|
+
const existing = nativeToolTracker.claimCustomToolByItemId(itemId);
|
|
1038
|
+
if (existing.title === undefined || existing.title.trim() === '') {
|
|
1039
|
+
existing.title = title;
|
|
1040
|
+
}
|
|
1041
|
+
return existing;
|
|
1042
|
+
}
|
|
1043
|
+
return nativeToolTracker.upsert({
|
|
1044
|
+
itemType,
|
|
1045
|
+
itemId,
|
|
1046
|
+
title,
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
catch (error) {
|
|
1050
|
+
const detail = error instanceof Error && error.message.trim().length > 0
|
|
1051
|
+
? error.message
|
|
1052
|
+
: `failed to resolve native tool state for ${itemType}`;
|
|
1053
|
+
return throwOpenAiMalformedStreamEvent(receiver, itemType, detail);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
513
1056
|
function applyArgsDelta(state, chunk) {
|
|
514
1057
|
if (chunk.length === 0)
|
|
515
1058
|
return;
|
|
@@ -538,7 +1081,28 @@ class OpenAiGen {
|
|
|
538
1081
|
state.emitted = true;
|
|
539
1082
|
await receiver_.funcCall(state.callId, state.name, args);
|
|
540
1083
|
}
|
|
1084
|
+
async function emitNativeToolCall(state, phase) {
|
|
1085
|
+
if (!receiver.nativeToolCall)
|
|
1086
|
+
return;
|
|
1087
|
+
await receiver.nativeToolCall(buildOpenAiNativeToolCallFromState(state, phase));
|
|
1088
|
+
}
|
|
541
1089
|
const activeFuncCallsByItemId = new Map();
|
|
1090
|
+
const activeWebSearchCallsByItemId = new Map();
|
|
1091
|
+
function readOptionalEventString(value) {
|
|
1092
|
+
return typeof value === 'string' ? value : '';
|
|
1093
|
+
}
|
|
1094
|
+
async function requireEventString(value, eventType, field) {
|
|
1095
|
+
if (typeof value === 'string')
|
|
1096
|
+
return value;
|
|
1097
|
+
return await throwOpenAiMalformedStreamEvent(receiver, eventType, `missing string ${field}`);
|
|
1098
|
+
}
|
|
1099
|
+
async function requireNonEmptyEventItemId(value, eventType) {
|
|
1100
|
+
const itemId = (await requireEventString(value, eventType, 'item_id')).trim();
|
|
1101
|
+
if (itemId.length === 0) {
|
|
1102
|
+
await throwOpenAiMalformedStreamEvent(receiver, eventType, 'empty item_id');
|
|
1103
|
+
}
|
|
1104
|
+
return itemId;
|
|
1105
|
+
}
|
|
542
1106
|
try {
|
|
543
1107
|
const stream = await client.responses.create(payload, {
|
|
544
1108
|
...(abortSignal ? { signal: abortSignal } : {}),
|
|
@@ -547,6 +1111,14 @@ class OpenAiGen {
|
|
|
547
1111
|
if (abortSignal?.aborted) {
|
|
548
1112
|
throw new Error('AbortError');
|
|
549
1113
|
}
|
|
1114
|
+
const providerEvent = event;
|
|
1115
|
+
const providerEventType = readEventType(providerEvent);
|
|
1116
|
+
if (providerEventType !== null && vendorHeartbeatEventTypes.has(providerEventType)) {
|
|
1117
|
+
// Some Responses-compatible gateways emit out-of-band heartbeat frames to keep idle
|
|
1118
|
+
// streams alive. They are not part of the official OpenAI event taxonomy and carry no
|
|
1119
|
+
// model semantics, so the OpenAI wrapper ignores them locally.
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
550
1122
|
switch (event.type) {
|
|
551
1123
|
case 'response.created':
|
|
552
1124
|
case 'response.in_progress':
|
|
@@ -556,6 +1128,15 @@ class OpenAiGen {
|
|
|
556
1128
|
returnedModel = tryExtractApiReturnedModel(event.response);
|
|
557
1129
|
}
|
|
558
1130
|
if (event.type === 'response.completed') {
|
|
1131
|
+
if (!sawOutputText && currentAudioTranscript.trim().length > 0) {
|
|
1132
|
+
if (!sayingStarted) {
|
|
1133
|
+
await receiver.sayingStart();
|
|
1134
|
+
}
|
|
1135
|
+
await receiver.sayingChunk(currentAudioTranscript);
|
|
1136
|
+
await receiver.sayingFinish();
|
|
1137
|
+
sayingStarted = false;
|
|
1138
|
+
sawOutputText = true;
|
|
1139
|
+
}
|
|
559
1140
|
if (thinkingStarted) {
|
|
560
1141
|
await receiver.thinkingFinish(buildReasoningPayloadFromText(currentThinkingContent));
|
|
561
1142
|
thinkingStarted = false;
|
|
@@ -598,7 +1179,7 @@ class OpenAiGen {
|
|
|
598
1179
|
throw new Error(message);
|
|
599
1180
|
}
|
|
600
1181
|
case 'response.output_text.delta': {
|
|
601
|
-
const delta = event.delta;
|
|
1182
|
+
const delta = await requireEventString(event.delta, event.type, 'delta');
|
|
602
1183
|
if (delta.length > 0) {
|
|
603
1184
|
if (activeStream === 'thinking') {
|
|
604
1185
|
const detail = 'OPENAI stream overlap violation: received output_text while thinking stream still active';
|
|
@@ -624,7 +1205,8 @@ class OpenAiGen {
|
|
|
624
1205
|
break;
|
|
625
1206
|
}
|
|
626
1207
|
case 'response.output_text.done': {
|
|
627
|
-
|
|
1208
|
+
const text = await requireEventString(event.text, event.type, 'text');
|
|
1209
|
+
if (!sawOutputText && text.length > 0) {
|
|
628
1210
|
if (activeStream === 'thinking') {
|
|
629
1211
|
const detail = 'OPENAI stream overlap violation: received output_text while thinking stream still active';
|
|
630
1212
|
log.error(detail, new Error('openai_stream_overlap_violation'));
|
|
@@ -643,7 +1225,7 @@ class OpenAiGen {
|
|
|
643
1225
|
await receiver.sayingStart();
|
|
644
1226
|
activeStream = 'saying';
|
|
645
1227
|
}
|
|
646
|
-
await receiver.sayingChunk(
|
|
1228
|
+
await receiver.sayingChunk(text);
|
|
647
1229
|
sawOutputText = true;
|
|
648
1230
|
}
|
|
649
1231
|
if (sayingStarted) {
|
|
@@ -656,7 +1238,7 @@ class OpenAiGen {
|
|
|
656
1238
|
}
|
|
657
1239
|
case 'response.reasoning_text.delta':
|
|
658
1240
|
case 'response.reasoning_summary_text.delta': {
|
|
659
|
-
const delta = event.delta;
|
|
1241
|
+
const delta = await requireEventString(event.delta, event.type, 'delta');
|
|
660
1242
|
if (delta.length > 0) {
|
|
661
1243
|
if (activeStream === 'saying') {
|
|
662
1244
|
const detail = 'OPENAI stream overlap violation: received reasoning while saying stream still active';
|
|
@@ -715,6 +1297,15 @@ class OpenAiGen {
|
|
|
715
1297
|
}
|
|
716
1298
|
break;
|
|
717
1299
|
}
|
|
1300
|
+
case 'response.audio.delta':
|
|
1301
|
+
case 'response.audio.done':
|
|
1302
|
+
break;
|
|
1303
|
+
case 'response.audio.transcript.delta': {
|
|
1304
|
+
currentAudioTranscript += await requireEventString(event.delta, event.type, 'delta');
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
case 'response.audio.transcript.done':
|
|
1308
|
+
break;
|
|
718
1309
|
case 'response.output_item.done': {
|
|
719
1310
|
const item = event.item;
|
|
720
1311
|
if (isRecord(item) && item.type === 'function_call') {
|
|
@@ -747,6 +1338,37 @@ class OpenAiGen {
|
|
|
747
1338
|
}
|
|
748
1339
|
break;
|
|
749
1340
|
}
|
|
1341
|
+
if (isRecord(item) && item.type === 'web_search_call' && receiver.webSearchCall) {
|
|
1342
|
+
const itemId = typeof item.id === 'string' ? item.id.trim() : '';
|
|
1343
|
+
if (itemId.length === 0) {
|
|
1344
|
+
await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
|
|
1345
|
+
}
|
|
1346
|
+
const call = toOpenAiLlmWebSearchCall(item, itemId, 'done');
|
|
1347
|
+
activeWebSearchCallsByItemId.set(itemId, {
|
|
1348
|
+
itemId,
|
|
1349
|
+
action: call.action,
|
|
1350
|
+
});
|
|
1351
|
+
await receiver.webSearchCall(call);
|
|
1352
|
+
break;
|
|
1353
|
+
}
|
|
1354
|
+
if (isRecord(item) &&
|
|
1355
|
+
typeof item.type === 'string' &&
|
|
1356
|
+
OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
|
|
1357
|
+
const itemId = typeof item.id === 'string' ? item.id.trim() : '';
|
|
1358
|
+
if (item.type !== 'custom_tool_call' && itemId.length === 0) {
|
|
1359
|
+
await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
|
|
1360
|
+
}
|
|
1361
|
+
if (item.type === 'custom_tool_call' &&
|
|
1362
|
+
!(typeof item.call_id === 'string' && item.call_id.trim().length > 0)) {
|
|
1363
|
+
await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing call_id');
|
|
1364
|
+
}
|
|
1365
|
+
const seed = buildOpenAiNativeToolSeed(item, itemId.length > 0 ? itemId : undefined);
|
|
1366
|
+
if (seed) {
|
|
1367
|
+
const state = nativeToolTracker.upsert(seed);
|
|
1368
|
+
await emitNativeToolCall(state, 'done');
|
|
1369
|
+
}
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
750
1372
|
if (isRecord(item) && item.type === 'message' && !sawOutputText) {
|
|
751
1373
|
const text = extractOutputMessageText(item);
|
|
752
1374
|
if (text.length > 0) {
|
|
@@ -841,14 +1463,182 @@ class OpenAiGen {
|
|
|
841
1463
|
activeFuncCallsByItemId.set(itemId, state);
|
|
842
1464
|
}
|
|
843
1465
|
}
|
|
1466
|
+
if (isRecord(item) && item.type === 'web_search_call' && receiver.webSearchCall) {
|
|
1467
|
+
const itemId = typeof item.id === 'string' ? item.id.trim() : '';
|
|
1468
|
+
if (itemId.length === 0) {
|
|
1469
|
+
await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
|
|
1470
|
+
}
|
|
1471
|
+
const call = toOpenAiLlmWebSearchCall(item, itemId, 'added');
|
|
1472
|
+
activeWebSearchCallsByItemId.set(itemId, {
|
|
1473
|
+
itemId,
|
|
1474
|
+
action: call.action,
|
|
1475
|
+
});
|
|
1476
|
+
await receiver.webSearchCall(call);
|
|
1477
|
+
}
|
|
1478
|
+
if (isRecord(item) &&
|
|
1479
|
+
typeof item.type === 'string' &&
|
|
1480
|
+
OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
|
|
1481
|
+
const itemId = typeof item.id === 'string' ? item.id.trim() : '';
|
|
1482
|
+
if (item.type !== 'custom_tool_call' && itemId.length === 0) {
|
|
1483
|
+
await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
|
|
1484
|
+
}
|
|
1485
|
+
if (item.type === 'custom_tool_call' &&
|
|
1486
|
+
!(typeof item.call_id === 'string' && item.call_id.trim().length > 0)) {
|
|
1487
|
+
await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing call_id');
|
|
1488
|
+
}
|
|
1489
|
+
const seed = buildOpenAiNativeToolSeed(item, itemId.length > 0 ? itemId : undefined);
|
|
1490
|
+
if (seed) {
|
|
1491
|
+
const state = nativeToolTracker.upsert(seed);
|
|
1492
|
+
await emitNativeToolCall(state, 'added');
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
break;
|
|
1496
|
+
}
|
|
1497
|
+
case 'response.web_search_call.in_progress':
|
|
1498
|
+
case 'response.web_search_call.searching':
|
|
1499
|
+
case 'response.web_search_call.completed': {
|
|
1500
|
+
if (!receiver.webSearchCall) {
|
|
1501
|
+
break;
|
|
1502
|
+
}
|
|
1503
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1504
|
+
const existing = activeWebSearchCallsByItemId.get(itemId);
|
|
1505
|
+
const status = event.type === 'response.web_search_call.in_progress'
|
|
1506
|
+
? 'in_progress'
|
|
1507
|
+
: event.type === 'response.web_search_call.searching'
|
|
1508
|
+
? 'searching'
|
|
1509
|
+
: 'completed';
|
|
1510
|
+
const phase = event.type === 'response.web_search_call.completed' ? 'done' : 'added';
|
|
1511
|
+
await receiver.webSearchCall(buildOpenAiWebSearchProgressCall(itemId, phase, status, existing?.action));
|
|
1512
|
+
break;
|
|
1513
|
+
}
|
|
1514
|
+
case 'response.file_search_call.in_progress':
|
|
1515
|
+
case 'response.file_search_call.searching':
|
|
1516
|
+
case 'response.file_search_call.completed': {
|
|
1517
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1518
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'file_search_call', 'File Search');
|
|
1519
|
+
existing.status =
|
|
1520
|
+
event.type === 'response.file_search_call.in_progress'
|
|
1521
|
+
? 'in_progress'
|
|
1522
|
+
: event.type === 'response.file_search_call.searching'
|
|
1523
|
+
? 'searching'
|
|
1524
|
+
: 'completed';
|
|
1525
|
+
await emitNativeToolCall(existing, event.type === 'response.file_search_call.completed' ? 'done' : 'added');
|
|
1526
|
+
break;
|
|
1527
|
+
}
|
|
1528
|
+
case 'response.code_interpreter_call.in_progress':
|
|
1529
|
+
case 'response.code_interpreter_call.interpreting':
|
|
1530
|
+
case 'response.code_interpreter_call.completed': {
|
|
1531
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1532
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'code_interpreter_call', 'Code Interpreter');
|
|
1533
|
+
existing.status =
|
|
1534
|
+
event.type === 'response.code_interpreter_call.in_progress'
|
|
1535
|
+
? 'in_progress'
|
|
1536
|
+
: event.type === 'response.code_interpreter_call.interpreting'
|
|
1537
|
+
? 'interpreting'
|
|
1538
|
+
: 'completed';
|
|
1539
|
+
await emitNativeToolCall(existing, event.type === 'response.code_interpreter_call.completed' ? 'done' : 'added');
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
case 'response.code_interpreter_call_code.delta': {
|
|
1543
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1544
|
+
const delta = await requireEventString(event.delta, event.type, 'delta');
|
|
1545
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'code_interpreter_call', 'Code Interpreter');
|
|
1546
|
+
existing.summary = `${existing.summary ?? ''}${delta}`;
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
case 'response.code_interpreter_call_code.done': {
|
|
1550
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1551
|
+
const code = await requireEventString(event.code, event.type, 'code');
|
|
1552
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'code_interpreter_call', 'Code Interpreter');
|
|
1553
|
+
existing.summary = code;
|
|
1554
|
+
await emitNativeToolCall(existing, 'added');
|
|
1555
|
+
break;
|
|
1556
|
+
}
|
|
1557
|
+
case 'response.image_generation_call.in_progress':
|
|
1558
|
+
case 'response.image_generation_call.generating':
|
|
1559
|
+
case 'response.image_generation_call.completed': {
|
|
1560
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1561
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'image_generation_call', 'Image Generation');
|
|
1562
|
+
existing.status =
|
|
1563
|
+
event.type === 'response.image_generation_call.in_progress'
|
|
1564
|
+
? 'in_progress'
|
|
1565
|
+
: event.type === 'response.image_generation_call.generating'
|
|
1566
|
+
? 'generating'
|
|
1567
|
+
: 'completed';
|
|
1568
|
+
await emitNativeToolCall(existing, event.type === 'response.image_generation_call.completed' ? 'done' : 'added');
|
|
1569
|
+
break;
|
|
1570
|
+
}
|
|
1571
|
+
case 'response.image_generation_call.partial_image': {
|
|
1572
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1573
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'image_generation_call', 'Image Generation');
|
|
1574
|
+
const currentIndex = typeof event.partial_image_index === 'number' ? event.partial_image_index + 1 : 1;
|
|
1575
|
+
existing.detail = `partial_images=${String(currentIndex)}`;
|
|
1576
|
+
break;
|
|
1577
|
+
}
|
|
1578
|
+
case 'response.mcp_call.in_progress':
|
|
1579
|
+
case 'response.mcp_call.failed':
|
|
1580
|
+
case 'response.mcp_call.completed': {
|
|
1581
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1582
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_call', 'MCP Tool Call');
|
|
1583
|
+
existing.status =
|
|
1584
|
+
event.type === 'response.mcp_call.in_progress'
|
|
1585
|
+
? 'in_progress'
|
|
1586
|
+
: event.type === 'response.mcp_call.failed'
|
|
1587
|
+
? 'failed'
|
|
1588
|
+
: 'completed';
|
|
1589
|
+
await emitNativeToolCall(existing, event.type === 'response.mcp_call.completed' ? 'done' : 'added');
|
|
1590
|
+
break;
|
|
1591
|
+
}
|
|
1592
|
+
case 'response.mcp_call_arguments.delta': {
|
|
1593
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1594
|
+
const delta = await requireEventString(event.delta, event.type, 'delta');
|
|
1595
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_call', 'MCP Tool Call');
|
|
1596
|
+
existing.detail = `${existing.detail ?? ''}${delta}`;
|
|
1597
|
+
break;
|
|
1598
|
+
}
|
|
1599
|
+
case 'response.mcp_call_arguments.done': {
|
|
1600
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1601
|
+
const args = await requireEventString(event.arguments, event.type, 'arguments');
|
|
1602
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_call', 'MCP Tool Call');
|
|
1603
|
+
existing.detail = args;
|
|
1604
|
+
await emitNativeToolCall(existing, 'added');
|
|
1605
|
+
break;
|
|
1606
|
+
}
|
|
1607
|
+
case 'response.mcp_list_tools.in_progress':
|
|
1608
|
+
case 'response.mcp_list_tools.failed':
|
|
1609
|
+
case 'response.mcp_list_tools.completed': {
|
|
1610
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1611
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_list_tools', 'MCP Tool Discovery');
|
|
1612
|
+
existing.status =
|
|
1613
|
+
event.type === 'response.mcp_list_tools.in_progress'
|
|
1614
|
+
? 'in_progress'
|
|
1615
|
+
: event.type === 'response.mcp_list_tools.failed'
|
|
1616
|
+
? 'failed'
|
|
1617
|
+
: 'completed';
|
|
1618
|
+
await emitNativeToolCall(existing, event.type === 'response.mcp_list_tools.completed' ? 'done' : 'added');
|
|
1619
|
+
break;
|
|
1620
|
+
}
|
|
1621
|
+
case 'response.custom_tool_call_input.delta': {
|
|
1622
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1623
|
+
const delta = await requireEventString(event.delta, event.type, 'delta');
|
|
1624
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'custom_tool_call', 'Custom Tool Call');
|
|
1625
|
+
existing.detail = `${existing.detail ?? ''}${delta}`;
|
|
1626
|
+
break;
|
|
1627
|
+
}
|
|
1628
|
+
case 'response.custom_tool_call_input.done': {
|
|
1629
|
+
const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
|
|
1630
|
+
const input = await requireEventString(event.input, event.type, 'input');
|
|
1631
|
+
const existing = await claimNativeToolStateByEventItemId(itemId, 'custom_tool_call', 'Custom Tool Call');
|
|
1632
|
+
existing.detail = input;
|
|
1633
|
+
await emitNativeToolCall(existing, 'added');
|
|
844
1634
|
break;
|
|
845
1635
|
}
|
|
846
1636
|
case 'response.content_part.added':
|
|
847
1637
|
case 'response.content_part.done':
|
|
848
1638
|
break;
|
|
849
1639
|
case 'response.function_call_arguments.delta': {
|
|
850
|
-
const itemId = event.item_id;
|
|
851
|
-
const delta = event.delta;
|
|
1640
|
+
const itemId = await requireEventString(event.item_id, event.type, 'item_id');
|
|
1641
|
+
const delta = await requireEventString(event.delta, event.type, 'delta');
|
|
852
1642
|
if (itemId.length > 0 && delta.length > 0) {
|
|
853
1643
|
const existing = activeFuncCallsByItemId.get(itemId);
|
|
854
1644
|
const state = existing ??
|
|
@@ -865,9 +1655,9 @@ class OpenAiGen {
|
|
|
865
1655
|
break;
|
|
866
1656
|
}
|
|
867
1657
|
case 'response.function_call_arguments.done': {
|
|
868
|
-
const itemId = event.item_id;
|
|
869
|
-
const name = event.name;
|
|
870
|
-
const args = event.arguments;
|
|
1658
|
+
const itemId = await requireEventString(event.item_id, event.type, 'item_id');
|
|
1659
|
+
const name = readOptionalEventString(event.name);
|
|
1660
|
+
const args = readOptionalEventString(event.arguments);
|
|
871
1661
|
if (itemId.length > 0) {
|
|
872
1662
|
const existing = activeFuncCallsByItemId.get(itemId);
|
|
873
1663
|
const state = existing ??
|
|
@@ -906,10 +1696,27 @@ class OpenAiGen {
|
|
|
906
1696
|
}
|
|
907
1697
|
}
|
|
908
1698
|
}
|
|
1699
|
+
for (const state of activeFuncCallsByItemId.values()) {
|
|
1700
|
+
await maybeEmitFuncCall(state, receiver);
|
|
1701
|
+
}
|
|
1702
|
+
const unresolvedFuncCalls = Array.from(activeFuncCallsByItemId.values()).filter((state) => !state.emitted &&
|
|
1703
|
+
(state.callId.length > 0 || state.name.length > 0 || state.argsJson.length > 0));
|
|
1704
|
+
if (unresolvedFuncCalls.length > 0) {
|
|
1705
|
+
const detail = 'OPENAI incomplete function-call stream state: ' +
|
|
1706
|
+
unresolvedFuncCalls
|
|
1707
|
+
.map((state) => `itemId=${state.itemId},callId=${state.callId || '<missing>'},name=${state.name || '<missing>'}`)
|
|
1708
|
+
.join('; ');
|
|
1709
|
+
log.error(detail, new Error('openai_incomplete_function_call_stream_state'));
|
|
1710
|
+
if (receiver.streamError) {
|
|
1711
|
+
await receiver.streamError(detail);
|
|
1712
|
+
}
|
|
1713
|
+
throw new Error(detail);
|
|
1714
|
+
}
|
|
909
1715
|
}
|
|
910
1716
|
catch (error) {
|
|
911
|
-
|
|
912
|
-
|
|
1717
|
+
const annotatedError = maybeAnnotateOpenAiQuirkFailure(providerConfig, error);
|
|
1718
|
+
log.warn('OPENAI streaming error', annotatedError);
|
|
1719
|
+
throw annotatedError;
|
|
913
1720
|
}
|
|
914
1721
|
finally {
|
|
915
1722
|
if (thinkingStarted) {
|
|
@@ -932,13 +1739,15 @@ class OpenAiGen {
|
|
|
932
1739
|
const requestInput = await buildOpenAiRequestInput(context, providerConfig);
|
|
933
1740
|
const openAiParams = agent.model_params?.openai || {};
|
|
934
1741
|
const maxTokens = agent.model_params?.max_tokens;
|
|
935
|
-
const jsonResponseEnabled = resolveOpenAiJsonResponseEnabled(agent, openAiParams);
|
|
936
1742
|
const modelInfo = providerConfig.models[agent.model];
|
|
937
1743
|
const outputLength = modelInfo?.output_length;
|
|
938
1744
|
const maxOutputTokens = maxTokens ?? openAiParams.max_tokens ?? outputLength ?? 1024;
|
|
939
1745
|
const parallelToolCalls = openAiParams.parallel_tool_calls ?? true;
|
|
940
|
-
const textConfig = buildOpenAiTextConfig(openAiParams
|
|
1746
|
+
const textConfig = buildOpenAiTextConfig(openAiParams);
|
|
941
1747
|
const reasoning = buildOpenAiReasoning(openAiParams);
|
|
1748
|
+
const nativeTools = buildOpenAiNativeTools(openAiParams);
|
|
1749
|
+
const include = buildOpenAiInclude(requestInput, reasoning, openAiParams);
|
|
1750
|
+
const tools = [...funcTools.map(funcToolToOpenAiTool), ...nativeTools];
|
|
942
1751
|
const payload = {
|
|
943
1752
|
model: agent.model,
|
|
944
1753
|
input: requestInput,
|
|
@@ -948,12 +1757,16 @@ class OpenAiGen {
|
|
|
948
1757
|
store: false,
|
|
949
1758
|
stream: false,
|
|
950
1759
|
...(openAiParams.service_tier !== undefined && { service_tier: openAiParams.service_tier }),
|
|
1760
|
+
...(openAiParams.safety_identifier !== undefined && {
|
|
1761
|
+
safety_identifier: openAiParams.safety_identifier,
|
|
1762
|
+
}),
|
|
951
1763
|
...(openAiParams.temperature !== undefined && { temperature: openAiParams.temperature }),
|
|
952
1764
|
...(openAiParams.top_p !== undefined && { top_p: openAiParams.top_p }),
|
|
953
1765
|
...(reasoning !== undefined && { reasoning }),
|
|
1766
|
+
...(include !== undefined ? { include } : {}),
|
|
954
1767
|
...(textConfig !== undefined && { text: textConfig }),
|
|
955
|
-
...(
|
|
956
|
-
? { tools
|
|
1768
|
+
...(tools.length > 0
|
|
1769
|
+
? { tools, tool_choice: 'auto' }
|
|
957
1770
|
: { tool_choice: 'none' }),
|
|
958
1771
|
};
|
|
959
1772
|
const response = await client.responses.create(payload, {
|
|
@@ -964,8 +1777,12 @@ class OpenAiGen {
|
|
|
964
1777
|
}
|
|
965
1778
|
const returnedModel = typeof response.model === 'string' ? response.model : undefined;
|
|
966
1779
|
const usage = parseOpenAiUsage(response.usage);
|
|
1780
|
+
const outputs = openAiResponseToBatchOutputs(response, genseq);
|
|
967
1781
|
return {
|
|
968
|
-
messages:
|
|
1782
|
+
messages: outputs
|
|
1783
|
+
.filter((entry) => entry.kind === 'message')
|
|
1784
|
+
.map((entry) => entry.message),
|
|
1785
|
+
outputs,
|
|
969
1786
|
usage,
|
|
970
1787
|
llmGenModel: returnedModel,
|
|
971
1788
|
};
|