@openclaw/ai 2026.9.5 → 2026.9.7
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/{anthropic-COtDvgtt.mjs → anthropic-Cw2pgccz.mjs} +59 -88
- package/dist/{anthropic-payload-policy-5Erq1Nzy.d.mts → anthropic-payload-policy-NIPVYXPq.d.mts} +8 -6
- package/dist/{anthropic-stream-reducer-BvdYYWL8.mjs → anthropic-stream-reducer-D9MCkl8Z.mjs} +194 -247
- package/dist/{api-registry-Ba2Cv-ut.d.mts → api-registry-D-nbmSqr.d.mts} +2 -2
- package/dist/{azure-openai-responses-YrgnD583.mjs → azure-openai-responses-BwT3-X8P.mjs} +22 -33
- package/dist/{base64-BQOzsvUH.mjs → base64-C1PsYcRQ.mjs} +22 -16
- package/dist/{diagnostics-Dm4bisWG.mjs → diagnostics-CJaB6g8J.mjs} +55 -10
- package/dist/{diagnostics-CPeq9F7y.mjs → diagnostics-DzrF1h4J.mjs} +18 -1
- package/dist/diagnostics.d.mts +11 -5
- package/dist/diagnostics.mjs +4 -4
- package/dist/{env-api-keys-bktO00EJ.mjs → env-api-keys-CyX4VKGO.mjs} +7 -5
- package/dist/event-stream-CMQfX_yr.d.mts +1 -0
- package/dist/{event-stream-CCoa-qSI.d.mts → event-stream-DOPFYVqs.d.mts} +2 -2
- package/dist/{event-stream-D8PARQfL.mjs → event-stream-vd9UnCAU.mjs} +10 -0
- package/dist/event-stream.d.mts +2 -2
- package/dist/event-stream.mjs +1 -1
- package/dist/{google-BuhpRO9r.mjs → google-BfapkS_K.mjs} +8 -11
- package/dist/google-interactions-DW3iXzjc.mjs +574 -0
- package/dist/{google-messages-CyWnYlh0.mjs → google-messages-CtjVtVot.mjs} +35 -71
- package/dist/{google-shared-BT5ZeNer.mjs → google-shared-Y2YfotEu.mjs} +43 -47
- package/dist/{google-vertex-C7EplRvt.mjs → google-vertex-Dov4kBWu.mjs} +16 -30
- package/dist/{host-B4MeUNBc.mjs → host-CHi3X87F.mjs} +64 -189
- package/dist/{host-policy-DUnXSx0I.mjs → host-policy-BOAwRg_M.mjs} +2 -14
- package/dist/{host-FZ1RA_qD.d.mts → host-rxlNaBwm.d.mts} +9 -3
- package/dist/{index-DaF2QbwS.d.mts → index-D6KsSE74.d.mts} +57 -9
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +7 -7
- package/dist/internal/anthropic.d.mts +13 -8
- package/dist/internal/anthropic.mjs +5 -5
- package/dist/internal/openai-completions-compat.d.mts +1 -1
- package/dist/internal/openai-completions-compat.mjs +1 -1
- package/dist/internal/openai-responses-payload-policy.d.mts +1 -1
- package/dist/internal/openai-responses-payload-policy.mjs +2 -2
- package/dist/internal/openai.d.mts +46 -9
- package/dist/internal/openai.mjs +13 -12
- package/dist/internal/runtime.d.mts +31 -70
- package/dist/internal/runtime.mjs +11 -66
- package/dist/internal/shared.d.mts +16 -13
- package/dist/internal/shared.mjs +6 -5
- package/dist/internal/tool-schema.d.mts +2 -2
- package/dist/internal/tool-schema.mjs +2 -2
- package/dist/{mistral-moA-mbwl.mjs → mistral-RS6mboC-.mjs} +46 -93
- package/dist/{model-transport-url-DKocrEsb.d.mts → model-transport-url-C5f59L3Y.d.mts} +1 -1
- package/dist/{openai-chatgpt-responses-DWID3EFO.mjs → openai-chatgpt-responses-CYbidtrn.mjs} +118 -99
- package/dist/{openai-completions-DJ1Vm-CD.mjs → openai-completions-D14ihPYk.mjs} +14 -12
- package/dist/{openai-completions-compat-CkwdxTZx.mjs → openai-completions-compat-BOVWBZDn.mjs} +3 -5
- package/dist/{openai-completions-compat-JE7gqxb9.d.mts → openai-completions-compat-qJjmqQ71.d.mts} +3 -3
- package/dist/{openai-completions-stream-Bu98b0Hv.mjs → openai-completions-stream-CDCuw3DP.mjs} +124 -183
- package/dist/{openai-prompt-cache-CJ_xEevu.d.mts → openai-prompt-cache-CouErLh7.d.mts} +2 -2
- package/dist/{openai-prompt-cache-1wfaIszn.mjs → openai-prompt-cache-D2V7MqR7.mjs} +1 -1
- package/dist/{openai-provider-client-DPo0Hmak.mjs → openai-provider-client-CwfsGBbA.mjs} +2 -2
- package/dist/{openai-reasoning-effort-NlFZmfEu.mjs → openai-reasoning-effort-ConhwkeX.mjs} +14 -5
- package/dist/{openai-responses-BejZzQCP.mjs → openai-responses-CKCBEc2W.mjs} +12 -11
- package/dist/openai-responses-compaction-replay-DiYn-gFM.d.mts +12 -0
- package/dist/{openai-responses-compaction-window-D6P5oZP5.mjs → openai-responses-compaction-window-UNK1GDpy.mjs} +5 -5
- package/dist/{openai-responses-contracts-DWrfMODE.mjs → openai-responses-contracts-CLxnqS9p.mjs} +1 -1
- package/dist/{openai-responses-contracts-Dflvrfa8.d.mts → openai-responses-contracts-D1iZ0njF.d.mts} +4 -4
- package/dist/{openai-responses-prompt-observer-internal-C15_-OwV.mjs → openai-responses-prompt-observer-internal-iAqLb4tk.mjs} +16 -13
- package/dist/openai-responses-request-lifecycle-Fsag-IGZ.mjs +36 -0
- package/dist/{openai-responses-shared-CwsziD_m.mjs → openai-responses-shared-DL4ulQn7.mjs} +838 -577
- package/dist/{openai-responses-terminal-usage-Dl3J6zrf.d.mts → openai-responses-terminal-usage-BE7lf7nW.d.mts} +2 -2
- package/dist/{openai-stop-reason-Drnn_6Qj.mjs → openai-stop-reason-8s70vDXj.mjs} +2 -12
- package/dist/{openai-tool-schema-ynZBgqW-.d.mts → openai-tool-schema-a5ysBBga.d.mts} +0 -6
- package/dist/{openai-tool-schema-BO8rwyAD.mjs → openai-tool-schema-vsl-cE-n.mjs} +365 -429
- package/dist/{openai-transport-params-DQeuAvBl.mjs → openai-transport-params-BwlpzX-2.mjs} +61 -106
- package/dist/{positive-integer-DtjCkbue.mjs → positive-integer-CjN5OJbH.mjs} +1 -1
- package/dist/{provider-error-DDqw9Qda.mjs → provider-error-CwT_j2v0.mjs} +82 -10
- package/dist/{provider-error-CzNw4BWX.d.mts → provider-error-xumb1V3F.d.mts} +3 -1
- package/dist/{provider-options-DprLsWh9.d.mts → provider-options-Df7ov0K1.d.mts} +6 -4
- package/dist/{provider-replay-context-CnUSOwhr.mjs → provider-replay-context-DsyI5jOP.mjs} +1 -1
- package/dist/{provider-transcript-transform-BsRAhuWJ.mjs → provider-transcript-transform-JbFbHV8h.mjs} +17 -2
- package/dist/{provider-transport-turn-state-CkGToCD2.mjs → provider-transport-turn-state-BheUi_hl.mjs} +1 -1
- package/dist/{provider-types-CAKRC7N5.d.mts → provider-types-d4XnoZMd.d.mts} +3 -3
- package/dist/provider-types.d.mts +6 -6
- package/dist/providers.d.mts +2 -2
- package/dist/providers.mjs +15 -11
- package/dist/{reasoning-tag-text-partitioner-Dy9IO8Dc.mjs → reasoning-tag-text-partitioner-B6FIIBt6.mjs} +193 -98
- package/dist/{record-coerce-DwRYMj3t.mjs → record-coerce-CNDovvft.mjs} +1 -1
- package/dist/simple-options-CGAkCwtM.mjs +349 -0
- package/dist/{src-DeKjbE8I.mjs → src-H4aXcFuX.mjs} +53 -26
- package/dist/{stream-CREqxHgU.mjs → stream-DDsCBgd-.mjs} +1 -7
- package/dist/{stream-first-event-timeout-C9ZadkJW.mjs → stream-first-event-timeout-B9o0S4SZ.mjs} +1 -1
- package/dist/{tool-schema-json-projection-CD9c_fK8.mjs → tool-schema-json-projection-Ct8SeBeJ.mjs} +63 -23
- package/dist/{transport-stream-shared-D-6FQSHm.mjs → transport-stream-shared-Dh5qKJ8e.mjs} +63 -37
- package/dist/{transport-stream-shared-BbUkFaHi.d.mts → transport-stream-shared-yYhXa4kX.d.mts} +13 -7
- package/dist/{transport-utils-1cyq5Y7x.mjs → transport-utils-C-4bf2Ok.mjs} +3 -4
- package/dist/transports.d.mts +37 -33
- package/dist/transports.mjs +208 -239
- package/dist/types-CB-_vNyI.d.mts +1 -0
- package/dist/{types-4_uVs5WH.d.mts → types-CLLR3eB4.d.mts} +5 -1
- package/dist/types.d.mts +6 -6
- package/dist/types.mjs +5 -5
- package/dist/{validation-Dw7cb6BV.mjs → validation-BFZc40Ig.mjs} +80 -54
- package/dist/{validation-Ctzu2DhF.d.mts → validation-Cat2wQsc.d.mts} +1 -1
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/package.json +6 -6
- package/dist/assistant-output-BsEkB-vU.mjs +0 -16
- package/dist/event-stream-DmSCpi1T.d.mts +0 -1
- package/dist/simple-options-C6cFWj_f.mjs +0 -175
- package/dist/types-DkJfb4W3.d.mts +0 -1
- package/dist/utf16-slice-CvGodqok.mjs +0 -29
|
@@ -1,26 +1,25 @@
|
|
|
1
|
-
import { t as appendAssistantMessageDiagnostic } from "./diagnostics-
|
|
2
|
-
import { r as appendAssistantThinking } from "./event-stream-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { n as providerReplayContextMatches, t as buildProviderReplayContext } from "./provider-replay-context-CnUSOwhr.mjs";
|
|
1
|
+
import { o as readProviderRefusalReview, t as appendAssistantMessageDiagnostic } from "./diagnostics-DzrF1h4J.mjs";
|
|
2
|
+
import { r as appendAssistantThinking } from "./event-stream-vd9UnCAU.mjs";
|
|
3
|
+
import { o as isRecord } from "./record-coerce-CNDovvft.mjs";
|
|
4
|
+
import { k as isImageWithMediaPayload, n as getAiTransportHost, r as resolveAiTransportHeaderSentinels, w as calculateCost } from "./host-CHi3X87F.mjs";
|
|
5
|
+
import { a as normalizeOptionalString, o as normalizeStringifiedOptionalString, s as readStringValue } from "./string-coerce-fsri9iCu.mjs";
|
|
6
|
+
import { d as supportsOpenAITemperature } from "./openai-reasoning-effort-ConhwkeX.mjs";
|
|
7
|
+
import { o as truncateUtf16Safe, s as stableStringify } from "./provider-error-CwT_j2v0.mjs";
|
|
8
|
+
import { a as resolveModelSseDebugMode, d as isResponsesProviderTool, g as resolveResponsesTerminalStopReason, h as readResponsesReasoningTokens, i as resolveModelPayloadDebugMode, l as ResponsesOutputIdentityError, m as mapResponsesTerminalUsage, r as emitModelTransportDebug, u as hasOnlyResponsesFunctionTools } from "./diagnostics-CJaB6g8J.mjs";
|
|
9
|
+
import { o as redactSensitiveText } from "./transport-utils-C-4bf2Ok.mjs";
|
|
10
|
+
import { i as resolveOpenAICompletionsCompat } from "./openai-completions-compat-BOVWBZDn.mjs";
|
|
11
|
+
import { A as shortHash, C as parseJsonObjectPreservingUnsafeIntegers, E as createToolArgumentPreviewSchedule, O as parseStreamingJson, S as withProviderResponseHook, _ as sanitizeNonEmptyTransportPayloadText, b as transportAbortError, c as failTransportStream, d as iterateModelStream, g as parseTerminalToolCallArguments, t as IncompleteToolCallError, u as finalizeTransportStream, v as sanitizeTransportPayloadText } from "./transport-stream-shared-Dh5qKJ8e.mjs";
|
|
12
|
+
import { C as stripSystemPromptCacheBoundary, D as sortPromptCacheToolsByName, d as describeToolResultMediaPlaceholder, m as extractToolResultText, s as prepareModelRequestBody } from "./simple-options-CGAkCwtM.mjs";
|
|
13
|
+
import { A as redactIdentifier, C as log, d as prepareOpenAITools, h as resolveOpenAIRequestReasoning, j as sha256Hex, u as resolveOpenAIStrictToolFlagWithDiagnostics, y as createOpenAIProviderAcceptanceHook } from "./openai-transport-params-BwlpzX-2.mjs";
|
|
14
|
+
import { n as providerReplayContextMatches, t as buildProviderReplayContext } from "./provider-replay-context-DsyI5jOP.mjs";
|
|
16
15
|
import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
|
|
17
|
-
import {
|
|
18
|
-
import { t as
|
|
19
|
-
import { a as resolveOpenAIProjectedToolsStrictToolFlag, f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-
|
|
20
|
-
import { a as OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE, c as OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE, f as RESPONSE_FAILED_NO_DETAILS_MESSAGE, o as OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY, p as isPreviousResponseRejection, s as OPENAI_RESPONSES_REASONING_REPLAY_META_KEY } from "./openai-responses-contracts-
|
|
21
|
-
import { n as isOpenAIResponsesCompactionOutput, r as readOpenAIResponsesCompactionWindow } from "./openai-responses-compaction-window-
|
|
16
|
+
import { t as transformProviderMessages } from "./provider-transcript-transform-JbFbHV8h.mjs";
|
|
17
|
+
import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-B9o0S4SZ.mjs";
|
|
18
|
+
import { a as resolveOpenAIProjectedToolsStrictToolFlag, f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-vsl-cE-n.mjs";
|
|
19
|
+
import { a as OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE, c as OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE, f as RESPONSE_FAILED_NO_DETAILS_MESSAGE, o as OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY, p as isPreviousResponseRejection, s as OPENAI_RESPONSES_REASONING_REPLAY_META_KEY } from "./openai-responses-contracts-CLxnqS9p.mjs";
|
|
20
|
+
import { n as isOpenAIResponsesCompactionOutput, r as readOpenAIResponsesCompactionWindow } from "./openai-responses-compaction-window-UNK1GDpy.mjs";
|
|
22
21
|
import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
|
|
23
|
-
import {
|
|
22
|
+
import { randomUUID } from "node:crypto";
|
|
24
23
|
//#region packages/ai/src/transports/openai-responses-replay.ts
|
|
25
24
|
/** Resolves the assistant message id that can be replayed to OpenAI Responses. */
|
|
26
25
|
function resolveReplayableResponsesMessageId(params) {
|
|
@@ -29,12 +28,68 @@ function resolveReplayableResponsesMessageId(params) {
|
|
|
29
28
|
return params.previousReplayItemWasReasoning ? params.textSignatureId : void 0;
|
|
30
29
|
}
|
|
31
30
|
//#endregion
|
|
31
|
+
//#region packages/ai/src/transports/openai-responses-tool-call-id-shape.ts
|
|
32
|
+
/**
|
|
33
|
+
* Pure OpenAI Responses `call_*`/`fc_*` tool-call-id shape helpers.
|
|
34
|
+
*
|
|
35
|
+
* Moved out of embedded-agent-helpers/openai.ts (the AgentMessage[]-walking
|
|
36
|
+
* caller lives in src/agents and imports from packages/ai, so the reverse
|
|
37
|
+
* import direction is not available) so the continuation transport can
|
|
38
|
+
* recognize its own cached raw ids after this same reshaping and restore
|
|
39
|
+
* them on a replayed wire request -- see openai-responses-continuation.ts.
|
|
40
|
+
*/
|
|
41
|
+
const OPENAI_RESPONSES_ID_MAX_LENGTH = 64;
|
|
42
|
+
const OPENAI_RESPONSES_CALL_ID_RE = /^call_[A-Za-z0-9_-]{1,59}$/;
|
|
43
|
+
const OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE = /^fc_[A-Za-z0-9_-]{1,61}$/;
|
|
44
|
+
function splitOpenAIFunctionCallPairing(id) {
|
|
45
|
+
const separator = id.indexOf("|");
|
|
46
|
+
if (separator <= 0 || separator >= id.length - 1) return { callId: id };
|
|
47
|
+
return {
|
|
48
|
+
callId: id.slice(0, separator),
|
|
49
|
+
itemId: id.slice(separator + 1)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function shortOpenAIResponsesIdHash(id) {
|
|
53
|
+
return sha256Hex(id).slice(0, 10);
|
|
54
|
+
}
|
|
55
|
+
function sanitizeOpenAIResponsesIdTail(value) {
|
|
56
|
+
return value.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
|
|
57
|
+
}
|
|
58
|
+
function normalizeOpenAIResponsesIdPart(params) {
|
|
59
|
+
const trimmed = params.value.trim();
|
|
60
|
+
if (params.isValid(trimmed)) return trimmed;
|
|
61
|
+
const rawTail = trimmed.startsWith(params.prefix) ? trimmed.slice(params.prefix.length) : trimmed;
|
|
62
|
+
const hash = shortOpenAIResponsesIdHash(trimmed || params.prefix);
|
|
63
|
+
const maxTailLength = 64 - params.prefix.length;
|
|
64
|
+
const hashSuffix = `_${hash}`;
|
|
65
|
+
const tail = `${sanitizeOpenAIResponsesIdTail(rawTail).slice(0, Math.max(1, maxTailLength - hashSuffix.length)) || "id"}${hashSuffix}`.slice(0, maxTailLength);
|
|
66
|
+
return `${params.prefix}${tail}`;
|
|
67
|
+
}
|
|
68
|
+
/** Same shaping `normalizeOpenAIResponsesToolCallIds` applies to a replayed `call_id`/`call_id|fc_id` pair. */
|
|
69
|
+
function normalizeOpenAIResponsesFunctionCallId(id) {
|
|
70
|
+
const { callId, itemId } = splitOpenAIFunctionCallPairing(id);
|
|
71
|
+
const normalizedCallId = normalizeOpenAIResponsesIdPart({
|
|
72
|
+
value: itemId ? `${callId}|${itemId}` : callId,
|
|
73
|
+
prefix: "call_",
|
|
74
|
+
isValid: (value) => OPENAI_RESPONSES_CALL_ID_RE.test(value)
|
|
75
|
+
});
|
|
76
|
+
if (!itemId) return normalizedCallId;
|
|
77
|
+
return `${normalizedCallId}|${normalizeOpenAIResponsesIdPart({
|
|
78
|
+
value: itemId,
|
|
79
|
+
prefix: "fc_",
|
|
80
|
+
isValid: (value) => OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE.test(value)
|
|
81
|
+
})}`;
|
|
82
|
+
}
|
|
83
|
+
function shouldNormalizeOpenAIResponsesToolCallId(id) {
|
|
84
|
+
const pairing = splitOpenAIFunctionCallPairing(id);
|
|
85
|
+
if (!OPENAI_RESPONSES_CALL_ID_RE.test(pairing.callId)) return true;
|
|
86
|
+
if (pairing.itemId === void 0) return false;
|
|
87
|
+
return !OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE.test(pairing.itemId);
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
32
90
|
//#region packages/ai/src/transports/openai-responses-compaction-replay.ts
|
|
33
91
|
const OPENAI_RESPONSES_COMPACTION_SUPPRESSION_TYPE = "openai-responses-compaction-suppression";
|
|
34
92
|
const OPENAI_RESPONSES_COMPACTION_SUPPRESSION_DATA = "rejected";
|
|
35
|
-
function buildOpenAIResponsesReplayContext(model, options) {
|
|
36
|
-
return buildProviderReplayContext(model, options);
|
|
37
|
-
}
|
|
38
93
|
function isOpenAIResponsesReplayContext(value) {
|
|
39
94
|
if (!isRecord(value)) return false;
|
|
40
95
|
return typeof value.provider === "string" && typeof value.api === "string" && typeof value.model === "string" && (value.baseUrlHash === void 0 || typeof value.baseUrlHash === "string") && (value.sessionHash === void 0 || typeof value.sessionHash === "string") && (value.authProfileHash === void 0 || typeof value.authProfileHash === "string");
|
|
@@ -81,7 +136,7 @@ function captureOpenAIResponsesCompaction(output, item, boundary, model, capture
|
|
|
81
136
|
output.providerReplay = replay;
|
|
82
137
|
}
|
|
83
138
|
function suppressOpenAIResponsesCompaction(output, model, options, rejectedCheckpoint) {
|
|
84
|
-
const context =
|
|
139
|
+
const context = buildProviderReplayContext(model, options);
|
|
85
140
|
if (!context.baseUrlHash) return;
|
|
86
141
|
output.providerReplay = {
|
|
87
142
|
v: 1,
|
|
@@ -113,7 +168,7 @@ function isSafeResponsesReplayItemId(id) {
|
|
|
113
168
|
return typeof id === "string" && id.length > 0 && id.length <= 64;
|
|
114
169
|
}
|
|
115
170
|
function resolveNewestOpenAIResponsesCompactionReplay(messages, model, options) {
|
|
116
|
-
const context =
|
|
171
|
+
const context = buildProviderReplayContext(model, options);
|
|
117
172
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
118
173
|
const message = messages[index];
|
|
119
174
|
if (message?.role !== "assistant") continue;
|
|
@@ -185,7 +240,7 @@ function buildOpenAIResponsesReasoningReplayMetadata(model, options) {
|
|
|
185
240
|
return {
|
|
186
241
|
v: 1,
|
|
187
242
|
source: "openai-responses",
|
|
188
|
-
...
|
|
243
|
+
...buildProviderReplayContext(model, options)
|
|
189
244
|
};
|
|
190
245
|
}
|
|
191
246
|
//#endregion
|
|
@@ -259,12 +314,87 @@ function requestWithoutInput(request) {
|
|
|
259
314
|
metadata
|
|
260
315
|
};
|
|
261
316
|
}
|
|
262
|
-
function
|
|
317
|
+
function canonicalizeToolCallId(callId, itemId) {
|
|
318
|
+
if (typeof callId !== "string") return callId;
|
|
319
|
+
const paired = typeof itemId === "string" && itemId ? `${callId}|${itemId}` : callId;
|
|
320
|
+
if (!shouldNormalizeOpenAIResponsesToolCallId(paired)) return callId;
|
|
321
|
+
return splitOpenAIFunctionCallPairing(normalizeOpenAIResponsesFunctionCallId(paired)).callId;
|
|
322
|
+
}
|
|
323
|
+
function responseToolCalls(items) {
|
|
324
|
+
return items.flatMap((item) => {
|
|
325
|
+
if (!isRecord(item) || item.type !== "function_call" || typeof item.call_id !== "string") return [];
|
|
326
|
+
return [{
|
|
327
|
+
callId: item.call_id,
|
|
328
|
+
...typeof item.id === "string" ? { itemId: item.id } : {}
|
|
329
|
+
}];
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function toolCallReplayShapes(call) {
|
|
333
|
+
return new Set([
|
|
334
|
+
call.callId,
|
|
335
|
+
canonicalizeToolCallId(call.callId, call.itemId),
|
|
336
|
+
canonicalizeToolCallId(call.callId, void 0)
|
|
337
|
+
].filter((shape) => typeof shape === "string"));
|
|
338
|
+
}
|
|
339
|
+
function toolCallOwnerByReplayShape(calls) {
|
|
340
|
+
const ownerByReplayShape = /* @__PURE__ */ new Map();
|
|
341
|
+
for (const [index, call] of calls.entries()) for (const replayShape of toolCallReplayShapes(call)) {
|
|
342
|
+
const owner = ownerByReplayShape.get(replayShape);
|
|
343
|
+
ownerByReplayShape.set(replayShape, owner === void 0 || owner === index ? index : null);
|
|
344
|
+
}
|
|
345
|
+
return ownerByReplayShape;
|
|
346
|
+
}
|
|
347
|
+
function pendingResponsesToolCalls(calls, input) {
|
|
348
|
+
const ownerByReplayShape = toolCallOwnerByReplayShape(calls);
|
|
349
|
+
const replayedCallIds = /* @__PURE__ */ new Set();
|
|
350
|
+
const lastCallPosition = /* @__PURE__ */ new Map();
|
|
351
|
+
const lastOutputPosition = /* @__PURE__ */ new Map();
|
|
352
|
+
for (const [inputIndex, item] of input.entries()) {
|
|
353
|
+
if (!isRecord(item) || typeof item.call_id !== "string") continue;
|
|
354
|
+
const owner = ownerByReplayShape.get(item.call_id);
|
|
355
|
+
if (item.type === "function_call") {
|
|
356
|
+
replayedCallIds.add(item.call_id);
|
|
357
|
+
if (owner !== void 0 && owner !== null) lastCallPosition.set(owner, inputIndex);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
if (item.type !== "function_call_output") continue;
|
|
361
|
+
if (owner !== void 0 && owner !== null) lastOutputPosition.set(owner, inputIndex);
|
|
362
|
+
}
|
|
363
|
+
return calls.filter((_call, index) => {
|
|
364
|
+
const callPosition = lastCallPosition.get(index);
|
|
365
|
+
return [...toolCallReplayShapes(_call)].some((shape) => replayedCallIds.has(shape)) && (callPosition === void 0 || (lastOutputPosition.get(index) ?? -1) < callPosition);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
function recordResponsesContinuationState(previous, lastRequest, response, continued = false) {
|
|
369
|
+
const calls = previous && continued ? [...previous.pendingToolCalls ?? [], ...responseToolCalls(previous.lastResponseItems)] : responseToolCalls(lastRequest.input ?? []);
|
|
370
|
+
const seenCalls = /* @__PURE__ */ new Set();
|
|
371
|
+
const pendingToolCalls = pendingResponsesToolCalls(calls.filter((call) => {
|
|
372
|
+
const key = JSON.stringify([call.callId, call.itemId]);
|
|
373
|
+
if (seenCalls.has(key)) return false;
|
|
374
|
+
seenCalls.add(key);
|
|
375
|
+
return true;
|
|
376
|
+
}), lastRequest.input ?? []);
|
|
377
|
+
return {
|
|
378
|
+
lastRequest,
|
|
379
|
+
lastResponseId: response.id,
|
|
380
|
+
lastResponseItems: response.output,
|
|
381
|
+
pendingToolCalls
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function normalizeAssistantReplayInput(input, fromResponse = false, ignoreCachedItemIds = false) {
|
|
263
385
|
return input.map((item) => {
|
|
264
386
|
if (!isRecord(item)) return item;
|
|
265
387
|
if (item.type === "reasoning") return { type: "reasoning" };
|
|
266
|
-
if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
|
|
267
|
-
const { id:
|
|
388
|
+
if (item.type !== "function_call" && item.type !== "function_call_output" && !(item.type === "message" && item.role === "assistant")) return item;
|
|
389
|
+
const { id: rawId, status: _status, ...stableItem } = item;
|
|
390
|
+
if (item.type === "function_call_output") {
|
|
391
|
+
if ("id" in item) stableItem.id = item.id;
|
|
392
|
+
if ("status" in item) stableItem.status = item.status;
|
|
393
|
+
}
|
|
394
|
+
if ("call_id" in stableItem) {
|
|
395
|
+
const itemId = item.type === "function_call" && !(fromResponse && ignoreCachedItemIds) ? rawId : void 0;
|
|
396
|
+
stableItem.call_id = canonicalizeToolCallId(stableItem.call_id, itemId);
|
|
397
|
+
}
|
|
268
398
|
if (fromResponse && item.type === "function_call") {
|
|
269
399
|
const args = parseJsonObjectPreservingUnsafeIntegers(stableItem.arguments);
|
|
270
400
|
stableItem.arguments = args ? JSON.stringify(args) : stableItem.arguments;
|
|
@@ -277,6 +407,53 @@ function normalizeAssistantReplayInput(input, fromResponse = false) {
|
|
|
277
407
|
return stableItem;
|
|
278
408
|
});
|
|
279
409
|
}
|
|
410
|
+
function normalizeContinuationHistory(input) {
|
|
411
|
+
const normalized = normalizeAssistantReplayInput(input);
|
|
412
|
+
const ownerByReplayShape = toolCallOwnerByReplayShape(responseToolCalls(input));
|
|
413
|
+
let callIndex = 0;
|
|
414
|
+
for (const [index, item] of input.entries()) {
|
|
415
|
+
const normalizedItem = normalized[index];
|
|
416
|
+
if (!isRecord(item) || !isRecord(normalizedItem)) continue;
|
|
417
|
+
if (item.type === "function_call") {
|
|
418
|
+
if (typeof normalizedItem.call_id !== "string") return;
|
|
419
|
+
const callOwner = ownerByReplayShape.get(normalizedItem.call_id);
|
|
420
|
+
if (callOwner !== callIndex) return;
|
|
421
|
+
normalizedItem.call_id = { callOwner };
|
|
422
|
+
callIndex += 1;
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (item.type !== "function_call_output" || typeof item.call_id !== "string") continue;
|
|
426
|
+
const owner = ownerByReplayShape.get(item.call_id);
|
|
427
|
+
if (owner === null) return;
|
|
428
|
+
normalizedItem.call_id = owner === void 0 ? { unknownCallId: canonicalizeToolCallId(item.call_id, void 0) } : { callOwner: owner };
|
|
429
|
+
}
|
|
430
|
+
return normalized;
|
|
431
|
+
}
|
|
432
|
+
function continuationHistoryMatches(previousInput, currentInput) {
|
|
433
|
+
for (const [index, previousItem] of previousInput.entries()) {
|
|
434
|
+
const currentItem = currentInput[index];
|
|
435
|
+
if (!isRecord(previousItem) || !isRecord(currentItem)) continue;
|
|
436
|
+
if (previousItem.type === "function_call" && currentItem.type === "function_call") {
|
|
437
|
+
if (typeof previousItem.call_id === "string" && typeof currentItem.call_id === "string" && !toolCallReplayShapes({
|
|
438
|
+
callId: previousItem.call_id,
|
|
439
|
+
...typeof previousItem.id === "string" ? { itemId: previousItem.id } : {}
|
|
440
|
+
}).has(currentItem.call_id)) return false;
|
|
441
|
+
}
|
|
442
|
+
if (previousItem.type === "function_call_output" && currentItem.type === "function_call_output" && typeof previousItem.call_id === "string" && typeof currentItem.call_id === "string" && currentItem.call_id !== previousItem.call_id && currentItem.call_id !== canonicalizeToolCallId(previousItem.call_id, void 0)) return false;
|
|
443
|
+
}
|
|
444
|
+
const previousCalls = responseToolCalls(previousInput);
|
|
445
|
+
const currentCalls = responseToolCalls(currentInput);
|
|
446
|
+
if (previousCalls.length !== currentCalls.length) return false;
|
|
447
|
+
for (const [index, previousCall] of previousCalls.entries()) {
|
|
448
|
+
const currentCall = currentCalls[index];
|
|
449
|
+
if (!currentCall) return false;
|
|
450
|
+
const previousShapes = toolCallReplayShapes(previousCall);
|
|
451
|
+
if (![...toolCallReplayShapes(currentCall)].some((shape) => previousShapes.has(shape))) return false;
|
|
452
|
+
}
|
|
453
|
+
const normalizedPrevious = normalizeContinuationHistory(previousInput);
|
|
454
|
+
const normalizedCurrent = normalizeContinuationHistory(currentInput);
|
|
455
|
+
return normalizedPrevious !== void 0 && normalizedCurrent !== void 0 && jsonValuesEqual(normalizedCurrent, normalizedPrevious);
|
|
456
|
+
}
|
|
280
457
|
function responsesContinuationRequestFingerprint(request) {
|
|
281
458
|
const serialized = JSON.stringify(requestWithoutInput(request));
|
|
282
459
|
return sha256Hex(stableStringify(JSON.parse(serialized)));
|
|
@@ -285,6 +462,34 @@ function responsesContinuationPrefixFingerprint(input, output = []) {
|
|
|
285
462
|
const serialized = JSON.stringify([...normalizeAssistantReplayInput(input), ...normalizeAssistantReplayInput(output, true)]);
|
|
286
463
|
return sha256Hex(stableStringify(JSON.parse(serialized)));
|
|
287
464
|
}
|
|
465
|
+
function restoreRawCallIdsInDelta(delta, cachedResponseItems, replayedToolRound, pendingToolCalls) {
|
|
466
|
+
const currentResponseCalls = responseToolCalls(cachedResponseItems);
|
|
467
|
+
const replayedCalls = replayedToolRound.filter((item) => isRecord(item) && item.type === "function_call");
|
|
468
|
+
if (currentResponseCalls.length !== replayedCalls.length) return;
|
|
469
|
+
const cachedCalls = [...pendingToolCalls, ...currentResponseCalls];
|
|
470
|
+
const currentCallOffset = pendingToolCalls.length;
|
|
471
|
+
const ownerByReplayShape = toolCallOwnerByReplayShape(cachedCalls);
|
|
472
|
+
for (const index of currentResponseCalls.keys()) {
|
|
473
|
+
const replayedCallId = replayedCalls[index]?.call_id;
|
|
474
|
+
if (typeof replayedCallId !== "string" || ownerByReplayShape.get(replayedCallId) !== currentCallOffset + index) return;
|
|
475
|
+
}
|
|
476
|
+
const restoredDelta = [];
|
|
477
|
+
for (const item of delta) {
|
|
478
|
+
if (!isRecord(item) || item.type !== "function_call_output" || typeof item.call_id !== "string") {
|
|
479
|
+
restoredDelta.push(item);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const owner = ownerByReplayShape.get(item.call_id);
|
|
483
|
+
if (owner === void 0 || owner === null) return;
|
|
484
|
+
const rawCallId = cachedCalls[owner]?.callId;
|
|
485
|
+
if (!rawCallId) return;
|
|
486
|
+
restoredDelta.push(rawCallId === item.call_id ? item : {
|
|
487
|
+
...item,
|
|
488
|
+
call_id: rawCallId
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
return restoredDelta;
|
|
492
|
+
}
|
|
288
493
|
function resolveResponsesContinuationRequest(continuation, request, steering) {
|
|
289
494
|
if (!continuation) return {
|
|
290
495
|
request,
|
|
@@ -310,15 +515,24 @@ function resolveResponsesContinuationRequest(continuation, request, steering) {
|
|
|
310
515
|
request,
|
|
311
516
|
continuationStatus: "history_shorter"
|
|
312
517
|
};
|
|
313
|
-
|
|
518
|
+
const replayedToolRoundInput = currentInput.slice(previousInput.length, baselineLength);
|
|
519
|
+
const replayedToolRound = normalizeAssistantReplayInput(replayedToolRoundInput);
|
|
520
|
+
const historyToolRoundUnchanged = jsonValuesEqual(replayedToolRound, normalizeAssistantReplayInput(continuation.lastResponseItems, true)) || jsonValuesEqual(replayedToolRound, normalizeAssistantReplayInput(continuation.lastResponseItems, true, true));
|
|
521
|
+
if (!continuationHistoryMatches(previousInput, currentInput.slice(0, previousInput.length)) || !historyToolRoundUnchanged) return {
|
|
522
|
+
request,
|
|
523
|
+
continuationStatus: "history_changed"
|
|
524
|
+
};
|
|
525
|
+
const restoredInput = restoreRawCallIdsInDelta(currentInput.slice(baselineLength), continuation.lastResponseItems, replayedToolRoundInput, continuation.pendingToolCalls ?? []);
|
|
526
|
+
if (!restoredInput) return {
|
|
314
527
|
request,
|
|
315
528
|
continuationStatus: "history_changed"
|
|
316
529
|
};
|
|
530
|
+
const restoredResponseInput = restoredInput;
|
|
317
531
|
return {
|
|
318
532
|
request: {
|
|
319
533
|
...prepared,
|
|
320
534
|
previous_response_id: continuation.lastResponseId,
|
|
321
|
-
input:
|
|
535
|
+
input: restoredResponseInput
|
|
322
536
|
},
|
|
323
537
|
...prepared !== request ? { fullRequest: prepared } : {},
|
|
324
538
|
continuationStatus: "continued"
|
|
@@ -353,16 +567,12 @@ function claimOpenAIResponsesHttpContinuation(params) {
|
|
|
353
567
|
return {
|
|
354
568
|
request: params.request.store === false ? fullRequest : resolved.request,
|
|
355
569
|
fullRequest,
|
|
356
|
-
commit: (effectiveRequest, response) => {
|
|
570
|
+
commit: (effectiveRequest, response, dispatchedPreviousResponseId) => {
|
|
357
571
|
if (httpContinuationEntries.get(key) !== claimed) return;
|
|
358
572
|
const ready = {
|
|
359
573
|
...claimed,
|
|
360
574
|
kind: "ready",
|
|
361
|
-
state:
|
|
362
|
-
lastRequest: effectiveRequest,
|
|
363
|
-
lastResponseId: response.id,
|
|
364
|
-
lastResponseItems: response.output
|
|
365
|
-
},
|
|
575
|
+
state: recordResponsesContinuationState(previous?.kind === "ready" ? previous.state : void 0, effectiveRequest, response, previous?.kind === "ready" && dispatchedPreviousResponseId === previous.state.lastResponseId),
|
|
366
576
|
idleTimer: setTimeout(() => deleteHttpContinuationIfOwned(key, ready), HTTP_CONTINUATION_IDLE_TTL_MS)
|
|
367
577
|
};
|
|
368
578
|
ready.idleTimer.unref?.();
|
|
@@ -574,36 +784,23 @@ function parseOpenAIResponsesTextSignature(signature) {
|
|
|
574
784
|
} catch {}
|
|
575
785
|
return { id: signature };
|
|
576
786
|
}
|
|
577
|
-
|
|
787
|
+
const responsesInputSource = Symbol("openclaw.responsesInputSource");
|
|
788
|
+
/** Source identity survives payload spreads but never enters serialized provider input. */
|
|
789
|
+
function bindResponsesInputMessage(source) {
|
|
790
|
+
const identity = {};
|
|
791
|
+
Object.defineProperty(source, responsesInputSource, {
|
|
792
|
+
value: identity,
|
|
793
|
+
enumerable: true
|
|
794
|
+
});
|
|
795
|
+
return (input) => typeof input === "object" && input !== null && Reflect.get(input, responsesInputSource) === identity;
|
|
796
|
+
}
|
|
797
|
+
function buildResponsesInputMessage(role, content, source) {
|
|
798
|
+
const identity = source && Reflect.get(source, responsesInputSource);
|
|
578
799
|
return {
|
|
579
800
|
type: "message",
|
|
580
801
|
role,
|
|
581
|
-
content
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
function createOpenAIResponsesAssistantOutput(model, api = model.api) {
|
|
585
|
-
return {
|
|
586
|
-
role: "assistant",
|
|
587
|
-
content: [],
|
|
588
|
-
api,
|
|
589
|
-
provider: model.provider,
|
|
590
|
-
model: model.id,
|
|
591
|
-
usage: {
|
|
592
|
-
input: 0,
|
|
593
|
-
output: 0,
|
|
594
|
-
cacheRead: 0,
|
|
595
|
-
cacheWrite: 0,
|
|
596
|
-
totalTokens: 0,
|
|
597
|
-
cost: {
|
|
598
|
-
input: 0,
|
|
599
|
-
output: 0,
|
|
600
|
-
cacheRead: 0,
|
|
601
|
-
cacheWrite: 0,
|
|
602
|
-
total: 0
|
|
603
|
-
}
|
|
604
|
-
},
|
|
605
|
-
stopReason: "stop",
|
|
606
|
-
timestamp: Date.now()
|
|
802
|
+
content,
|
|
803
|
+
...identity ? { [responsesInputSource]: identity } : {}
|
|
607
804
|
};
|
|
608
805
|
}
|
|
609
806
|
function convertResponsesMessagesWithStyle(model, context, allowedToolCallProviders, options, conversionStyle) {
|
|
@@ -611,7 +808,7 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
611
808
|
const providerStyle = conversionStyle === "provider";
|
|
612
809
|
const shouldReplayReasoningItems = options?.replayReasoningItems ?? true;
|
|
613
810
|
const shouldReplayResponsesItemIds = options?.replayResponsesItemIds ?? true;
|
|
614
|
-
const replayContext =
|
|
811
|
+
const replayContext = buildProviderReplayContext(model, {
|
|
615
812
|
sessionId: options?.sessionId,
|
|
616
813
|
authProfileId: options?.authProfileId
|
|
617
814
|
});
|
|
@@ -649,7 +846,7 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
649
846
|
authProfileId: options?.authProfileId,
|
|
650
847
|
mode: options?.replayMode
|
|
651
848
|
});
|
|
652
|
-
const transformMessages = (source) => providerStyle ? transformProviderMessages(source, model, normalizeToolCallId) : transformTransportMessages(source, model, normalizeToolCallId, {
|
|
849
|
+
const transformMessages = (source) => providerStyle ? transformProviderMessages(source, model, normalizeToolCallId) : getAiTransportHost().transformTransportMessages(source, model, normalizeToolCallId, {
|
|
653
850
|
normalizeSameModelToolCallIds: shouldNormalizeSameModelToolCallIds,
|
|
654
851
|
preserveUnframedToolResults: replayPlan.preserveUnframedToolResults
|
|
655
852
|
});
|
|
@@ -685,7 +882,7 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
685
882
|
if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
|
|
686
883
|
type: "input_text",
|
|
687
884
|
text: sanitizeTransportPayloadText(msg.content)
|
|
688
|
-
}]));
|
|
885
|
+
}], msg));
|
|
689
886
|
else {
|
|
690
887
|
const content = msg.content.map((item) => item.type === "text" ? {
|
|
691
888
|
type: "input_text",
|
|
@@ -695,7 +892,7 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
695
892
|
detail: "auto",
|
|
696
893
|
image_url: `data:${item.mimeType};base64,${item.data}`
|
|
697
894
|
}).filter((item) => providerStyle || model.input.includes("image") || item.type !== "input_image");
|
|
698
|
-
if (content.length > 0) messages.push(buildResponsesInputMessage("user", content));
|
|
895
|
+
if (content.length > 0) messages.push(buildResponsesInputMessage("user", content, msg));
|
|
699
896
|
else if (providerStyle) continue;
|
|
700
897
|
}
|
|
701
898
|
} else if (msg.role === "assistant") {
|
|
@@ -793,7 +990,7 @@ function convertResponsesMessagesWithStyle(model, context, allowedToolCallProvid
|
|
|
793
990
|
}
|
|
794
991
|
return messages;
|
|
795
992
|
}
|
|
796
|
-
function convertResponsesMessages
|
|
993
|
+
function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
797
994
|
return convertResponsesMessagesWithStyle(model, context, allowedToolCallProviders, options, "transport");
|
|
798
995
|
}
|
|
799
996
|
function convertProviderResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
@@ -826,7 +1023,7 @@ function contextFingerprint(input, output = []) {
|
|
|
826
1023
|
function recordResponsesContextUsage(message, model, identity, request, output, projection) {
|
|
827
1024
|
const usage = message.usage.contextUsage;
|
|
828
1025
|
if (usage?.state !== "available" || !Number.isSafeInteger(usage.totalTokens) || usage.totalTokens <= 0 || message.stopReason === "error" || message.stopReason === "aborted" || message.providerReplay || request.previous_response_id || !Array.isArray(request.input) || !request.input.some((item) => isRecord(item) && item.type === "compaction") || !Array.isArray(output) || output.some((item) => isRecord(item) && item.type === "compaction")) return;
|
|
829
|
-
const replayOutput = (projection === "transport" ? convertResponsesMessages
|
|
1026
|
+
const replayOutput = (projection === "transport" ? convertResponsesMessages : convertProviderResponsesMessages)(model, { messages: [message] }, TOOL_CALL_PROVIDERS, {
|
|
830
1027
|
...identity,
|
|
831
1028
|
includeSystemPrompt: false
|
|
832
1029
|
}).filter((item) => item.type !== "function_call_output");
|
|
@@ -855,7 +1052,7 @@ function resolveResponsesContextUsageBoundary(messages, model, identity, systemP
|
|
|
855
1052
|
if (!isRecord(state) || usage?.state !== "available") continue;
|
|
856
1053
|
const { projection, prefixHash, prefixLength, promptTokens, totalTokens, includeSystemPrompt } = state;
|
|
857
1054
|
if (!isOpenAIResponsesReplayContext(state) || !providerReplayContextMatches(state, buildProviderReplayContext(model, identity)) || message.provider !== model.provider || message.api !== model.api || message.model !== model.id || projection !== "transport" && projection !== "provider" || typeof prefixHash !== "string" || typeof prefixLength !== "number" || !Number.isSafeInteger(prefixLength) || prefixLength <= 0 || promptTokens !== usage.promptTokens || totalTokens !== usage.totalTokens || !Number.isSafeInteger(usage.totalTokens) || usage.totalTokens <= 0) return;
|
|
858
|
-
const input = (projection === "transport" ? convertResponsesMessages
|
|
1055
|
+
const input = (projection === "transport" ? convertResponsesMessages : convertProviderResponsesMessages)(model, {
|
|
859
1056
|
messages: messages.filter(isProviderMessage),
|
|
860
1057
|
systemPrompt
|
|
861
1058
|
}, TOOL_CALL_PROVIDERS, {
|
|
@@ -877,342 +1074,88 @@ function isProviderMessage(message) {
|
|
|
877
1074
|
return message.role === "user" || message.role === "assistant" || message.role === "toolResult";
|
|
878
1075
|
}
|
|
879
1076
|
//#endregion
|
|
880
|
-
//#region packages/ai/src/transports/openai-responses-
|
|
881
|
-
function
|
|
882
|
-
|
|
1077
|
+
//#region packages/ai/src/transports/openai-responses-debug.ts
|
|
1078
|
+
function stringifyUnknown(value, fallback = "") {
|
|
1079
|
+
if (typeof value === "string") return value;
|
|
1080
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1081
|
+
return fallback;
|
|
883
1082
|
}
|
|
884
|
-
function
|
|
885
|
-
if (
|
|
886
|
-
|
|
887
|
-
if (
|
|
888
|
-
|
|
889
|
-
return
|
|
1083
|
+
function safeDebugValue(value) {
|
|
1084
|
+
if (typeof value === "string") return value;
|
|
1085
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1086
|
+
if (value === null) return "null";
|
|
1087
|
+
if (value === void 0) return "undefined";
|
|
1088
|
+
return Array.isArray(value) ? "array" : typeof value;
|
|
890
1089
|
}
|
|
891
|
-
function
|
|
892
|
-
if (
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1090
|
+
function responseInputTextChars(input) {
|
|
1091
|
+
if (typeof input === "string") return input.length;
|
|
1092
|
+
if (Array.isArray(input)) return input.reduce((total, item) => total + responseInputTextChars(item), 0);
|
|
1093
|
+
if (!input || typeof input !== "object") return 0;
|
|
1094
|
+
const record = input;
|
|
1095
|
+
let total = 0;
|
|
1096
|
+
if (typeof record.text === "string") total += record.text.length;
|
|
1097
|
+
if (typeof record.content === "string") total += record.content.length;
|
|
1098
|
+
else if (Array.isArray(record.content)) total += responseInputTextChars(record.content);
|
|
1099
|
+
return total;
|
|
896
1100
|
}
|
|
897
|
-
function
|
|
898
|
-
if (
|
|
1101
|
+
function responseInputRoles(input) {
|
|
1102
|
+
if (!Array.isArray(input)) return "";
|
|
1103
|
+
const roles = /* @__PURE__ */ new Set();
|
|
1104
|
+
for (const item of input) if (item && typeof item === "object") {
|
|
1105
|
+
const role = item.role;
|
|
1106
|
+
if (typeof role === "string" && role.trim()) roles.add(role.trim());
|
|
1107
|
+
}
|
|
1108
|
+
return [...roles].toSorted().join(",");
|
|
899
1109
|
}
|
|
900
|
-
function
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
};
|
|
1110
|
+
function responseInputItemShape(input) {
|
|
1111
|
+
if (!Array.isArray(input)) return "none";
|
|
1112
|
+
return input.map((item) => {
|
|
1113
|
+
if (!isRecord(item) || typeof item.type !== "string") return "unknown";
|
|
1114
|
+
if (item.type === "message" && typeof item.role === "string") return `message:${item.role}`;
|
|
1115
|
+
return item.type;
|
|
1116
|
+
}).join(",") || "none";
|
|
907
1117
|
}
|
|
908
|
-
function
|
|
909
|
-
if (!Array.isArray(
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1118
|
+
function summarizeResponsesCompactionItems(input) {
|
|
1119
|
+
if (!Array.isArray(input)) return [
|
|
1120
|
+
"compactionItems=0",
|
|
1121
|
+
"compactionIdHashes=none",
|
|
1122
|
+
"compactionPayloadHashes=none",
|
|
1123
|
+
"compactionInputIndexes=none"
|
|
1124
|
+
];
|
|
1125
|
+
const compactions = input.flatMap((item, inputIndex) => {
|
|
1126
|
+
if (!isRecord(item) || item.type !== "compaction") return [];
|
|
1127
|
+
return [{
|
|
1128
|
+
idHash: typeof item.id === "string" ? sha256Hex(item.id) : void 0,
|
|
1129
|
+
inputIndex,
|
|
1130
|
+
payloadHash: typeof item.encrypted_content === "string" ? sha256Hex(item.encrypted_content) : void 0
|
|
1131
|
+
}];
|
|
1132
|
+
});
|
|
1133
|
+
return [
|
|
1134
|
+
`compactionItems=${compactions.length}`,
|
|
1135
|
+
`compactionIdHashes=${compactions.flatMap((item) => item.idHash ?? []).join(",") || "none"}`,
|
|
1136
|
+
`compactionPayloadHashes=${compactions.flatMap((item) => item.payloadHash ?? []).join(",") || "none"}`,
|
|
1137
|
+
`compactionInputIndexes=${compactions.map((item) => item.inputIndex).join(",") || "none"}`
|
|
1138
|
+
];
|
|
915
1139
|
}
|
|
916
|
-
function
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
} : void 0;
|
|
1140
|
+
function readToolPayloadField(record, field) {
|
|
1141
|
+
try {
|
|
1142
|
+
return record[field];
|
|
1143
|
+
} catch {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
923
1146
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1147
|
+
function readResponsesToolDisplayName(tool) {
|
|
1148
|
+
if (!tool || typeof tool !== "object") return "";
|
|
1149
|
+
const record = tool;
|
|
1150
|
+
const name = readToolPayloadField(record, "name");
|
|
1151
|
+
if (typeof name === "string") return name;
|
|
1152
|
+
const fn = readToolPayloadField(record, "function");
|
|
1153
|
+
if (fn && typeof fn === "object") {
|
|
1154
|
+
const fnName = readToolPayloadField(fn, "name");
|
|
1155
|
+
if (typeof fnName === "string") return fnName;
|
|
933
1156
|
}
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
let compactionStripped = options?.buildFullHistoryRequest ? await options.buildFullHistoryRequest() : locallyStripped;
|
|
937
|
-
compactionStripped = stripResponsesRequestCompaction(compactionStripped);
|
|
938
|
-
if (attempt.kind === "reasoning-stripped") compactionStripped = stripResponsesRequestEncryptedReasoning(compactionStripped);
|
|
939
|
-
return {
|
|
940
|
-
kind: "compaction-stripped",
|
|
941
|
-
request: compactionStripped,
|
|
942
|
-
rejectedCompaction: readOpenAIResponsesCompactionRejection(attempt.request)
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
async function createResponsesStreamWithEncryptedContentRetry(params) {
|
|
946
|
-
const send = async (initialAttempt) => {
|
|
947
|
-
let attempt = initialAttempt;
|
|
948
|
-
for (;;) {
|
|
949
|
-
params.observePrompt?.(attempt.request, {
|
|
950
|
-
egress: "responses-sdk",
|
|
951
|
-
payloadVariant: attempt.kind
|
|
952
|
-
});
|
|
953
|
-
try {
|
|
954
|
-
const { data, response } = await params.client.responses.create(attempt.request, params.requestOptions).withResponse();
|
|
955
|
-
commitResponsesEncryptedContentAttempt(attempt, (checkpoint) => {
|
|
956
|
-
if (checkpoint) params.onCompactionRejected?.(checkpoint);
|
|
957
|
-
});
|
|
958
|
-
if (!isAsyncIterable(data)) throw new Error("OpenAI Responses streaming request returned a non-stream response");
|
|
959
|
-
return {
|
|
960
|
-
stream: data,
|
|
961
|
-
response,
|
|
962
|
-
attempt
|
|
963
|
-
};
|
|
964
|
-
} catch (error) {
|
|
965
|
-
let nextAttempt = await resolveNextResponsesEncryptedContentAttempt(attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest });
|
|
966
|
-
if (!nextAttempt && attempt.request.previous_response_id && error && typeof error === "object" && typeof error.status === "number" && isPreviousResponseRejection(error)) {
|
|
967
|
-
const request = { ...params.buildFullHistoryRequest ? await params.buildFullHistoryRequest() : attempt.request };
|
|
968
|
-
delete request.previous_response_id;
|
|
969
|
-
nextAttempt = {
|
|
970
|
-
kind: "continuation-rejected",
|
|
971
|
-
request
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
if (!nextAttempt) throw error;
|
|
975
|
-
const retryDescription = nextAttempt.kind === "reasoning-stripped" ? "without encrypted reasoning content" : nextAttempt.kind === "compaction-stripped" ? "without encrypted compaction content" : "full history after rejected previous_response_id";
|
|
976
|
-
log.warn(`[responses] retrying ${retryDescription} provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
977
|
-
attempt = nextAttempt;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
};
|
|
981
|
-
const result = await send({
|
|
982
|
-
kind: params.initialAttemptKind ?? "initial",
|
|
983
|
-
request: params.request,
|
|
984
|
-
...params.initialRejectedCompaction ? { rejectedCompaction: params.initialRejectedCompaction } : {}
|
|
985
|
-
});
|
|
986
|
-
return { stream: { async *[Symbol.asyncIterator]() {
|
|
987
|
-
let current = result;
|
|
988
|
-
for (;;) {
|
|
989
|
-
let rejectedEvent;
|
|
990
|
-
try {
|
|
991
|
-
for await (const event of params.wrapStream?.(current) ?? current.stream) {
|
|
992
|
-
if (isRecord(event)) {
|
|
993
|
-
const failure = event.type === "response.failed" && isRecord(event.response) ? event.response.error : event.type === "error" ? event.error ?? event : void 0;
|
|
994
|
-
if (isRecord(failure) && params.canRetryStream?.() === true && isInvalidEncryptedContentError(failure)) {
|
|
995
|
-
rejectedEvent = event;
|
|
996
|
-
const message = typeof failure.message === "string" ? failure.message : "";
|
|
997
|
-
throw Object.assign(new Error(message), {
|
|
998
|
-
code: failure.code,
|
|
999
|
-
status: failure.status
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
yield event;
|
|
1004
|
-
}
|
|
1005
|
-
return;
|
|
1006
|
-
} catch (error) {
|
|
1007
|
-
const nextAttempt = params.canRetryStream?.() === true && !params.requestOptions?.signal?.aborted ? await resolveNextResponsesEncryptedContentAttempt(current.attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest }) : void 0;
|
|
1008
|
-
if (!nextAttempt) {
|
|
1009
|
-
if (rejectedEvent !== void 0) {
|
|
1010
|
-
yield rejectedEvent;
|
|
1011
|
-
return;
|
|
1012
|
-
}
|
|
1013
|
-
throw error;
|
|
1014
|
-
}
|
|
1015
|
-
log.warn(`[responses] retrying streamed encrypted content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
1016
|
-
current = await send(nextAttempt);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
} } };
|
|
1020
|
-
}
|
|
1021
|
-
function resolveAzureOpenAIApiVersion(env = process.env) {
|
|
1022
|
-
return env.AZURE_OPENAI_API_VERSION?.trim() || "preview";
|
|
1023
|
-
}
|
|
1024
|
-
//#endregion
|
|
1025
|
-
//#region packages/ai/src/providers/openai-responses-stream-compat.ts
|
|
1026
|
-
const OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE = "output_text";
|
|
1027
|
-
const AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE = "text";
|
|
1028
|
-
const OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE = "response.output_text.delta";
|
|
1029
|
-
const AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE = "response.text.delta";
|
|
1030
|
-
function isResponsesTextContentPartType(type) {
|
|
1031
|
-
return type === "output_text" || type === "text";
|
|
1032
|
-
}
|
|
1033
|
-
function isResponsesTextDeltaEventType(type) {
|
|
1034
|
-
return type === "response.output_text.delta" || type === "response.text.delta";
|
|
1035
|
-
}
|
|
1036
|
-
function isAzureResponsesTextDeltaEventType(type) {
|
|
1037
|
-
return type === AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE;
|
|
1038
|
-
}
|
|
1039
|
-
function isAzureResponsesTextDeltaEvent(event) {
|
|
1040
|
-
return isAzureResponsesTextDeltaEventType(event.type) && typeof event.delta === "string";
|
|
1041
|
-
}
|
|
1042
|
-
function resolveResponsesMessageSnapshotCollapse(params) {
|
|
1043
|
-
const { prior, nextText } = params;
|
|
1044
|
-
if (!prior?.text || !nextText || prior.phase !== params.nextPhase) return { kind: "keep" };
|
|
1045
|
-
if (nextText.length > prior.text.length && nextText.startsWith(prior.text)) return {
|
|
1046
|
-
kind: "extend",
|
|
1047
|
-
text: nextText
|
|
1048
|
-
};
|
|
1049
|
-
return { kind: "keep" };
|
|
1050
|
-
}
|
|
1051
|
-
//#endregion
|
|
1052
|
-
//#region packages/ai/src/providers/openai-responses-tool-call-tracker.ts
|
|
1053
|
-
function readOutputIndex(event) {
|
|
1054
|
-
return typeof event.output_index === "number" && Number.isInteger(event.output_index) && event.output_index >= 0 ? event.output_index : void 0;
|
|
1055
|
-
}
|
|
1056
|
-
function readEventIdentity(event) {
|
|
1057
|
-
return { itemId: normalizeOptionalString(event.item_id) };
|
|
1058
|
-
}
|
|
1059
|
-
function readResponsesToolCallItemIdentity(item) {
|
|
1060
|
-
return {
|
|
1061
|
-
itemId: normalizeOptionalString(item.id),
|
|
1062
|
-
callId: normalizeOptionalString(item.call_id)
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
function createResponsesToolCallTracker() {
|
|
1066
|
-
const indexedCalls = /* @__PURE__ */ new Map();
|
|
1067
|
-
const unindexedCalls = /* @__PURE__ */ new Set();
|
|
1068
|
-
const identitiesConflict = (state, identity) => Boolean(state.itemId && identity.itemId && state.itemId !== identity.itemId || state.callId && identity.callId && state.callId !== identity.callId);
|
|
1069
|
-
const sharesIdentity = (state, identity) => Boolean(state.itemId && identity.itemId && state.itemId === identity.itemId || state.callId && identity.callId && state.callId === identity.callId);
|
|
1070
|
-
const adoptIdentity = (state, identity) => {
|
|
1071
|
-
state.itemId ??= identity.itemId;
|
|
1072
|
-
state.callId ??= identity.callId;
|
|
1073
|
-
return state;
|
|
1074
|
-
};
|
|
1075
|
-
const resolveCompatible = (candidates, identity, allowUnmatchedIdentity) => {
|
|
1076
|
-
const uniqueCandidates = [...new Set(candidates)];
|
|
1077
|
-
if (!identity.itemId && !identity.callId) return allowUnmatchedIdentity && uniqueCandidates.length === 1 ? uniqueCandidates.at(0) : void 0;
|
|
1078
|
-
const compatible = uniqueCandidates.filter((state) => !identitiesConflict(state, identity));
|
|
1079
|
-
const matches = compatible.filter((state) => sharesIdentity(state, identity));
|
|
1080
|
-
const matched = matches.length === 1 ? matches.at(0) : void 0;
|
|
1081
|
-
if (matched) return adoptIdentity(matched, identity);
|
|
1082
|
-
const soleCompatible = allowUnmatchedIdentity && uniqueCandidates.length === 1 && compatible.length === 1 && matches.length === 0 ? compatible.at(0) : void 0;
|
|
1083
|
-
return soleCompatible ? adoptIdentity(soleCompatible, identity) : void 0;
|
|
1084
|
-
};
|
|
1085
|
-
return {
|
|
1086
|
-
register(event, state) {
|
|
1087
|
-
const outputIndex = readOutputIndex(event);
|
|
1088
|
-
if (outputIndex === void 0) {
|
|
1089
|
-
unindexedCalls.add(state);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
if (indexedCalls.has(outputIndex)) throw new Error(`Responses stream reused active tool-call output index ${outputIndex}`);
|
|
1093
|
-
state.outputIndex = outputIndex;
|
|
1094
|
-
indexedCalls.set(outputIndex, state);
|
|
1095
|
-
},
|
|
1096
|
-
resolve(event, identity = readEventIdentity(event), allowUnmatchedIdentity = true) {
|
|
1097
|
-
const outputIndex = readOutputIndex(event);
|
|
1098
|
-
if (outputIndex !== void 0) {
|
|
1099
|
-
const indexed = indexedCalls.get(outputIndex);
|
|
1100
|
-
if (indexed) {
|
|
1101
|
-
if (indexed.callId && identity.callId && indexed.callId !== identity.callId) return;
|
|
1102
|
-
return adoptIdentity(indexed, identity);
|
|
1103
|
-
}
|
|
1104
|
-
const unindexed = resolveCompatible(unindexedCalls, identity, allowUnmatchedIdentity);
|
|
1105
|
-
if (unindexed) {
|
|
1106
|
-
unindexedCalls.delete(unindexed);
|
|
1107
|
-
unindexed.outputIndex = outputIndex;
|
|
1108
|
-
indexedCalls.set(outputIndex, unindexed);
|
|
1109
|
-
}
|
|
1110
|
-
return unindexed;
|
|
1111
|
-
}
|
|
1112
|
-
return resolveCompatible([...indexedCalls.values(), ...unindexedCalls], identity, allowUnmatchedIdentity);
|
|
1113
|
-
},
|
|
1114
|
-
forget(toolCall) {
|
|
1115
|
-
for (const [outputIndex, tracked] of indexedCalls) if (tracked === toolCall) indexedCalls.delete(outputIndex);
|
|
1116
|
-
unindexedCalls.delete(toolCall);
|
|
1117
|
-
},
|
|
1118
|
-
markArgumentsUnreliable() {
|
|
1119
|
-
for (const toolCall of /* @__PURE__ */ new Set([...indexedCalls.values(), ...unindexedCalls])) toolCall.argumentStreamReliable = false;
|
|
1120
|
-
},
|
|
1121
|
-
hasActive() {
|
|
1122
|
-
return indexedCalls.size > 0 || unindexedCalls.size > 0;
|
|
1123
|
-
},
|
|
1124
|
-
hasExactlyActive(expected) {
|
|
1125
|
-
const active = /* @__PURE__ */ new Set([...indexedCalls.values(), ...unindexedCalls]);
|
|
1126
|
-
return active.size === expected.length && expected.every((state) => active.has(state));
|
|
1127
|
-
}
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
//#endregion
|
|
1131
|
-
//#region packages/ai/src/transports/openai-responses-debug.ts
|
|
1132
|
-
function stringifyUnknown(value, fallback = "") {
|
|
1133
|
-
if (typeof value === "string") return value;
|
|
1134
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1135
|
-
return fallback;
|
|
1136
|
-
}
|
|
1137
|
-
function safeDebugValue(value) {
|
|
1138
|
-
if (typeof value === "string") return value;
|
|
1139
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1140
|
-
if (value === null) return "null";
|
|
1141
|
-
if (value === void 0) return "undefined";
|
|
1142
|
-
return Array.isArray(value) ? "array" : typeof value;
|
|
1143
|
-
}
|
|
1144
|
-
function responseInputTextChars(input) {
|
|
1145
|
-
if (typeof input === "string") return input.length;
|
|
1146
|
-
if (Array.isArray(input)) return input.reduce((total, item) => total + responseInputTextChars(item), 0);
|
|
1147
|
-
if (!input || typeof input !== "object") return 0;
|
|
1148
|
-
const record = input;
|
|
1149
|
-
let total = 0;
|
|
1150
|
-
if (typeof record.text === "string") total += record.text.length;
|
|
1151
|
-
if (typeof record.content === "string") total += record.content.length;
|
|
1152
|
-
else if (Array.isArray(record.content)) total += responseInputTextChars(record.content);
|
|
1153
|
-
return total;
|
|
1154
|
-
}
|
|
1155
|
-
function responseInputRoles(input) {
|
|
1156
|
-
if (!Array.isArray(input)) return "";
|
|
1157
|
-
const roles = /* @__PURE__ */ new Set();
|
|
1158
|
-
for (const item of input) if (item && typeof item === "object") {
|
|
1159
|
-
const role = item.role;
|
|
1160
|
-
if (typeof role === "string" && role.trim()) roles.add(role.trim());
|
|
1161
|
-
}
|
|
1162
|
-
return [...roles].toSorted().join(",");
|
|
1163
|
-
}
|
|
1164
|
-
function responseInputItemShape(input) {
|
|
1165
|
-
if (!Array.isArray(input)) return "none";
|
|
1166
|
-
return input.map((item) => {
|
|
1167
|
-
if (!isRecord(item) || typeof item.type !== "string") return "unknown";
|
|
1168
|
-
if (item.type === "message" && typeof item.role === "string") return `message:${item.role}`;
|
|
1169
|
-
return item.type;
|
|
1170
|
-
}).join(",") || "none";
|
|
1171
|
-
}
|
|
1172
|
-
function hashOpaqueResponsesValue(value) {
|
|
1173
|
-
return createHash("sha256").update(value).digest("hex");
|
|
1174
|
-
}
|
|
1175
|
-
function summarizeResponsesCompactionItems(input) {
|
|
1176
|
-
if (!Array.isArray(input)) return [
|
|
1177
|
-
"compactionItems=0",
|
|
1178
|
-
"compactionIdHashes=none",
|
|
1179
|
-
"compactionPayloadHashes=none",
|
|
1180
|
-
"compactionInputIndexes=none"
|
|
1181
|
-
];
|
|
1182
|
-
const compactions = input.flatMap((item, inputIndex) => {
|
|
1183
|
-
if (!isRecord(item) || item.type !== "compaction") return [];
|
|
1184
|
-
return [{
|
|
1185
|
-
idHash: typeof item.id === "string" ? hashOpaqueResponsesValue(item.id) : void 0,
|
|
1186
|
-
inputIndex,
|
|
1187
|
-
payloadHash: typeof item.encrypted_content === "string" ? hashOpaqueResponsesValue(item.encrypted_content) : void 0
|
|
1188
|
-
}];
|
|
1189
|
-
});
|
|
1190
|
-
return [
|
|
1191
|
-
`compactionItems=${compactions.length}`,
|
|
1192
|
-
`compactionIdHashes=${compactions.flatMap((item) => item.idHash ?? []).join(",") || "none"}`,
|
|
1193
|
-
`compactionPayloadHashes=${compactions.flatMap((item) => item.payloadHash ?? []).join(",") || "none"}`,
|
|
1194
|
-
`compactionInputIndexes=${compactions.map((item) => item.inputIndex).join(",") || "none"}`
|
|
1195
|
-
];
|
|
1196
|
-
}
|
|
1197
|
-
function readToolPayloadField(record, field) {
|
|
1198
|
-
try {
|
|
1199
|
-
return record[field];
|
|
1200
|
-
} catch {
|
|
1201
|
-
return;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
function readResponsesToolDisplayName(tool) {
|
|
1205
|
-
if (!tool || typeof tool !== "object") return "";
|
|
1206
|
-
const record = tool;
|
|
1207
|
-
const name = readToolPayloadField(record, "name");
|
|
1208
|
-
if (typeof name === "string") return name;
|
|
1209
|
-
const fn = readToolPayloadField(record, "function");
|
|
1210
|
-
if (fn && typeof fn === "object") {
|
|
1211
|
-
const fnName = readToolPayloadField(fn, "name");
|
|
1212
|
-
if (typeof fnName === "string") return fnName;
|
|
1213
|
-
}
|
|
1214
|
-
const type = readToolPayloadField(record, "type");
|
|
1215
|
-
return typeof type === "string" && type !== "function" ? type : "";
|
|
1157
|
+
const type = readToolPayloadField(record, "type");
|
|
1158
|
+
return typeof type === "string" && type !== "function" ? type : "";
|
|
1216
1159
|
}
|
|
1217
1160
|
function summarizeResponsesTools(tools) {
|
|
1218
1161
|
if (!Array.isArray(tools)) return "count=0";
|
|
@@ -1305,131 +1248,391 @@ function collectResponseFailedIdentifierHashes(value, opts = {}) {
|
|
|
1305
1248
|
}
|
|
1306
1249
|
return out;
|
|
1307
1250
|
}
|
|
1308
|
-
function redactResponseFailedDiagnosticValue(value, opts = {}) {
|
|
1309
|
-
const key = opts.key ?? "";
|
|
1310
|
-
const depth = opts.depth ?? 0;
|
|
1311
|
-
if (typeof value === "string" || typeof value === "number") return key && isResponseFailedIdentifierKey(key) ? redactIdentifier(String(value), { len: 12 }) : value;
|
|
1312
|
-
if (depth > 6 || !value || typeof value !== "object") return value;
|
|
1313
|
-
const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
|
|
1314
|
-
if (seen.has(value)) return "<circular>";
|
|
1315
|
-
seen.add(value);
|
|
1316
|
-
if (Array.isArray(value)) return value.slice(0, 16).map((item) => redactResponseFailedDiagnosticValue(item, {
|
|
1317
|
-
key,
|
|
1318
|
-
depth: depth + 1,
|
|
1319
|
-
seen
|
|
1320
|
-
}));
|
|
1321
|
-
const out = {};
|
|
1322
|
-
for (const [childKey, child] of Object.entries(value)) out[childKey] = redactResponseFailedDiagnosticValue(child, {
|
|
1323
|
-
key: childKey,
|
|
1324
|
-
depth: depth + 1,
|
|
1325
|
-
seen
|
|
1326
|
-
});
|
|
1327
|
-
return out;
|
|
1251
|
+
function redactResponseFailedDiagnosticValue(value, opts = {}) {
|
|
1252
|
+
const key = opts.key ?? "";
|
|
1253
|
+
const depth = opts.depth ?? 0;
|
|
1254
|
+
if (typeof value === "string" || typeof value === "number") return key && isResponseFailedIdentifierKey(key) ? redactIdentifier(String(value), { len: 12 }) : value;
|
|
1255
|
+
if (depth > 6 || !value || typeof value !== "object") return value;
|
|
1256
|
+
const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
|
|
1257
|
+
if (seen.has(value)) return "<circular>";
|
|
1258
|
+
seen.add(value);
|
|
1259
|
+
if (Array.isArray(value)) return value.slice(0, 16).map((item) => redactResponseFailedDiagnosticValue(item, {
|
|
1260
|
+
key,
|
|
1261
|
+
depth: depth + 1,
|
|
1262
|
+
seen
|
|
1263
|
+
}));
|
|
1264
|
+
const out = {};
|
|
1265
|
+
for (const [childKey, child] of Object.entries(value)) out[childKey] = redactResponseFailedDiagnosticValue(child, {
|
|
1266
|
+
key: childKey,
|
|
1267
|
+
depth: depth + 1,
|
|
1268
|
+
seen
|
|
1269
|
+
});
|
|
1270
|
+
return out;
|
|
1271
|
+
}
|
|
1272
|
+
function buildResponsesFailedFailureFields(response) {
|
|
1273
|
+
if (!response) return {};
|
|
1274
|
+
const fields = {};
|
|
1275
|
+
for (const key of RESPONSE_FAILED_FAILURE_FIELD_KEYS) if (response[key] !== void 0 && response[key] !== null) fields[key] = response[key];
|
|
1276
|
+
return fields;
|
|
1277
|
+
}
|
|
1278
|
+
function buildResponsesFailedNoDetailsObservation(event, model, response = isRecord(event.response) ? event.response : void 0) {
|
|
1279
|
+
const failureFields = redactResponseFailedDiagnosticValue(buildResponsesFailedFailureFields(response));
|
|
1280
|
+
const metadataKeys = isRecord(response?.metadata) ? Object.keys(response.metadata).toSorted() : [];
|
|
1281
|
+
const responsePreview = {
|
|
1282
|
+
id: readResponseFailedString(response, "id"),
|
|
1283
|
+
status: readResponseFailedString(response, "status"),
|
|
1284
|
+
model: readResponseFailedString(response, "model"),
|
|
1285
|
+
object: readResponseFailedString(response, "object"),
|
|
1286
|
+
failureFields,
|
|
1287
|
+
metadataKeys
|
|
1288
|
+
};
|
|
1289
|
+
return {
|
|
1290
|
+
event: "openai_responses_response_failed_without_details",
|
|
1291
|
+
provider: model.provider,
|
|
1292
|
+
api: model.api,
|
|
1293
|
+
transportModel: model.id,
|
|
1294
|
+
providerRuntimeFailureKind: "no_error_details",
|
|
1295
|
+
responseId: responsePreview.id,
|
|
1296
|
+
responseStatus: responsePreview.status,
|
|
1297
|
+
responseModel: responsePreview.model,
|
|
1298
|
+
responseObject: responsePreview.object,
|
|
1299
|
+
metadataKeys,
|
|
1300
|
+
requestIdHashes: collectResponseFailedIdentifierHashes(event),
|
|
1301
|
+
failureFieldsPreview: stringifyRedactedEvent(failureFields),
|
|
1302
|
+
responsePreview: stringifyRedactedEvent(responsePreview)
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
function summarizeResponsesFailedNoDetailsObservation(observation) {
|
|
1306
|
+
const requestIds = observation.requestIdHashes.join(",");
|
|
1307
|
+
const metadataKeys = observation.metadataKeys.join(",");
|
|
1308
|
+
return `responseId=${safeDebugValue(observation.responseId || void 0)} responseStatus=${safeDebugValue(observation.responseStatus || void 0)} responseModel=${safeDebugValue(observation.responseModel || void 0)} requestIds=${requestIds || "none"} metadataKeys=${metadataKeys || "none"} failureFields=${observation.failureFieldsPreview}`;
|
|
1309
|
+
}
|
|
1310
|
+
function normalizeResponsesFailedEvent(event, model) {
|
|
1311
|
+
const response = isRecord(event.response) ? event.response : void 0;
|
|
1312
|
+
const responseId = readResponseFailedString(response, "id") || void 0;
|
|
1313
|
+
const error = isRecord(response?.error) ? response.error : void 0;
|
|
1314
|
+
if (error) {
|
|
1315
|
+
const code = readResponseFailedString(error, "code").trim();
|
|
1316
|
+
const message = readResponseFailedString(error, "message").trim();
|
|
1317
|
+
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId, code || void 0);
|
|
1318
|
+
}
|
|
1319
|
+
const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
|
|
1320
|
+
if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
|
|
1321
|
+
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, void 0, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
1322
|
+
}
|
|
1323
|
+
var ResponsesStreamFailure = class extends Error {
|
|
1324
|
+
constructor(failure, response) {
|
|
1325
|
+
super(failure.message);
|
|
1326
|
+
this.name = "ResponsesStreamFailure";
|
|
1327
|
+
this.responseId = failure.responseId;
|
|
1328
|
+
this.response = response;
|
|
1329
|
+
this.code = failure.code;
|
|
1330
|
+
this.observation = failure.observation;
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
function logResponsesFailedNoDetails(observation) {
|
|
1334
|
+
log.warn(`[responses] response.failed missing error details provider=${observation.provider} api=${observation.api} model=${observation.transportModel} ` + summarizeResponsesFailedNoDetailsObservation(observation), observation);
|
|
1335
|
+
}
|
|
1336
|
+
function summarizeResponsesPayload(params) {
|
|
1337
|
+
if (!params || typeof params !== "object") return "payload=non-object";
|
|
1338
|
+
const record = params;
|
|
1339
|
+
const input = record.input;
|
|
1340
|
+
const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
|
|
1341
|
+
const text = record.text && typeof record.text === "object" ? record.text : void 0;
|
|
1342
|
+
const parts = [
|
|
1343
|
+
`fields=${Object.keys(record).toSorted().join(",")}`,
|
|
1344
|
+
`model=${safeDebugValue(record.model)}`,
|
|
1345
|
+
`stream=${safeDebugValue(record.stream)}`,
|
|
1346
|
+
`inputItems=${Array.isArray(input) ? input.length : typeof input}`,
|
|
1347
|
+
`inputItemShape=${responseInputItemShape(input)}`,
|
|
1348
|
+
`inputRoles=${responseInputRoles(input) || "none"}`,
|
|
1349
|
+
`inputTextChars=${responseInputTextChars(input)}`,
|
|
1350
|
+
`tools=${summarizeResponsesTools(record.tools)}`,
|
|
1351
|
+
`reasoningEffort=${safeDebugValue(reasoning?.effort)}`,
|
|
1352
|
+
`reasoningSummary=${safeDebugValue(reasoning?.summary)}`,
|
|
1353
|
+
`textVerbosity=${safeDebugValue(text?.verbosity)}`,
|
|
1354
|
+
`serviceTier=${safeDebugValue(record.service_tier)}`,
|
|
1355
|
+
...summarizeResponsesCompactionItems(input),
|
|
1356
|
+
`store=${safeDebugValue(record.store)}`,
|
|
1357
|
+
`promptCacheKey=${record.prompt_cache_key === void 0 ? "absent" : "present"}`,
|
|
1358
|
+
`metadataKeys=${record.metadata && typeof record.metadata === "object" ? Object.keys(record.metadata).toSorted().join(",") : "none"}`
|
|
1359
|
+
];
|
|
1360
|
+
if (resolveModelPayloadDebugMode() === "full-redacted") parts.push(`payload=${stringifyRedactedPayload(record)}`);
|
|
1361
|
+
return parts.join(" ");
|
|
1362
|
+
}
|
|
1363
|
+
function summarizeOpenAITransportError(error) {
|
|
1364
|
+
if (!error || typeof error !== "object") return `type=${typeof error} message=${safeDebugValue(error)}`;
|
|
1365
|
+
const record = error;
|
|
1366
|
+
const cause = record.cause && typeof record.cause === "object" ? record.cause : void 0;
|
|
1367
|
+
return [
|
|
1368
|
+
`name=${safeDebugValue(record.name)}`,
|
|
1369
|
+
`status=${safeDebugValue(record.status)}`,
|
|
1370
|
+
`code=${safeDebugValue(record.code)}`,
|
|
1371
|
+
`type=${safeDebugValue(record.type)}`,
|
|
1372
|
+
`causeName=${safeDebugValue(cause?.name)}`,
|
|
1373
|
+
`causeCode=${safeDebugValue(cause?.code)}`,
|
|
1374
|
+
`message=${error instanceof Error ? error.message : safeDebugValue(error)}`
|
|
1375
|
+
].join(" ");
|
|
1376
|
+
}
|
|
1377
|
+
//#endregion
|
|
1378
|
+
//#region packages/ai/src/transports/openai-responses-replay-internal.ts
|
|
1379
|
+
function isAsyncIterable(value) {
|
|
1380
|
+
return (typeof value === "object" && value !== null || typeof value === "function") && Symbol.asyncIterator in value;
|
|
1381
|
+
}
|
|
1382
|
+
function isInvalidEncryptedContentError(error) {
|
|
1383
|
+
if (!error || typeof error !== "object") return false;
|
|
1384
|
+
const record = error;
|
|
1385
|
+
if (record.code === "invalid_encrypted_content" || record.code === "thinking_signature_invalid") return true;
|
|
1386
|
+
const message = typeof record.message === "string" ? record.message.toLowerCase() : "";
|
|
1387
|
+
return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || (record.status === 400 || record.status == null && /(?:^|[\s(])400(?:[\s):]|$)/.test(message)) && (message.includes("could not decrypt the provided encrypted_content") || message.includes("encrypted content") && (message.includes("could not be verified") || message.includes("could not be decrypted or parsed")));
|
|
1388
|
+
}
|
|
1389
|
+
function isOrphanedFunctionCallOutputError(error) {
|
|
1390
|
+
if (!error || typeof error !== "object") return false;
|
|
1391
|
+
const record = error;
|
|
1392
|
+
const message = typeof record.message === "string" ? record.message : "";
|
|
1393
|
+
return /No tool call found for function call output with call_id [A-Za-z0-9_-]+/.test(message);
|
|
1394
|
+
}
|
|
1395
|
+
function commitResponsesEncryptedContentAttempt(attempt, commit) {
|
|
1396
|
+
if (attempt.kind === "compaction-stripped") commit(attempt.rejectedCompaction);
|
|
1397
|
+
}
|
|
1398
|
+
function stripResponsesRequestEncryptedReasoning(request) {
|
|
1399
|
+
const stripped = stripEncryptedReasoningContentFields(request.input);
|
|
1400
|
+
if (!stripped.changed) return request;
|
|
1401
|
+
return {
|
|
1402
|
+
...request,
|
|
1403
|
+
input: stripped.value
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
function stripResponsesRequestCompaction(request) {
|
|
1407
|
+
if (!Array.isArray(request.input)) return request;
|
|
1408
|
+
const input = request.input.filter((item) => !(item !== null && typeof item === "object" && item.type === "compaction"));
|
|
1409
|
+
return input.length === request.input.length ? request : {
|
|
1410
|
+
...request,
|
|
1411
|
+
input
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
function readOpenAIResponsesCompactionRejection(request) {
|
|
1415
|
+
if (!Array.isArray(request.input)) return;
|
|
1416
|
+
const item = request.input.find((candidate) => candidate !== null && typeof candidate === "object" && candidate.type === "compaction" && typeof candidate.encrypted_content === "string");
|
|
1417
|
+
return item ? {
|
|
1418
|
+
data: item.encrypted_content,
|
|
1419
|
+
...typeof item.id === "string" ? { id: item.id } : {}
|
|
1420
|
+
} : void 0;
|
|
1421
|
+
}
|
|
1422
|
+
async function resolveNextResponsesEncryptedContentAttempt(attempt, error, options) {
|
|
1423
|
+
const orphanedFunctionOutput = isOrphanedFunctionCallOutputError(error);
|
|
1424
|
+
if (!isInvalidEncryptedContentError(error) && !orphanedFunctionOutput || attempt.kind === "compaction-stripped") return;
|
|
1425
|
+
if (!orphanedFunctionOutput && (attempt.kind === "initial" || attempt.kind === "continuation-rejected")) {
|
|
1426
|
+
const reasoningStripped = stripResponsesRequestEncryptedReasoning(attempt.request);
|
|
1427
|
+
if (reasoningStripped !== attempt.request) return {
|
|
1428
|
+
kind: "reasoning-stripped",
|
|
1429
|
+
request: reasoningStripped
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
const locallyStripped = stripResponsesRequestCompaction(attempt.request);
|
|
1433
|
+
if (locallyStripped === attempt.request) return;
|
|
1434
|
+
let compactionStripped = options?.buildFullHistoryRequest ? await options.buildFullHistoryRequest() : locallyStripped;
|
|
1435
|
+
compactionStripped = stripResponsesRequestCompaction(compactionStripped);
|
|
1436
|
+
if (attempt.kind === "reasoning-stripped") compactionStripped = stripResponsesRequestEncryptedReasoning(compactionStripped);
|
|
1437
|
+
return {
|
|
1438
|
+
kind: "compaction-stripped",
|
|
1439
|
+
request: compactionStripped,
|
|
1440
|
+
rejectedCompaction: readOpenAIResponsesCompactionRejection(attempt.request)
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
async function createResponsesStreamWithEncryptedContentRetry(params) {
|
|
1444
|
+
const encodeBody = params.encodeBody ?? prepareModelRequestBody(void 0);
|
|
1445
|
+
const send = async (initialAttempt) => {
|
|
1446
|
+
let attempt = initialAttempt;
|
|
1447
|
+
for (;;) {
|
|
1448
|
+
params.observePrompt?.(attempt.request, {
|
|
1449
|
+
egress: "responses-sdk",
|
|
1450
|
+
payloadVariant: attempt.kind
|
|
1451
|
+
});
|
|
1452
|
+
const bodyOptions = await encodeBody(attempt.request);
|
|
1453
|
+
try {
|
|
1454
|
+
const { data, response } = await params.client.responses.create(attempt.request, {
|
|
1455
|
+
...params.requestOptions,
|
|
1456
|
+
...bodyOptions,
|
|
1457
|
+
headers: {
|
|
1458
|
+
...bodyOptions.headers,
|
|
1459
|
+
...params.requestOptions?.headers
|
|
1460
|
+
}
|
|
1461
|
+
}).withResponse();
|
|
1462
|
+
commitResponsesEncryptedContentAttempt(attempt, (checkpoint) => {
|
|
1463
|
+
if (checkpoint) params.onCompactionRejected?.(checkpoint);
|
|
1464
|
+
});
|
|
1465
|
+
if (!isAsyncIterable(data)) throw new Error("OpenAI Responses streaming request returned a non-stream response");
|
|
1466
|
+
return {
|
|
1467
|
+
stream: data,
|
|
1468
|
+
response,
|
|
1469
|
+
attempt
|
|
1470
|
+
};
|
|
1471
|
+
} catch (error) {
|
|
1472
|
+
let nextAttempt = await resolveNextResponsesEncryptedContentAttempt(attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest });
|
|
1473
|
+
if (!nextAttempt && attempt.request.previous_response_id && error && typeof error === "object" && typeof error.status === "number" && isPreviousResponseRejection(error)) {
|
|
1474
|
+
const request = { ...params.buildFullHistoryRequest ? await params.buildFullHistoryRequest() : attempt.request };
|
|
1475
|
+
delete request.previous_response_id;
|
|
1476
|
+
nextAttempt = {
|
|
1477
|
+
kind: "continuation-rejected",
|
|
1478
|
+
request
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
if (!nextAttempt) throw error;
|
|
1482
|
+
const retryDescription = nextAttempt.kind === "reasoning-stripped" ? "without encrypted reasoning content" : nextAttempt.kind === "compaction-stripped" ? "without encrypted compaction content" : "full history after rejected previous_response_id";
|
|
1483
|
+
log.warn(`[responses] retrying ${retryDescription} provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
1484
|
+
attempt = nextAttempt;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
const result = await send({
|
|
1489
|
+
kind: params.initialAttemptKind ?? "initial",
|
|
1490
|
+
request: params.request,
|
|
1491
|
+
...params.initialRejectedCompaction ? { rejectedCompaction: params.initialRejectedCompaction } : {}
|
|
1492
|
+
});
|
|
1493
|
+
return { stream: { async *[Symbol.asyncIterator]() {
|
|
1494
|
+
let current = result;
|
|
1495
|
+
for (;;) {
|
|
1496
|
+
let rejectedEvent;
|
|
1497
|
+
try {
|
|
1498
|
+
for await (const event of params.wrapStream?.(current) ?? current.stream) {
|
|
1499
|
+
if (isRecord(event)) {
|
|
1500
|
+
const failure = event.type === "response.failed" && isRecord(event.response) ? event.response.error : event.type === "error" ? event.error ?? event : void 0;
|
|
1501
|
+
if (isRecord(failure) && params.canRetryStream?.() === true && isInvalidEncryptedContentError(failure)) {
|
|
1502
|
+
rejectedEvent = event;
|
|
1503
|
+
const message = typeof failure.message === "string" ? failure.message : "";
|
|
1504
|
+
throw Object.assign(new Error(message), {
|
|
1505
|
+
code: failure.code,
|
|
1506
|
+
status: failure.status
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
yield event;
|
|
1511
|
+
}
|
|
1512
|
+
return;
|
|
1513
|
+
} catch (error) {
|
|
1514
|
+
const nextAttempt = params.canRetryStream?.() === true && !params.requestOptions?.signal?.aborted ? await resolveNextResponsesEncryptedContentAttempt(current.attempt, error, { buildFullHistoryRequest: params.buildFullHistoryRequest }) : void 0;
|
|
1515
|
+
if (!nextAttempt) {
|
|
1516
|
+
if (rejectedEvent !== void 0) {
|
|
1517
|
+
yield rejectedEvent;
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
throw error;
|
|
1521
|
+
}
|
|
1522
|
+
log.warn(`[responses] retrying streamed encrypted content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
|
|
1523
|
+
current = await send(nextAttempt);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
} } };
|
|
1527
|
+
}
|
|
1528
|
+
function resolveAzureOpenAIApiVersion(env = process.env) {
|
|
1529
|
+
return env.AZURE_OPENAI_API_VERSION?.trim() || "preview";
|
|
1328
1530
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1531
|
+
//#endregion
|
|
1532
|
+
//#region packages/ai/src/providers/openai-responses-stream-compat.ts
|
|
1533
|
+
const OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE = "output_text";
|
|
1534
|
+
const AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE = "text";
|
|
1535
|
+
const OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE = "response.output_text.delta";
|
|
1536
|
+
const AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE = "response.text.delta";
|
|
1537
|
+
function isResponsesTextContentPartType(type) {
|
|
1538
|
+
return type === "output_text" || type === "text";
|
|
1334
1539
|
}
|
|
1335
|
-
function
|
|
1336
|
-
|
|
1337
|
-
const metadataKeys = isRecord(response?.metadata) ? Object.keys(response.metadata).toSorted() : [];
|
|
1338
|
-
const responsePreview = {
|
|
1339
|
-
id: readResponseFailedString(response, "id"),
|
|
1340
|
-
status: readResponseFailedString(response, "status"),
|
|
1341
|
-
model: readResponseFailedString(response, "model"),
|
|
1342
|
-
object: readResponseFailedString(response, "object"),
|
|
1343
|
-
failureFields,
|
|
1344
|
-
metadataKeys
|
|
1345
|
-
};
|
|
1346
|
-
return {
|
|
1347
|
-
event: "openai_responses_response_failed_without_details",
|
|
1348
|
-
provider: model.provider,
|
|
1349
|
-
api: model.api,
|
|
1350
|
-
transportModel: model.id,
|
|
1351
|
-
providerRuntimeFailureKind: "no_error_details",
|
|
1352
|
-
responseId: responsePreview.id,
|
|
1353
|
-
responseStatus: responsePreview.status,
|
|
1354
|
-
responseModel: responsePreview.model,
|
|
1355
|
-
responseObject: responsePreview.object,
|
|
1356
|
-
metadataKeys,
|
|
1357
|
-
requestIdHashes: collectResponseFailedIdentifierHashes(event),
|
|
1358
|
-
failureFieldsPreview: stringifyRedactedEvent(failureFields),
|
|
1359
|
-
responsePreview: stringifyRedactedEvent(responsePreview)
|
|
1360
|
-
};
|
|
1540
|
+
function isResponsesTextDeltaEventType(type) {
|
|
1541
|
+
return type === "response.output_text.delta" || type === "response.text.delta";
|
|
1361
1542
|
}
|
|
1362
|
-
function
|
|
1363
|
-
|
|
1364
|
-
const metadataKeys = observation.metadataKeys.join(",");
|
|
1365
|
-
return `responseId=${safeDebugValue(observation.responseId || void 0)} responseStatus=${safeDebugValue(observation.responseStatus || void 0)} responseModel=${safeDebugValue(observation.responseModel || void 0)} requestIds=${requestIds || "none"} metadataKeys=${metadataKeys || "none"} failureFields=${observation.failureFieldsPreview}`;
|
|
1543
|
+
function isAzureResponsesTextDeltaEventType(type) {
|
|
1544
|
+
return type === AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE;
|
|
1366
1545
|
}
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1369
|
-
const responseId = readResponseFailedString(response, "id") || void 0;
|
|
1370
|
-
const error = isRecord(response?.error) ? response.error : void 0;
|
|
1371
|
-
if (error) {
|
|
1372
|
-
const code = readResponseFailedString(error, "code").trim();
|
|
1373
|
-
const message = readResponseFailedString(error, "message").trim();
|
|
1374
|
-
if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId, code || void 0);
|
|
1375
|
-
}
|
|
1376
|
-
const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
|
|
1377
|
-
if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
|
|
1378
|
-
return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, void 0, buildResponsesFailedNoDetailsObservation(event, model, response));
|
|
1546
|
+
function isAzureResponsesTextDeltaEvent(event) {
|
|
1547
|
+
return isAzureResponsesTextDeltaEventType(event.type) && typeof event.delta === "string";
|
|
1379
1548
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
}
|
|
1389
|
-
};
|
|
1390
|
-
function logResponsesFailedNoDetails(observation) {
|
|
1391
|
-
log.warn(`[responses] response.failed missing error details provider=${observation.provider} api=${observation.api} model=${observation.transportModel} ` + summarizeResponsesFailedNoDetailsObservation(observation), observation);
|
|
1549
|
+
function resolveResponsesMessageSnapshotCollapse(params) {
|
|
1550
|
+
const { prior, nextText } = params;
|
|
1551
|
+
if (!prior?.text || !nextText || prior.phase !== params.nextPhase) return { kind: "keep" };
|
|
1552
|
+
if (nextText.length > prior.text.length && nextText.startsWith(prior.text)) return {
|
|
1553
|
+
kind: "extend",
|
|
1554
|
+
text: nextText
|
|
1555
|
+
};
|
|
1556
|
+
return { kind: "keep" };
|
|
1392
1557
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
|
|
1398
|
-
const text = record.text && typeof record.text === "object" ? record.text : void 0;
|
|
1399
|
-
const parts = [
|
|
1400
|
-
`fields=${Object.keys(record).toSorted().join(",")}`,
|
|
1401
|
-
`model=${safeDebugValue(record.model)}`,
|
|
1402
|
-
`stream=${safeDebugValue(record.stream)}`,
|
|
1403
|
-
`inputItems=${Array.isArray(input) ? input.length : typeof input}`,
|
|
1404
|
-
`inputItemShape=${responseInputItemShape(input)}`,
|
|
1405
|
-
`inputRoles=${responseInputRoles(input) || "none"}`,
|
|
1406
|
-
`inputTextChars=${responseInputTextChars(input)}`,
|
|
1407
|
-
`tools=${summarizeResponsesTools(record.tools)}`,
|
|
1408
|
-
`reasoningEffort=${safeDebugValue(reasoning?.effort)}`,
|
|
1409
|
-
`reasoningSummary=${safeDebugValue(reasoning?.summary)}`,
|
|
1410
|
-
`textVerbosity=${safeDebugValue(text?.verbosity)}`,
|
|
1411
|
-
`serviceTier=${safeDebugValue(record.service_tier)}`,
|
|
1412
|
-
...summarizeResponsesCompactionItems(input),
|
|
1413
|
-
`store=${safeDebugValue(record.store)}`,
|
|
1414
|
-
`promptCacheKey=${record.prompt_cache_key === void 0 ? "absent" : "present"}`,
|
|
1415
|
-
`metadataKeys=${record.metadata && typeof record.metadata === "object" ? Object.keys(record.metadata).toSorted().join(",") : "none"}`
|
|
1416
|
-
];
|
|
1417
|
-
if (resolveModelPayloadDebugMode() === "full-redacted") parts.push(`payload=${stringifyRedactedPayload(record)}`);
|
|
1418
|
-
return parts.join(" ");
|
|
1558
|
+
//#endregion
|
|
1559
|
+
//#region packages/ai/src/providers/openai-responses-tool-call-tracker.ts
|
|
1560
|
+
function readOutputIndex(event) {
|
|
1561
|
+
return typeof event.output_index === "number" && Number.isInteger(event.output_index) && event.output_index >= 0 ? event.output_index : void 0;
|
|
1419
1562
|
}
|
|
1420
|
-
function
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
return
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1563
|
+
function readEventIdentity(event) {
|
|
1564
|
+
return { itemId: normalizeOptionalString(event.item_id) };
|
|
1565
|
+
}
|
|
1566
|
+
function readResponsesToolCallItemIdentity(item) {
|
|
1567
|
+
return {
|
|
1568
|
+
itemId: normalizeOptionalString(item.id),
|
|
1569
|
+
callId: normalizeOptionalString(item.call_id)
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
function createResponsesToolCallTracker() {
|
|
1573
|
+
const indexedCalls = /* @__PURE__ */ new Map();
|
|
1574
|
+
const unindexedCalls = /* @__PURE__ */ new Set();
|
|
1575
|
+
const identitiesConflict = (state, identity) => Boolean(state.itemId && identity.itemId && state.itemId !== identity.itemId || state.callId && identity.callId && state.callId !== identity.callId);
|
|
1576
|
+
const sharesIdentity = (state, identity) => Boolean(state.itemId && identity.itemId && state.itemId === identity.itemId || state.callId && identity.callId && state.callId === identity.callId);
|
|
1577
|
+
const adoptIdentity = (state, identity) => {
|
|
1578
|
+
state.itemId ??= identity.itemId;
|
|
1579
|
+
state.callId ??= identity.callId;
|
|
1580
|
+
return state;
|
|
1581
|
+
};
|
|
1582
|
+
const resolveCompatible = (candidates, identity, allowUnmatchedIdentity) => {
|
|
1583
|
+
const uniqueCandidates = [...new Set(candidates)];
|
|
1584
|
+
if (!identity.itemId && !identity.callId) return allowUnmatchedIdentity && uniqueCandidates.length === 1 ? uniqueCandidates.at(0) : void 0;
|
|
1585
|
+
const compatible = uniqueCandidates.filter((state) => !identitiesConflict(state, identity));
|
|
1586
|
+
const matches = compatible.filter((state) => sharesIdentity(state, identity));
|
|
1587
|
+
const matched = matches.length === 1 ? matches.at(0) : void 0;
|
|
1588
|
+
if (matched) return adoptIdentity(matched, identity);
|
|
1589
|
+
const soleCompatible = allowUnmatchedIdentity && uniqueCandidates.length === 1 && compatible.length === 1 && matches.length === 0 ? compatible.at(0) : void 0;
|
|
1590
|
+
return soleCompatible ? adoptIdentity(soleCompatible, identity) : void 0;
|
|
1591
|
+
};
|
|
1592
|
+
return {
|
|
1593
|
+
register(event, state) {
|
|
1594
|
+
const outputIndex = readOutputIndex(event);
|
|
1595
|
+
if (outputIndex === void 0) {
|
|
1596
|
+
unindexedCalls.add(state);
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
if (indexedCalls.has(outputIndex)) throw new Error(`Responses stream reused active tool-call output index ${outputIndex}`);
|
|
1600
|
+
state.outputIndex = outputIndex;
|
|
1601
|
+
indexedCalls.set(outputIndex, state);
|
|
1602
|
+
},
|
|
1603
|
+
resolve(event, identity = readEventIdentity(event), allowUnmatchedIdentity = true) {
|
|
1604
|
+
const outputIndex = readOutputIndex(event);
|
|
1605
|
+
if (outputIndex !== void 0) {
|
|
1606
|
+
const indexed = indexedCalls.get(outputIndex);
|
|
1607
|
+
if (indexed) {
|
|
1608
|
+
if (indexed.callId && identity.callId && indexed.callId !== identity.callId) return;
|
|
1609
|
+
return adoptIdentity(indexed, identity);
|
|
1610
|
+
}
|
|
1611
|
+
const unindexed = resolveCompatible(unindexedCalls, identity, allowUnmatchedIdentity);
|
|
1612
|
+
if (unindexed) {
|
|
1613
|
+
unindexedCalls.delete(unindexed);
|
|
1614
|
+
unindexed.outputIndex = outputIndex;
|
|
1615
|
+
indexedCalls.set(outputIndex, unindexed);
|
|
1616
|
+
}
|
|
1617
|
+
return unindexed;
|
|
1618
|
+
}
|
|
1619
|
+
return resolveCompatible([...indexedCalls.values(), ...unindexedCalls], identity, allowUnmatchedIdentity);
|
|
1620
|
+
},
|
|
1621
|
+
forget(toolCall) {
|
|
1622
|
+
for (const [outputIndex, tracked] of indexedCalls) if (tracked === toolCall) indexedCalls.delete(outputIndex);
|
|
1623
|
+
unindexedCalls.delete(toolCall);
|
|
1624
|
+
},
|
|
1625
|
+
markArgumentsUnreliable() {
|
|
1626
|
+
for (const toolCall of /* @__PURE__ */ new Set([...indexedCalls.values(), ...unindexedCalls])) toolCall.argumentStreamReliable = false;
|
|
1627
|
+
},
|
|
1628
|
+
hasActive() {
|
|
1629
|
+
return indexedCalls.size > 0 || unindexedCalls.size > 0;
|
|
1630
|
+
},
|
|
1631
|
+
hasExactlyActive(expected) {
|
|
1632
|
+
const active = /* @__PURE__ */ new Set([...indexedCalls.values(), ...unindexedCalls]);
|
|
1633
|
+
return active.size === expected.length && expected.every((state) => active.has(state));
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1433
1636
|
}
|
|
1434
1637
|
//#endregion
|
|
1435
1638
|
//#region packages/ai/src/transports/openai-responses-stream-observer-internal.ts
|
|
@@ -1442,15 +1645,12 @@ const STRING_DELTA_EVENTS = /* @__PURE__ */ new Set([
|
|
|
1442
1645
|
"response.text.delta"
|
|
1443
1646
|
]);
|
|
1444
1647
|
async function* adaptResponsesStream(stream, signal) {
|
|
1445
|
-
const
|
|
1446
|
-
for await (const event of stream) {
|
|
1447
|
-
if (signal?.aborted) throw transportAbortError(signal);
|
|
1648
|
+
for await (const event of iterateModelStream(stream, signal)) {
|
|
1448
1649
|
if (!isRecord(event) || typeof event.type !== "string") throw new Error("Responses stream delivered a malformed event without a string type");
|
|
1449
1650
|
if (STRING_DELTA_EVENTS.has(event.type) && typeof event.delta !== "string") throw new Error(`Responses stream delivered malformed ${event.type} delta`);
|
|
1450
1651
|
if ((event.type === "response.output_item.added" || event.type === "response.output_item.done") && !isRecord(event.item)) throw new Error(`Responses stream delivered malformed ${event.type} item`);
|
|
1451
1652
|
if ((event.type === "response.created" || event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed") && !isRecord(event.response)) throw new Error(`Responses stream delivered malformed ${event.type} response`);
|
|
1452
1653
|
yield event;
|
|
1453
|
-
await scheduler.afterEvent();
|
|
1454
1654
|
}
|
|
1455
1655
|
}
|
|
1456
1656
|
async function* observeResponsesStream(stream, model, requestStartedAt) {
|
|
@@ -1474,8 +1674,12 @@ async function* observeResponsesStream(stream, model, requestStartedAt) {
|
|
|
1474
1674
|
}
|
|
1475
1675
|
//#endregion
|
|
1476
1676
|
//#region packages/ai/src/transports/openai-responses-stream-slots-internal.ts
|
|
1477
|
-
function createResponsesOutputTracker() {
|
|
1677
|
+
function createResponsesOutputTracker(params) {
|
|
1478
1678
|
const outputs = /* @__PURE__ */ new Map();
|
|
1679
|
+
let currentEventType = "unknown";
|
|
1680
|
+
let providerToolObserved = false;
|
|
1681
|
+
let visibleTextEmitted = false;
|
|
1682
|
+
let terminalAllowsRetry = false;
|
|
1479
1683
|
const identity = (item) => {
|
|
1480
1684
|
if ((item.type === "reasoning" || item.type === "message") && item.id) return `${item.type}:${item.id}`;
|
|
1481
1685
|
const callId = item.call_id ?? item.id;
|
|
@@ -1485,10 +1689,37 @@ function createResponsesOutputTracker() {
|
|
|
1485
1689
|
const key = identity(item);
|
|
1486
1690
|
const output = (outputIndex === void 0 ? void 0 : outputs.get(outputIndex)) ?? (key === void 0 ? void 0 : outputs.get(key));
|
|
1487
1691
|
if (!output || outputIndex !== void 0 && output.outputIndex !== void 0 && output.outputIndex !== outputIndex) return;
|
|
1488
|
-
if (output.type !== item.type || output.callId && item.call_id && output.callId !== item.call_id) throw new
|
|
1692
|
+
if (output.type !== item.type || output.callId && item.call_id && output.callId !== item.call_id) throw new ResponsesOutputIdentityError({
|
|
1693
|
+
outputIndex,
|
|
1694
|
+
expectedType: output.type,
|
|
1695
|
+
actualType: item.type,
|
|
1696
|
+
completed: output.completed,
|
|
1697
|
+
completedToolCall: [...outputs.values()].some((candidate) => candidate.type === "function_call" && candidate.completed),
|
|
1698
|
+
eventType: currentEventType,
|
|
1699
|
+
retrySafe: params.canRetryIdentityConflict?.() === true && !providerToolObserved && !visibleTextEmitted && terminalAllowsRetry && !params.output.content.some((block) => block.type === "text" && block.text.length > 0)
|
|
1700
|
+
});
|
|
1489
1701
|
return output;
|
|
1490
1702
|
};
|
|
1491
1703
|
return {
|
|
1704
|
+
trackStream(sink) {
|
|
1705
|
+
return { push(event) {
|
|
1706
|
+
visibleTextEmitted ||= event.type === "text_delta" && event.delta.length > 0 || event.type === "text_end" && event.content.length > 0;
|
|
1707
|
+
sink.push(event);
|
|
1708
|
+
} };
|
|
1709
|
+
},
|
|
1710
|
+
observeEvent(event) {
|
|
1711
|
+
currentEventType = event.type;
|
|
1712
|
+
terminalAllowsRetry = false;
|
|
1713
|
+
if (event.type === "response.output_item.added" || event.type === "response.output_item.done") providerToolObserved ||= isResponsesProviderTool(event.item);
|
|
1714
|
+
else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
1715
|
+
let terminalHasRefusal = false;
|
|
1716
|
+
for (const item of event.response.output ?? []) {
|
|
1717
|
+
providerToolObserved ||= isResponsesProviderTool(item);
|
|
1718
|
+
terminalHasRefusal ||= item.type === "message" && (item.content ?? []).some((part) => part.type === "refusal");
|
|
1719
|
+
}
|
|
1720
|
+
terminalAllowsRetry = event.type === "response.completed" && event.response.status === "completed" && event.response.error == null && event.response.incomplete_details == null && !terminalHasRefusal;
|
|
1721
|
+
}
|
|
1722
|
+
},
|
|
1492
1723
|
get,
|
|
1493
1724
|
set(item, contentIndex, outputIndex, completed = false) {
|
|
1494
1725
|
const output = get(item, outputIndex) ?? {
|
|
@@ -1511,9 +1742,10 @@ function createResponsesOutputTracker() {
|
|
|
1511
1742
|
};
|
|
1512
1743
|
}
|
|
1513
1744
|
function appendResponsesPendingTextDelta(slot, delta, materialize) {
|
|
1745
|
+
const offset = slot.pendingText?.length ?? 0;
|
|
1514
1746
|
slot.pendingText = `${slot.pendingText ?? ""}${delta}`;
|
|
1515
1747
|
const priorText = slot.collapseCandidate?.block.text ?? "";
|
|
1516
|
-
if (priorText.
|
|
1748
|
+
if (offset >= priorText.length || priorText.startsWith(delta.slice(0, priorText.length - offset), offset)) return;
|
|
1517
1749
|
materialize(slot);
|
|
1518
1750
|
}
|
|
1519
1751
|
function readResponsesOutputIndex(event) {
|
|
@@ -1547,6 +1779,11 @@ function createResponsesOutputSlotTracker() {
|
|
|
1547
1779
|
const outputIndex = readResponsesOutputIndex(event);
|
|
1548
1780
|
return outputIndex === void 0 ? unindexed : indexed.get(outputIndex);
|
|
1549
1781
|
},
|
|
1782
|
+
resolveOutputItem(event, item) {
|
|
1783
|
+
if (item.type === "reasoning") return this.resolve(event, "thinking");
|
|
1784
|
+
if (item.type === "message") return this.resolve(event, "text");
|
|
1785
|
+
return readResponsesOutputIndex(event) === void 0 ? void 0 : this.get(event);
|
|
1786
|
+
},
|
|
1550
1787
|
values() {
|
|
1551
1788
|
return [.../* @__PURE__ */ new Set([...indexed.values(), ...unindexed ? [unindexed] : []])];
|
|
1552
1789
|
},
|
|
@@ -1788,6 +2025,7 @@ function createResponsesTerminalController(params) {
|
|
|
1788
2025
|
timestamp: Date.now(),
|
|
1789
2026
|
details: {
|
|
1790
2027
|
eventType: terminalEventType,
|
|
2028
|
+
stopReason: terminal.stopReason,
|
|
1791
2029
|
...terminalEventType === "response.incomplete" ? { incompleteReason: incompleteReason === "max_output_tokens" || incompleteReason === "max_messages" || incompleteReason === "content_filter" || incompleteReason === "steered" ? incompleteReason : "unknown" } : {},
|
|
1792
2030
|
endTurn: typeof response.end_turn === "boolean" ? response.end_turn : response.end_turn === void 0 ? "absent" : "invalid"
|
|
1793
2031
|
}
|
|
@@ -1802,12 +2040,16 @@ function createResponsesTerminalController(params) {
|
|
|
1802
2040
|
}
|
|
1803
2041
|
//#endregion
|
|
1804
2042
|
//#region packages/ai/src/transports/openai-responses-stream-internal.ts
|
|
1805
|
-
async function processResponsesStream(openaiStream, output,
|
|
2043
|
+
async function processResponsesStream(openaiStream, output, sink, model, options) {
|
|
1806
2044
|
const streamingToolCalls = createResponsesToolCallTracker();
|
|
1807
2045
|
const outputSlots = createResponsesOutputSlotTracker();
|
|
1808
|
-
const outputs = createResponsesOutputTracker(
|
|
2046
|
+
const outputs = createResponsesOutputTracker({
|
|
2047
|
+
output,
|
|
2048
|
+
canRetryIdentityConflict: options?.canRetryIdentityConflict
|
|
2049
|
+
});
|
|
2050
|
+
const stream = outputs.trackStream(sink);
|
|
1809
2051
|
let terminalResponse;
|
|
1810
|
-
let
|
|
2052
|
+
let rejectedToolCall;
|
|
1811
2053
|
let lastTextBlock = null;
|
|
1812
2054
|
const blocks = output.content;
|
|
1813
2055
|
const compactionTracker = createCompactionTracker(output, model, options);
|
|
@@ -1864,11 +2106,6 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
1864
2106
|
return slot;
|
|
1865
2107
|
}
|
|
1866
2108
|
};
|
|
1867
|
-
const resolveOutputItemSlot = (event, item) => {
|
|
1868
|
-
if (item.type === "reasoning") return outputSlots.resolve(event, "thinking");
|
|
1869
|
-
if (item.type === "message") return outputSlots.resolve(event, "text");
|
|
1870
|
-
return readResponsesOutputIndex(event) === void 0 ? void 0 : outputSlots.get(event);
|
|
1871
|
-
};
|
|
1872
2109
|
const materializeDeferredTextSlot = (slot) => {
|
|
1873
2110
|
if (slot.block || slot.pendingText === null) return;
|
|
1874
2111
|
const text = slot.pendingText;
|
|
@@ -1984,9 +2221,14 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
1984
2221
|
}), options?.signal);
|
|
1985
2222
|
try {
|
|
1986
2223
|
for await (const event of guardedStream) {
|
|
2224
|
+
outputs.observeEvent(event);
|
|
1987
2225
|
notifyLlmRequestActivity(options?.signal);
|
|
1988
|
-
if (event.type === "response.output_item.done" && event.item.type === "function_call" && event.item.status
|
|
1989
|
-
|
|
2226
|
+
if (event.type === "response.output_item.done" && event.item.type === "function_call" && event.item.status && event.item.status !== "completed" && !rejectedToolCall) try {
|
|
2227
|
+
resolveCompletedResponsesToolCall(event.item);
|
|
2228
|
+
} catch (error) {
|
|
2229
|
+
rejectedToolCall = { error };
|
|
2230
|
+
}
|
|
2231
|
+
if (rejectedToolCall && event.type !== "response.completed" && event.type !== "response.incomplete" && event.type !== "response.failed" && event.type !== "error") continue;
|
|
1990
2232
|
if (event.type === "response.created") output.responseId = event.response.id;
|
|
1991
2233
|
else if (event.type === "response.output_item.added") {
|
|
1992
2234
|
materializeDeferredTextSlots();
|
|
@@ -2029,22 +2271,15 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2029
2271
|
if (!slot) continue;
|
|
2030
2272
|
slot.item.summary = slot.item.summary || [];
|
|
2031
2273
|
slot.item.summary.push(event.part);
|
|
2032
|
-
} else if (event.type === "response.reasoning_summary_text.delta") {
|
|
2033
|
-
const slot = outputSlots.resolve(event, "thinking");
|
|
2034
|
-
if (!slot) continue;
|
|
2035
|
-
slot.item.summary = slot.item.summary || [];
|
|
2036
|
-
const lastPart = slot.item.summary[slot.item.summary.length - 1];
|
|
2037
|
-
if (!lastPart) continue;
|
|
2038
|
-
lastPart.text += event.delta;
|
|
2039
|
-
appendThinkingDelta(slot, event.delta);
|
|
2040
|
-
} else if (event.type === "response.reasoning_summary_part.done") {
|
|
2274
|
+
} else if (event.type === "response.reasoning_summary_text.delta" || event.type === "response.reasoning_summary_part.done") {
|
|
2041
2275
|
const slot = outputSlots.resolve(event, "thinking");
|
|
2042
2276
|
if (!slot) continue;
|
|
2043
2277
|
slot.item.summary = slot.item.summary || [];
|
|
2044
2278
|
const lastPart = slot.item.summary[slot.item.summary.length - 1];
|
|
2045
2279
|
if (!lastPart) continue;
|
|
2046
|
-
|
|
2047
|
-
|
|
2280
|
+
const delta = event.type === "response.reasoning_summary_text.delta" ? event.delta : "\n\n";
|
|
2281
|
+
lastPart.text += delta;
|
|
2282
|
+
appendThinkingDelta(slot, delta);
|
|
2048
2283
|
} else if (event.type === "response.reasoning_text.delta") {
|
|
2049
2284
|
const slot = outputSlots.resolve(event, "thinking");
|
|
2050
2285
|
if (!slot) continue;
|
|
@@ -2134,7 +2369,7 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2134
2369
|
} else if (event.type === "response.output_item.done") {
|
|
2135
2370
|
const item = event.item;
|
|
2136
2371
|
if (item.type !== "message") lastTextBlock = null;
|
|
2137
|
-
const existingOutputSlot =
|
|
2372
|
+
const existingOutputSlot = outputSlots.resolveOutputItem(event, item);
|
|
2138
2373
|
materializeDeferredTextSlots(existingOutputSlot);
|
|
2139
2374
|
const outputSlot = existingOutputSlot ?? createOutputSlot(event, item);
|
|
2140
2375
|
compactionTracker.completed(item, blocks.length);
|
|
@@ -2216,23 +2451,23 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2216
2451
|
const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
|
|
2217
2452
|
if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) continue;
|
|
2218
2453
|
const streamedArguments = streamingToolCall?.block.partialJson || "";
|
|
2219
|
-
const
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2454
|
+
const parsedStreamedArguments = streamingToolCall?.argumentStreamReliable && streamingToolCall?.argumentsStreamed && streamedArguments.length > 0 && completedArguments !== void 0 && streamedArguments !== completedArguments ? parseJsonObjectPreservingUnsafeIntegers(streamedArguments) : null;
|
|
2455
|
+
let validated;
|
|
2456
|
+
try {
|
|
2457
|
+
validated = resolveCompletedResponsesToolCall(item, {
|
|
2458
|
+
name: streamingToolCall?.block.name,
|
|
2459
|
+
arguments: parsedStreamedArguments ?? (completedArguments || streamedArguments)
|
|
2460
|
+
});
|
|
2461
|
+
} catch (error) {
|
|
2462
|
+
rejectedToolCall = { error };
|
|
2463
|
+
continue;
|
|
2464
|
+
}
|
|
2224
2465
|
finalizeToolCall(item, readResponsesOutputIndex(event), streamingToolCall, validated);
|
|
2225
2466
|
}
|
|
2226
2467
|
} else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
2227
2468
|
terminal.finalizeResponse(event.response, event.type);
|
|
2228
|
-
if (
|
|
2229
|
-
|
|
2230
|
-
resolveCompletedResponsesToolCall(incompleteToolCall);
|
|
2231
|
-
}
|
|
2232
|
-
if (event.type === "response.incomplete" && streamingToolCalls.hasActive()) {
|
|
2233
|
-
if (output.errorMessage) throw new Error(output.errorMessage);
|
|
2234
|
-
throw new IncompleteToolCallError("Responses stream completed with unresolved tool calls");
|
|
2235
|
-
}
|
|
2469
|
+
if (rejectedToolCall) throw output.errorMessage ? new Error(output.errorMessage) : rejectedToolCall.error;
|
|
2470
|
+
if (event.type === "response.incomplete" && streamingToolCalls.hasActive()) throw output.errorMessage ? new Error(output.errorMessage) : new IncompleteToolCallError("Responses stream completed with unresolved tool calls");
|
|
2236
2471
|
if (event.type === "response.completed" || output.stopReason === "length") {
|
|
2237
2472
|
const items = event.response.output ?? [];
|
|
2238
2473
|
const completeToolCall = event.type === "response.completed" ? prepareTerminalToolCalls(items) : void 0;
|
|
@@ -2241,14 +2476,22 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2241
2476
|
terminalResponse = event.type === "response.completed" ? event.response : null;
|
|
2242
2477
|
if (output.stopReason === "stop" && output.content.some((block) => block.type === "toolCall")) output.stopReason = "toolUse";
|
|
2243
2478
|
break;
|
|
2244
|
-
} else if (event.type === "error")
|
|
2245
|
-
|
|
2479
|
+
} else if (event.type === "error") {
|
|
2480
|
+
const details = isRecord(event) && isRecord(event.error) ? event.error : event;
|
|
2481
|
+
const message = readStringValue(details.message);
|
|
2482
|
+
const code = normalizeStringifiedOptionalString(details.code);
|
|
2483
|
+
throw Object.assign(new Error(message ? code ? `Error Code ${code}: ${message}` : message : "Unknown error"), {
|
|
2484
|
+
code: details.code,
|
|
2485
|
+
error: details
|
|
2486
|
+
});
|
|
2487
|
+
} else if (event.type === "response.failed") {
|
|
2246
2488
|
const failure = normalizeResponsesFailedEvent(isRecord(event) ? event : {}, model);
|
|
2247
2489
|
terminal.finalizeFailedResponse(event.response, failure.responseId);
|
|
2248
2490
|
throw new ResponsesStreamFailure(failure, event.response);
|
|
2249
2491
|
}
|
|
2250
2492
|
}
|
|
2251
2493
|
if (options?.signal?.aborted) throw transportAbortError(options.signal);
|
|
2494
|
+
if (rejectedToolCall) throw rejectedToolCall.error;
|
|
2252
2495
|
if (streamingToolCalls.hasActive()) throw new Error("Responses stream ended with unresolved tool calls");
|
|
2253
2496
|
if (terminalResponse === void 0) throw new Error("OpenAI Responses stream ended before a terminal response event");
|
|
2254
2497
|
return terminalResponse ?? void 0;
|
|
@@ -2257,6 +2500,17 @@ async function processResponsesStream(openaiStream, output, stream, model, optio
|
|
|
2257
2500
|
}
|
|
2258
2501
|
}
|
|
2259
2502
|
//#endregion
|
|
2503
|
+
//#region packages/ai/src/providers/openai-provider-refusal.ts
|
|
2504
|
+
/** Responses uses snake_case; the ChatGPT app-server error envelope uses camelCase. */
|
|
2505
|
+
function readOpenAIMisalignmentReview(value, continuationSupported) {
|
|
2506
|
+
if (!isRecord(value)) return;
|
|
2507
|
+
return readProviderRefusalReview({
|
|
2508
|
+
explanation: value.detailed_explanation ?? value.detailedExplanation,
|
|
2509
|
+
errorType: value.error_type ?? value.errorType,
|
|
2510
|
+
...continuationSupported ? { continuation: value.steer } : {}
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
//#endregion
|
|
2260
2514
|
//#region packages/ai/src/providers/openai-responses-tools.ts
|
|
2261
2515
|
/** Projects direct provider descriptors before resolving their strict policy. */
|
|
2262
2516
|
function convertResponsesToolPayload(tools, options) {
|
|
@@ -2293,16 +2547,12 @@ function resolveResponsesStrictToolSetting(options) {
|
|
|
2293
2547
|
if (options?.strict !== void 0) return options.strict;
|
|
2294
2548
|
if (options?.model) return getAiTransportHost().resolveOpenAIStrictToolSetting(options.model, {
|
|
2295
2549
|
transport: "stream",
|
|
2296
|
-
supportsStrictMode: options.supportsStrictMode
|
|
2550
|
+
supportsStrictMode: options.supportsStrictMode ?? resolveOpenAICompletionsCompat(options.model).supportsStrictMode
|
|
2297
2551
|
});
|
|
2298
2552
|
return false;
|
|
2299
2553
|
}
|
|
2300
2554
|
//#endregion
|
|
2301
2555
|
//#region packages/ai/src/providers/openai-responses-shared.ts
|
|
2302
|
-
function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
2303
|
-
return convertProviderResponsesMessages(model, context, allowedToolCallProviders, options);
|
|
2304
|
-
}
|
|
2305
|
-
const createResponsesAssistantOutput = createOpenAIResponsesAssistantOutput;
|
|
2306
2556
|
function applyResponsesServiceTierPricing(usage, serviceTier, model) {
|
|
2307
2557
|
let multiplier = 1;
|
|
2308
2558
|
if (serviceTier === "flex") multiplier = .5;
|
|
@@ -2331,13 +2581,6 @@ function applyCommonResponsesParams(params, model, context, options, config) {
|
|
|
2331
2581
|
params.include = ["reasoning.encrypted_content"];
|
|
2332
2582
|
}
|
|
2333
2583
|
}
|
|
2334
|
-
function buildResponsesRequestOptions(options) {
|
|
2335
|
-
return {
|
|
2336
|
-
...options?.signal ? { signal: options.signal } : {},
|
|
2337
|
-
...options?.timeoutMs !== void 0 ? { timeout: options.timeoutMs } : {},
|
|
2338
|
-
maxRetries: 0
|
|
2339
|
-
};
|
|
2340
|
-
}
|
|
2341
2584
|
function cleanStreamingScratchBuffers(output) {
|
|
2342
2585
|
for (const block of output.content) {
|
|
2343
2586
|
delete block.index;
|
|
@@ -2350,6 +2593,7 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
2350
2593
|
try {
|
|
2351
2594
|
const model = params.resolveRequestModel?.(params.model) ?? params.model;
|
|
2352
2595
|
const client = params.createClient(model);
|
|
2596
|
+
const encodeBody = prepareModelRequestBody(options);
|
|
2353
2597
|
const buildRequest = async (replayMode) => {
|
|
2354
2598
|
let request = params.buildParams(model, replayMode);
|
|
2355
2599
|
const nextRequest = await options?.onPayload?.(request, model);
|
|
@@ -2365,10 +2609,12 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
2365
2609
|
client,
|
|
2366
2610
|
request: requestParams,
|
|
2367
2611
|
requestOptions: {
|
|
2368
|
-
|
|
2369
|
-
|
|
2612
|
+
signal: firstEvent.signal,
|
|
2613
|
+
...options?.timeoutMs !== void 0 ? { timeout: options.timeoutMs } : {},
|
|
2614
|
+
maxRetries: 0
|
|
2370
2615
|
},
|
|
2371
2616
|
model,
|
|
2617
|
+
encodeBody,
|
|
2372
2618
|
buildFullHistoryRequest: () => buildRequest("full-history"),
|
|
2373
2619
|
onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, options, checkpoint),
|
|
2374
2620
|
canRetryStream: () => output.content.length === 0,
|
|
@@ -2401,6 +2647,7 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
2401
2647
|
onFirstEventTimeout: params.processStreamOptions?.onFirstEventTimeout ?? onFirstEventTimeout,
|
|
2402
2648
|
signal: params.processStreamOptions?.signal ?? options?.signal
|
|
2403
2649
|
} : void 0,
|
|
2650
|
+
canRetryIdentityConflict: () => hasOnlyResponsesFunctionTools(admittedRequest),
|
|
2404
2651
|
reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
|
|
2405
2652
|
sessionId: options?.sessionId,
|
|
2406
2653
|
authProfileId: options?.authProfileId
|
|
@@ -2413,6 +2660,20 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
2413
2660
|
signal: options?.signal
|
|
2414
2661
|
});
|
|
2415
2662
|
} catch (error) {
|
|
2663
|
+
const response = error instanceof ResponsesStreamFailure ? error.response : error;
|
|
2664
|
+
const rawError = isRecord(response) && isRecord(response.error) ? response.error : response;
|
|
2665
|
+
if (params.model.provider === "openai" && params.model.api === "openai-responses" && isRecord(rawError) && rawError.code === "misalignment_policy_violation") {
|
|
2666
|
+
const review = readOpenAIMisalignmentReview(rawError.misalignment, false);
|
|
2667
|
+
appendAssistantMessageDiagnostic(output, {
|
|
2668
|
+
type: "provider_refusal",
|
|
2669
|
+
timestamp: Date.now(),
|
|
2670
|
+
details: {
|
|
2671
|
+
provider: "openai",
|
|
2672
|
+
category: "misalignment",
|
|
2673
|
+
...review ? { review } : {}
|
|
2674
|
+
}
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2416
2677
|
failTransportStream({
|
|
2417
2678
|
stream,
|
|
2418
2679
|
output,
|
|
@@ -2425,4 +2686,4 @@ async function runResponsesStreamLifecycle(params) {
|
|
|
2425
2686
|
}
|
|
2426
2687
|
}
|
|
2427
2688
|
//#endregion
|
|
2428
|
-
export {
|
|
2689
|
+
export { OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE as $, recordResponsesContextUsage as A, resolveResponsesContinuationRequest as B, resolveAzureOpenAIApiVersion as C, safeDebugValue as D, logResponsesFailedNoDetails as E, convertResponsesMessages as F, supportsResponsesReasoningUpdate as G, responsesContinuationRequestFingerprint as H, recordResponsesInputReplay as I, captureOpenAIResponsesCompaction as J, CompactionReplayRefreshRequiredError as K, responsesInputFingerprint as L, bindResponsesInputMessage as M, buildResponsesInputMessage as N, summarizeOpenAITransportError as O, convertProviderResponsesMessages as P, OPENAI_RESPONSES_CALL_ID_RE as Q, claimOpenAIResponsesHttpContinuation as R, isInvalidEncryptedContentError as S, ResponsesStreamFailure as T, isConfigurationUpdate as U, responsesContinuationPrefixFingerprint as V, replayResponsesReasoningUpdates as W, resolveNewestOpenAIResponsesCompactionReplay as X, isOpenAIResponsesReplayContext as Y, suppressOpenAIResponsesCompaction as Z, isResponsesTextContentPartType as _, prepareResponsesTools as a, commitResponsesEncryptedContentAttempt as b, observeResponsesStream as c, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as d, OPENAI_RESPONSES_ID_MAX_LENGTH as et, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as f, isAzureResponsesTextDeltaEventType as g, isAzureResponsesTextDeltaEvent as h, convertResponsesToolPayload as i, resolveReplayableResponsesMessageId as it, resolveResponsesContextUsageBoundary as j, summarizeResponsesPayload as k, createResponsesToolCallTracker as l, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as m, applyResponsesServiceTierPricing as n, shouldNormalizeOpenAIResponsesToolCallId as nt, readOpenAIMisalignmentReview as o, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as p, buildOpenAIResponsesReasoningReplayMetadata as q, runResponsesStreamLifecycle as r, splitOpenAIFunctionCallPairing as rt, processResponsesStream as s, applyCommonResponsesParams as t, normalizeOpenAIResponsesFunctionCallId as tt, readResponsesToolCallItemIdentity as u, isResponsesTextDeltaEventType as v, resolveNextResponsesEncryptedContentAttempt as w, createResponsesStreamWithEncryptedContentRetry as x, resolveResponsesMessageSnapshotCollapse as y, recordResponsesContinuationState as z };
|