@fased/fased 0.1.57 → 0.1.59

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.
Files changed (172) hide show
  1. package/dist/agents/anthropic-payload-policy.js +188 -0
  2. package/dist/agents/anthropic-transport-stream.js +736 -0
  3. package/dist/agents/copilot-dynamic-headers.js +1 -1
  4. package/dist/agents/current-model-catalog.js +5 -2
  5. package/dist/agents/custom-api-registry.js +21 -0
  6. package/dist/agents/google-transport-stream.js +543 -0
  7. package/dist/agents/model-catalog-status.js +6 -1
  8. package/dist/agents/models-config.js +2 -1
  9. package/dist/agents/openai-completions-compat.js +49 -0
  10. package/dist/agents/openai-responses-payload-policy.js +100 -0
  11. package/dist/agents/openai-tool-schema.js +130 -0
  12. package/dist/agents/openai-transport-stream.js +1040 -0
  13. package/dist/agents/pi-embedded-runner/compact.js +9 -0
  14. package/dist/agents/pi-embedded-runner/run/attempt.js +9 -0
  15. package/dist/agents/pi-embedded-runner/session-manager-init.js +5 -20
  16. package/dist/agents/provider-extension-catalog-manifest.js +2 -2
  17. package/dist/agents/provider-request-config.js +1 -1
  18. package/dist/agents/provider-stream.js +29 -0
  19. package/dist/agents/provider-transport-fetch.js +89 -0
  20. package/dist/agents/provider-transport-stream.js +72 -0
  21. package/dist/agents/system-prompt-cache-boundary.js +1 -1
  22. package/dist/agents/system-prompt.js +1 -1
  23. package/dist/agents/transport-message-transform.js +143 -0
  24. package/dist/agents/transport-stream-shared.js +81 -0
  25. package/dist/brand.js +1 -1
  26. package/dist/build-info.json +3 -3
  27. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  28. package/dist/channels/delivery.js +78 -0
  29. package/dist/channels/plugins/catalog.js +10 -5
  30. package/dist/cli/components-cli.js +16 -13
  31. package/dist/cli/program/register.subclis.js +9 -0
  32. package/dist/cli/providers-cli.js +79 -6
  33. package/dist/cli/services-cli.js +28 -0
  34. package/dist/commands/auth-choice.js +1 -1
  35. package/dist/commands/models/list.auth-overview.js +12 -0
  36. package/dist/commands/onboard-channels.js +5 -0
  37. package/dist/commands/onboard-helpers.js +1 -1
  38. package/dist/commands/vllm-setup.js +12 -3
  39. package/dist/control-ui/assets/{app-DltSyY4Y.js → app-S_jg-ADS.js} +1706 -1675
  40. package/dist/control-ui/assets/{index-D5JlBp7f.js → index-C22rDOFm.js} +3 -3
  41. package/dist/control-ui/assets/{providers-Cc89iMdx.js → providers-QMn4w2_f.js} +3 -3
  42. package/dist/control-ui/index.html +1 -1
  43. package/dist/control-ui/version.json +1 -1
  44. package/dist/gateway/control-reply-text.js +10 -1
  45. package/dist/gateway/method-scopes.js +2 -0
  46. package/dist/gateway/protocol/schema/agents-models-skills.js +1 -0
  47. package/dist/gateway/server-chat.js +3 -3
  48. package/dist/gateway/server-methods/channels.js +10 -2
  49. package/dist/gateway/server-methods/chat.js +19 -12
  50. package/dist/gateway/server-methods/models.js +4 -0
  51. package/dist/gateway/server-methods/services.js +47 -2
  52. package/dist/gateway/server-methods-list.js +2 -0
  53. package/dist/gateway/session-event-payloads.js +20 -9
  54. package/dist/infra/google-api-base-url.js +29 -0
  55. package/dist/plugin-sdk/{accounts-CmMwe9Dt.js → accounts-DxblobYM.js} +1 -1
  56. package/dist/plugin-sdk/{actions-DYViFQiX.js → actions-D4UNimWa.js} +2 -2
  57. package/dist/plugin-sdk/{active-listener-DVbpcisf.js → active-listener-CFuy106s.js} +1 -1
  58. package/dist/plugin-sdk/{audio-preflight-D-Mfay8i.js → audio-preflight-CxZixyhW.js} +25 -25
  59. package/dist/plugin-sdk/{audit-JImYFOzJ.js → audit-Cs0K7LBp.js} +1 -1
  60. package/dist/plugin-sdk/{channel-access-cxCK2mci.js → channel-access-D5aHoTY8.js} +1 -1
  61. package/dist/plugin-sdk/{channel-access-configure-B_qYjml8.js → channel-access-configure-Dvch9mMQ.js} +1 -1
  62. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  63. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  64. package/dist/plugin-sdk/{chunk-CF8nvZNj.js → chunk-B0FUGc5A.js} +1 -1
  65. package/dist/plugin-sdk/{chunk-items-yLFoO571.js → chunk-items-Cy9a3X7x.js} +1 -1
  66. package/dist/plugin-sdk/command-status.js +25 -25
  67. package/dist/plugin-sdk/{commands-registry-CK4KmMG0.js → commands-registry-DmDRhRAU.js} +2 -2
  68. package/dist/plugin-sdk/{common-Cky4hpqr.js → common-CfUOowOp.js} +1 -1
  69. package/dist/plugin-sdk/{config-Dbxj_jUP.js → config-mxULv0fQ.js} +4 -1
  70. package/dist/plugin-sdk/{config-schema-aKPMn6ML.js → config-schema-D8E0LX8X.js} +4422 -486
  71. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  72. package/dist/plugin-sdk/discord.d.ts +4 -4
  73. package/dist/plugin-sdk/discord.js +19 -19
  74. package/dist/plugin-sdk/{handle-action-BSNDD2Vc.js → handle-action-DbVCF9HZ.js} +13 -13
  75. package/dist/plugin-sdk/{helpers-CDo2XBSr.js → helpers-BXbdgs5H.js} +1 -1
  76. package/dist/plugin-sdk/{image-ops-DbeBxbn4.js → image-ops-D7jPxaph.js} +1 -1
  77. package/dist/plugin-sdk/index.d.ts +15 -15
  78. package/dist/plugin-sdk/index.js +27 -27
  79. package/dist/plugin-sdk/{login-CWXvFTd8.js → login-BRxww6nv.js} +3 -3
  80. package/dist/plugin-sdk/{login-qr-CJB2zjmM.js → login-qr-DJXsk9XH.js} +3 -3
  81. package/dist/plugin-sdk/{manager-CbiFFLKS.js → manager-Y2S_5IlI.js} +2 -2
  82. package/dist/plugin-sdk/{markdown-tables-Bskt9r8i.js → markdown-tables-DHLFQqaF.js} +1 -1
  83. package/dist/plugin-sdk/{media-B3aAg5rh.js → media-Czo4carg.js} +2 -2
  84. package/dist/plugin-sdk/{monitor-BYGbTI5V.js → monitor-CFHFAUOj.js} +32 -32
  85. package/dist/plugin-sdk/{monitor-BJs6z7AT.js → monitor-xS9A8Upr.js} +29 -29
  86. package/dist/plugin-sdk/ollama-runtime.js +970 -0
  87. package/dist/plugin-sdk/{openresponses-http-BGCl2dS2.js → openresponses-http-BcUbeVV6.js} +25 -25
  88. package/dist/plugin-sdk/{optional-runtime-dependency-BlTVkcAg.js → optional-runtime-dependency-DdMbxS24.js} +1 -1
  89. package/dist/plugin-sdk/{outbound-CIemga5_.js → outbound-Db8mRN6P.js} +6 -6
  90. package/dist/plugin-sdk/{outbound-attachment-2RckWqrk.js → outbound-attachment-BqFjcypb.js} +2 -2
  91. package/dist/plugin-sdk/{pairing-message-r-VQfhFB.js → pairing-message-B3GC_7CD.js} +1 -1
  92. package/dist/plugin-sdk/{pairing-message-C403mq4K.d.ts → pairing-message-CxonJmwM.d.ts} +1 -1
  93. package/dist/plugin-sdk/{pairing-store-uPnLfGgB.js → pairing-store-CQikgIbw.js} +2 -2
  94. package/dist/plugin-sdk/{pi-model-discovery-BpEilAym.js → pi-model-discovery-CY3m5zJP.js} +1 -1
  95. package/dist/plugin-sdk/{pi-model-discovery-runtime-DwNdFaz-.js → pi-model-discovery-runtime-DSh1T2pn.js} +2 -2
  96. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  97. package/dist/plugin-sdk/{pw-ai-H7UgunQQ.js → pw-ai-AFUqn-tQ.js} +3 -3
  98. package/dist/plugin-sdk/{replies-CKMwkH4G.js → replies-Fzh9PhXl.js} +2 -2
  99. package/dist/plugin-sdk/{resolve-outbound-target-B68fSoGi.js → resolve-outbound-target-KpF3co6T.js} +1 -1
  100. package/dist/plugin-sdk/{resolve-route-B8fOfihG.js → resolve-route-CDl74O19.js} +1 -1
  101. package/dist/plugin-sdk/{retry-policy-B6GQldxB.js → retry-policy-D4uiqUOG.js} +1 -1
  102. package/dist/plugin-sdk/{runtime-nVJl3PlS.js → runtime-Bb6KRnCC.js} +6 -4
  103. package/dist/plugin-sdk/{runtime-dependency-DlqVSsSs.js → runtime-dependency-DLDmB94f.js} +11 -11
  104. package/dist/plugin-sdk/sat-runtime.d.ts +2 -2
  105. package/dist/plugin-sdk/sat-runtime.js +25 -25
  106. package/dist/plugin-sdk/{send-CPK_HlYW.js → send-5nFktR1n.js} +5 -5
  107. package/dist/plugin-sdk/{send-hSu1HWms.js → send-Bfzf11VY.js} +7 -7
  108. package/dist/plugin-sdk/{send-D3DQOg2a.js → send-C4k4JuJN.js} +6 -6
  109. package/dist/plugin-sdk/{send-BHokB4TR.js → send-D-uDKGIq.js} +6 -6
  110. package/dist/plugin-sdk/{send-BCJ4hi3t.js → send-Du8ykP1k.js} +8 -8
  111. package/dist/plugin-sdk/{session-DQ4Dr-mf.js → session-CS-Td9EM.js} +2 -2
  112. package/dist/plugin-sdk/{skill-commands-CmdoKLh_.js → skill-commands-CFAlXWHk.js} +2 -2
  113. package/dist/plugin-sdk/{slack-B70NHpMF.js → slack-DXYGbizH.js} +37 -37
  114. package/dist/plugin-sdk/{slack-actions-fSmP981b.js → slack-actions-BcMkNv1Z.js} +11 -11
  115. package/dist/plugin-sdk/slack.d.ts +4 -4
  116. package/dist/plugin-sdk/slack.js +9 -9
  117. package/dist/plugin-sdk/{sticker-cache-B4dG2sKU.js → sticker-cache-Dr3rYfU1.js} +3 -3
  118. package/dist/plugin-sdk/{tables-EX4Hy-a-.js → tables-bFXyRpkx.js} +1 -1
  119. package/dist/plugin-sdk/{targets-BeGUCGWK.js → targets-DH7a9lKB.js} +1 -1
  120. package/dist/plugin-sdk/{telegram-actions-CM8SJgc9.js → telegram-actions-Besbw_Hg.js} +28 -28
  121. package/dist/plugin-sdk/telegram.d.ts +4 -4
  122. package/dist/plugin-sdk/telegram.js +8 -8
  123. package/dist/plugin-sdk/{thread-bindings.lifecycle-tHK_1mvp.js → thread-bindings.lifecycle-CgOvvdw8.js} +3 -3
  124. package/dist/plugin-sdk/{thread-bindings.state-D7xG1GHu.js → thread-bindings.state-CQpk44Hq.js} +2 -2
  125. package/dist/plugin-sdk/{tmp-fased-dir-Ouj58axY.d.ts → tmp-fased-dir-DQ00xQ5D.d.ts} +1 -1
  126. package/dist/plugin-sdk/{types-B4hj6HgR.d.ts → types-B6zx-Dl2.d.ts} +1 -1
  127. package/dist/plugin-sdk/{types-dWZFX1fk.d.ts → types-eUM7hcDz.d.ts} +1 -1
  128. package/dist/plugin-sdk/{web-BFolfZ3i.js → web-Q1-G--JH.js} +30 -30
  129. package/dist/plugin-sdk/{whatsapp-actions-CYmleHCz.js → whatsapp-actions-BtSc2CBv.js} +13 -13
  130. package/dist/plugin-sdk/{whatsapp-heartbeat-BEwm4GK3.js → whatsapp-heartbeat-Bg9TuYzl.js} +2 -2
  131. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  132. package/dist/plugin-sdk/whatsapp.js +12 -12
  133. package/dist/plugin-sdk/{wsl-BtMDzMv5.js → wsl-C4DGFkOd.js} +3 -3
  134. package/dist/plugin-sdk/{zod-schema.providers-core-FaYR6KdD.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
  135. package/dist/plugins/provider-runtime.js +79 -0
  136. package/dist/providers/refresh.js +14 -0
  137. package/extensions/acpx/package.json +1 -1
  138. package/extensions/bluebubbles/package.json +1 -1
  139. package/extensions/copilot-proxy/package.json +1 -1
  140. package/extensions/diagnostics-otel/package.json +1 -1
  141. package/extensions/fased-federation/package.json +1 -1
  142. package/extensions/google-gemini-cli-auth/package.json +1 -1
  143. package/extensions/imessage/package.json +1 -1
  144. package/extensions/irc/package.json +1 -1
  145. package/extensions/line/package.json +1 -1
  146. package/extensions/llm-task/package.json +1 -1
  147. package/extensions/lobster/package.json +1 -1
  148. package/extensions/matrix/CHANGELOG.md +12 -0
  149. package/extensions/matrix/package.json +1 -1
  150. package/extensions/mattermost/package.json +1 -1
  151. package/extensions/memory-core/package.json +2 -2
  152. package/extensions/memory-lancedb/package.json +1 -1
  153. package/extensions/minimax-portal-auth/package.json +1 -1
  154. package/extensions/msteams/CHANGELOG.md +12 -0
  155. package/extensions/msteams/package.json +1 -1
  156. package/extensions/nextcloud-talk/package.json +1 -1
  157. package/extensions/nostr/CHANGELOG.md +12 -0
  158. package/extensions/nostr/package.json +1 -1
  159. package/extensions/open-prose/package.json +1 -1
  160. package/extensions/sat-mining/package.json +1 -1
  161. package/extensions/signal/package.json +1 -1
  162. package/extensions/synology-chat/package.json +1 -1
  163. package/extensions/tlon/package.json +1 -1
  164. package/extensions/twitch/CHANGELOG.md +12 -0
  165. package/extensions/twitch/package.json +1 -1
  166. package/extensions/voice-call/CHANGELOG.md +12 -0
  167. package/extensions/voice-call/package.json +1 -1
  168. package/extensions/zalo/CHANGELOG.md +12 -0
  169. package/extensions/zalo/package.json +1 -1
  170. package/extensions/zalouser/CHANGELOG.md +12 -0
  171. package/extensions/zalouser/package.json +1 -1
  172. package/package.json +1 -1
@@ -0,0 +1,1040 @@
1
+ import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./copilot-dynamic-headers.js";
2
+ import { resolveProviderTransportTurnStateWithPlugin } from "../plugins/provider-runtime.js";
3
+ import { stripSystemPromptCacheBoundary } from "./system-prompt-cache-boundary.js";
4
+ import { buildGuardedModelFetch } from "./provider-transport-fetch.js";
5
+ import { transformTransportMessages } from "./transport-message-transform.js";
6
+ import { mergeTransportMetadata, sanitizeTransportPayloadText } from "./transport-stream-shared.js";
7
+ import { detectOpenAICompletionsCompat } from "./openai-completions-compat.js";
8
+ import { applyOpenAIResponsesPayloadPolicy, resolveOpenAIResponsesPayloadPolicy } from "./openai-responses-payload-policy.js";
9
+ import { normalizeOpenAIStrictToolParameters, resolveOpenAIStrictToolFlagForInventory, resolveOpenAIStrictToolSetting } from "./openai-tool-schema.js";
10
+ import { randomUUID } from "node:crypto";
11
+ import { calculateCost, createAssistantMessageEventStream, getEnvApiKey, parseStreamingJson } from "@mariozechner/pi-ai/compat";
12
+ import { convertMessages } from "@mariozechner/pi-ai/api/openai-completions";
13
+
14
+ //#region src/agents/openai-transport-stream.ts
15
+ function resolveAzureOpenAIApiVersion(env = process.env) {
16
+ return env.AZURE_OPENAI_API_VERSION?.trim() || "2024-12-01-preview";
17
+ }
18
+ function stringifyUnknown(value, fallback = "") {
19
+ if (typeof value === "string") {
20
+ return value;
21
+ }
22
+ if (typeof value === "number" || typeof value === "boolean") {
23
+ return String(value);
24
+ }
25
+ return fallback;
26
+ }
27
+ function stringifyJsonLike(value, fallback = "") {
28
+ if (typeof value === "string") {
29
+ return value;
30
+ }
31
+ if (value && typeof value === "object") {
32
+ return JSON.stringify(value);
33
+ }
34
+ if (typeof value === "number" || typeof value === "boolean") {
35
+ return String(value);
36
+ }
37
+ return fallback;
38
+ }
39
+ function getServiceTierCostMultiplier(serviceTier) {
40
+ switch (serviceTier) {
41
+ case "flex": return .5;
42
+ case "priority": return 2;
43
+ default: return 1;
44
+ }
45
+ }
46
+ function applyServiceTierPricing(usage, serviceTier) {
47
+ const multiplier = getServiceTierCostMultiplier(serviceTier);
48
+ if (multiplier === 1) {
49
+ return;
50
+ }
51
+ usage.cost.input *= multiplier;
52
+ usage.cost.output *= multiplier;
53
+ usage.cost.cacheRead *= multiplier;
54
+ usage.cost.cacheWrite *= multiplier;
55
+ usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
56
+ }
57
+ function resolveOpenAIEndpoint(baseURL, path) {
58
+ const base = (baseURL?.trim() || "https://api.openai.com/v1").replace(/\/+$/, "");
59
+ return `${base}${path}`;
60
+ }
61
+ async function* parseServerSentJson(response) {
62
+ if (!response.body) {
63
+ const text = await response.text();
64
+ for (const line of text.split(/\r?\n/)) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed || trimmed === "[DONE]") {
67
+ continue;
68
+ }
69
+ yield JSON.parse(trimmed);
70
+ }
71
+ return;
72
+ }
73
+ const reader = response.body.getReader();
74
+ const decoder = new TextDecoder();
75
+ let buffer = "";
76
+ const emitBlock = function* (block) {
77
+ const data = block.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice("data:".length).trim()).join("\n").trim();
78
+ if (!data || data === "[DONE]") {
79
+ return;
80
+ }
81
+ yield JSON.parse(data);
82
+ };
83
+ while (true) {
84
+ const chunk = await reader.read();
85
+ if (chunk.done) {
86
+ break;
87
+ }
88
+ buffer += decoder.decode(chunk.value, { stream: true });
89
+ let boundary = buffer.indexOf("\n\n");
90
+ while (boundary >= 0) {
91
+ const block = buffer.slice(0, boundary);
92
+ buffer = buffer.slice(boundary + 2);
93
+ for (const event of emitBlock(block)) {
94
+ yield event;
95
+ }
96
+ boundary = buffer.indexOf("\n\n");
97
+ }
98
+ }
99
+ buffer += decoder.decode();
100
+ if (buffer.trim()) {
101
+ for (const event of emitBlock(buffer)) {
102
+ yield event;
103
+ }
104
+ }
105
+ }
106
+ function createOpenAIStreamClient(options) {
107
+ const request = async (path, payload, init) => {
108
+ const response = await options.fetch(resolveOpenAIEndpoint(options.baseURL, path), {
109
+ method: "POST",
110
+ headers: {
111
+ authorization: `Bearer ${options.apiKey}`,
112
+ "content-type": "application/json",
113
+ accept: "text/event-stream",
114
+ ...options.defaultHeaders
115
+ },
116
+ body: JSON.stringify(payload),
117
+ signal: init?.signal
118
+ });
119
+ if (!response.ok) {
120
+ const text = await response.text().catch(() => "");
121
+ throw new Error(`OpenAI transport request failed (${response.status} ${response.statusText})${text ? `: ${text.slice(0, 500)}` : ""}`);
122
+ }
123
+ return parseServerSentJson(response);
124
+ };
125
+ return {
126
+ responses: { create: (payload, init) => request("/responses", payload, init) },
127
+ chat: { completions: { create: (payload, init) => request("/chat/completions", payload, init) } }
128
+ };
129
+ }
130
+ async function resolvePayloadOverride(onPayload, payload, model) {
131
+ const nextPayload = onPayload?.(payload, model);
132
+ return nextPayload instanceof Promise ? await nextPayload : nextPayload;
133
+ }
134
+ function shortHash(value) {
135
+ let hash = 0;
136
+ for (let i = 0; i < value.length; i += 1) {
137
+ hash = hash * 31 + value.charCodeAt(i) | 0;
138
+ }
139
+ return Math.abs(hash).toString(36);
140
+ }
141
+ function encodeTextSignatureV1(id, phase) {
142
+ return JSON.stringify({
143
+ v: 1,
144
+ id,
145
+ ...phase ? { phase } : {}
146
+ });
147
+ }
148
+ function parseTextSignature(signature) {
149
+ if (!signature) {
150
+ return undefined;
151
+ }
152
+ if (signature.startsWith("{")) {
153
+ try {
154
+ const parsed = JSON.parse(signature);
155
+ if (parsed.v === 1 && typeof parsed.id === "string") {
156
+ return parsed.phase === "commentary" || parsed.phase === "final_answer" ? {
157
+ id: parsed.id,
158
+ phase: parsed.phase
159
+ } : { id: parsed.id };
160
+ }
161
+ } catch {}
162
+ }
163
+ return { id: signature };
164
+ }
165
+ function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
166
+ const messages = [];
167
+ const normalizeIdPart = (part) => {
168
+ const sanitized = part.replace(/[^a-zA-Z0-9_-]/g, "_");
169
+ const normalized = sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized;
170
+ return normalized.replace(/_+$/, "");
171
+ };
172
+ const buildForeignResponsesItemId = (itemId) => {
173
+ const normalized = `fc_${shortHash(itemId)}`;
174
+ return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
175
+ };
176
+ const normalizeToolCallId = (id, _targetModel, source) => {
177
+ if (!allowedToolCallProviders.has(model.provider)) {
178
+ return normalizeIdPart(id);
179
+ }
180
+ if (!id.includes("|")) {
181
+ return normalizeIdPart(id);
182
+ }
183
+ const [callId, itemId] = id.split("|");
184
+ const normalizedCallId = normalizeIdPart(callId);
185
+ const isForeignToolCall = source.provider !== model.provider || source.api !== model.api;
186
+ let normalizedItemId = isForeignToolCall ? buildForeignResponsesItemId(itemId) : normalizeIdPart(itemId);
187
+ if (!normalizedItemId.startsWith("fc_")) {
188
+ normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
189
+ }
190
+ return `${normalizedCallId}|${normalizedItemId}`;
191
+ };
192
+ const transformedMessages = transformTransportMessages(context.messages, model, normalizeToolCallId);
193
+ const includeSystemPrompt = options?.includeSystemPrompt ?? true;
194
+ if (includeSystemPrompt && context.systemPrompt) {
195
+ messages.push({
196
+ role: model.reasoning && options?.supportsDeveloperRole !== false ? "developer" : "system",
197
+ content: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
198
+ });
199
+ }
200
+ let msgIndex = 0;
201
+ for (const msg of transformedMessages) {
202
+ if (msg.role === "user") {
203
+ if (typeof msg.content === "string") {
204
+ messages.push({
205
+ role: "user",
206
+ content: [{
207
+ type: "input_text",
208
+ text: sanitizeTransportPayloadText(msg.content)
209
+ }]
210
+ });
211
+ } else {
212
+ const content = msg.content.map((item) => item.type === "text" ? {
213
+ type: "input_text",
214
+ text: sanitizeTransportPayloadText(item.text ?? "")
215
+ } : {
216
+ type: "input_image",
217
+ detail: "auto",
218
+ image_url: `data:${item.mimeType};base64,${item.data}`
219
+ }).filter((item) => model.input.includes("image") || item.type !== "input_image");
220
+ if (content.length > 0) {
221
+ messages.push({
222
+ role: "user",
223
+ content
224
+ });
225
+ }
226
+ }
227
+ } else if (msg.role === "assistant") {
228
+ const output = [];
229
+ const isDifferentModel = msg.model !== model.id && msg.provider === model.provider && msg.api === model.api;
230
+ for (const block of msg.content) {
231
+ if (block.type === "thinking") {
232
+ if (block.thinkingSignature) {
233
+ output.push(JSON.parse(block.thinkingSignature));
234
+ }
235
+ } else if (block.type === "text") {
236
+ let msgId = parseTextSignature(block.textSignature)?.id ?? `msg_${msgIndex}`;
237
+ if (msgId.length > 64) {
238
+ msgId = `msg_${shortHash(msgId)}`;
239
+ }
240
+ output.push({
241
+ type: "message",
242
+ role: "assistant",
243
+ content: [{
244
+ type: "output_text",
245
+ text: sanitizeTransportPayloadText(block.text),
246
+ annotations: []
247
+ }],
248
+ status: "completed",
249
+ id: msgId,
250
+ phase: parseTextSignature(block.textSignature)?.phase
251
+ });
252
+ } else if (block.type === "toolCall") {
253
+ const [callId, itemIdRaw] = block.id.split("|");
254
+ const itemId = isDifferentModel && itemIdRaw?.startsWith("fc_") ? undefined : itemIdRaw;
255
+ output.push({
256
+ type: "function_call",
257
+ id: itemId,
258
+ call_id: callId,
259
+ name: block.name,
260
+ arguments: JSON.stringify(block.arguments)
261
+ });
262
+ }
263
+ }
264
+ if (output.length > 0) {
265
+ messages.push(...output);
266
+ }
267
+ } else if (msg.role === "toolResult") {
268
+ const textResult = msg.content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
269
+ const hasImages = msg.content.some((item) => item.type === "image");
270
+ const [callId] = msg.toolCallId.split("|");
271
+ messages.push({
272
+ type: "function_call_output",
273
+ call_id: callId,
274
+ output: hasImages && model.input.includes("image") ? [...textResult ? [{
275
+ type: "input_text",
276
+ text: sanitizeTransportPayloadText(textResult)
277
+ }] : [], ...msg.content.filter((item) => item.type === "image").map((item) => ({
278
+ type: "input_image",
279
+ detail: "auto",
280
+ image_url: `data:${item.mimeType};base64,${item.data}`
281
+ }))] : sanitizeTransportPayloadText(textResult || "(see attached image)")
282
+ });
283
+ }
284
+ msgIndex += 1;
285
+ }
286
+ return messages;
287
+ }
288
+ function convertResponsesTools(tools, options) {
289
+ const strict = resolveOpenAIStrictToolFlagForInventory(tools, options?.strict);
290
+ if (strict === undefined) {
291
+ return tools.map((tool) => ({
292
+ type: "function",
293
+ name: tool.name,
294
+ description: tool.description,
295
+ parameters: tool.parameters
296
+ }));
297
+ }
298
+ return tools.map((tool) => ({
299
+ type: "function",
300
+ name: tool.name,
301
+ description: tool.description,
302
+ parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict),
303
+ strict
304
+ }));
305
+ }
306
+ async function processResponsesStream(openaiStream, output, stream, model, options) {
307
+ let currentItem = null;
308
+ let currentBlock = null;
309
+ const blockIndex = () => output.content.length - 1;
310
+ for await (const rawEvent of openaiStream) {
311
+ const event = rawEvent;
312
+ const type = stringifyUnknown(event.type);
313
+ if (type === "response.created") {
314
+ output.responseId = stringifyUnknown(event.response?.id);
315
+ } else if (type === "response.output_item.added") {
316
+ const item = event.item;
317
+ if (item.type === "reasoning") {
318
+ currentItem = item;
319
+ currentBlock = {
320
+ type: "thinking",
321
+ thinking: ""
322
+ };
323
+ output.content.push(currentBlock);
324
+ stream.push({
325
+ type: "thinking_start",
326
+ contentIndex: blockIndex(),
327
+ partial: output
328
+ });
329
+ } else if (item.type === "message") {
330
+ currentItem = item;
331
+ currentBlock = {
332
+ type: "text",
333
+ text: ""
334
+ };
335
+ output.content.push(currentBlock);
336
+ stream.push({
337
+ type: "text_start",
338
+ contentIndex: blockIndex(),
339
+ partial: output
340
+ });
341
+ } else if (item.type === "function_call") {
342
+ currentItem = item;
343
+ currentBlock = {
344
+ type: "toolCall",
345
+ id: `${stringifyUnknown(item.call_id)}|${stringifyUnknown(item.id)}`,
346
+ name: stringifyUnknown(item.name),
347
+ arguments: {},
348
+ partialJson: stringifyJsonLike(item.arguments)
349
+ };
350
+ output.content.push(currentBlock);
351
+ stream.push({
352
+ type: "toolcall_start",
353
+ contentIndex: blockIndex(),
354
+ partial: output
355
+ });
356
+ }
357
+ } else if (type === "response.reasoning_summary_text.delta") {
358
+ if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
359
+ currentBlock.thinking = `${stringifyUnknown(currentBlock.thinking)}${stringifyUnknown(event.delta)}`;
360
+ stream.push({
361
+ type: "thinking_delta",
362
+ contentIndex: blockIndex(),
363
+ delta: stringifyUnknown(event.delta),
364
+ partial: output
365
+ });
366
+ }
367
+ } else if (type === "response.output_text.delta" || type === "response.refusal.delta") {
368
+ if (currentItem?.type === "message" && currentBlock?.type === "text") {
369
+ currentBlock.text = `${stringifyUnknown(currentBlock.text)}${stringifyUnknown(event.delta)}`;
370
+ stream.push({
371
+ type: "text_delta",
372
+ contentIndex: blockIndex(),
373
+ delta: stringifyUnknown(event.delta),
374
+ partial: output
375
+ });
376
+ }
377
+ } else if (type === "response.function_call_arguments.delta") {
378
+ if (currentItem?.type === "function_call" && currentBlock?.type === "toolCall") {
379
+ currentBlock.partialJson = `${stringifyJsonLike(currentBlock.partialJson)}${stringifyJsonLike(event.delta)}`;
380
+ currentBlock.arguments = parseStreamingJson(stringifyJsonLike(currentBlock.partialJson));
381
+ stream.push({
382
+ type: "toolcall_delta",
383
+ contentIndex: blockIndex(),
384
+ delta: stringifyJsonLike(event.delta),
385
+ partial: output
386
+ });
387
+ }
388
+ } else if (type === "response.output_item.done") {
389
+ const item = event.item;
390
+ if (item.type === "reasoning" && currentBlock?.type === "thinking") {
391
+ const summary = Array.isArray(item.summary) ? item.summary.map((part) => String(part.text ?? "")).join("\n\n") : "";
392
+ currentBlock.thinking = summary;
393
+ currentBlock.thinkingSignature = JSON.stringify(item);
394
+ stream.push({
395
+ type: "thinking_end",
396
+ contentIndex: blockIndex(),
397
+ content: stringifyUnknown(currentBlock.thinking),
398
+ partial: output
399
+ });
400
+ currentBlock = null;
401
+ } else if (item.type === "message" && currentBlock?.type === "text") {
402
+ const content = Array.isArray(item.content) ? item.content : [];
403
+ currentBlock.text = content.map((part) => part.type === "output_text" ? String(part.text ?? "") : String(part.refusal ?? "")).join("");
404
+ currentBlock.textSignature = encodeTextSignatureV1(stringifyUnknown(item.id), item.phase ?? undefined);
405
+ stream.push({
406
+ type: "text_end",
407
+ contentIndex: blockIndex(),
408
+ content: stringifyUnknown(currentBlock.text),
409
+ partial: output
410
+ });
411
+ currentBlock = null;
412
+ } else if (item.type === "function_call") {
413
+ const args = currentBlock?.type === "toolCall" && currentBlock.partialJson ? parseStreamingJson(stringifyJsonLike(currentBlock.partialJson, "{}")) : parseStreamingJson(stringifyJsonLike(item.arguments, "{}"));
414
+ stream.push({
415
+ type: "toolcall_end",
416
+ contentIndex: blockIndex(),
417
+ toolCall: {
418
+ type: "toolCall",
419
+ id: `${stringifyUnknown(item.call_id)}|${stringifyUnknown(item.id)}`,
420
+ name: stringifyUnknown(item.name),
421
+ arguments: args
422
+ },
423
+ partial: output
424
+ });
425
+ currentBlock = null;
426
+ }
427
+ } else if (type === "response.completed") {
428
+ const response = event.response;
429
+ if (typeof response?.id === "string") {
430
+ output.responseId = response.id;
431
+ }
432
+ const usage = response?.usage;
433
+ if (usage) {
434
+ const cachedTokens = usage.input_tokens_details?.cached_tokens || 0;
435
+ output.usage = {
436
+ input: (usage.input_tokens || 0) - cachedTokens,
437
+ output: usage.output_tokens || 0,
438
+ cacheRead: cachedTokens,
439
+ cacheWrite: 0,
440
+ totalTokens: usage.total_tokens || 0,
441
+ cost: {
442
+ input: 0,
443
+ output: 0,
444
+ cacheRead: 0,
445
+ cacheWrite: 0,
446
+ total: 0
447
+ }
448
+ };
449
+ }
450
+ calculateCost(model, output.usage);
451
+ if (options?.applyServiceTierPricing) {
452
+ options.applyServiceTierPricing(output.usage, response?.service_tier ?? options.serviceTier);
453
+ }
454
+ output.stopReason = mapResponsesStopReason(response?.status);
455
+ if (output.content.some((block) => block.type === "toolCall") && output.stopReason === "stop") {
456
+ output.stopReason = "toolUse";
457
+ }
458
+ } else if (type === "error") {
459
+ throw new Error(`Error Code ${stringifyUnknown(event.code, "unknown")}: ${stringifyUnknown(event.message, "Unknown error")}`);
460
+ } else if (type === "response.failed") {
461
+ const response = event.response;
462
+ const msg = response?.error ? `${response.error.code || "unknown"}: ${response.error.message || "no message"}` : response?.incomplete_details?.reason ? `incomplete: ${response.incomplete_details.reason}` : "Unknown error (no error details in response)";
463
+ throw new Error(msg);
464
+ }
465
+ }
466
+ }
467
+ function mapResponsesStopReason(status) {
468
+ if (!status) {
469
+ return "stop";
470
+ }
471
+ switch (status) {
472
+ case "completed": return "stop";
473
+ case "incomplete": return "length";
474
+ case "failed":
475
+ case "cancelled": return "error";
476
+ case "in_progress":
477
+ case "queued": return "stop";
478
+ default: throw new Error(`Unhandled stop reason: ${status}`);
479
+ }
480
+ }
481
+ function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders) {
482
+ const headers = { ...model.headers };
483
+ if (model.provider === "github-copilot") {
484
+ Object.assign(headers, buildCopilotDynamicHeaders({
485
+ messages: context.messages,
486
+ hasImages: hasCopilotVisionInput(context.messages)
487
+ }));
488
+ }
489
+ if (optionHeaders) {
490
+ Object.assign(headers, optionHeaders);
491
+ }
492
+ if (turnHeaders) {
493
+ Object.assign(headers, turnHeaders);
494
+ }
495
+ return headers;
496
+ }
497
+ function resolveProviderTransportTurnState(model, params) {
498
+ return resolveProviderTransportTurnStateWithPlugin({
499
+ provider: model.provider,
500
+ context: {
501
+ provider: model.provider,
502
+ modelId: model.id,
503
+ model,
504
+ sessionId: params.sessionId,
505
+ turnId: params.turnId,
506
+ attempt: params.attempt,
507
+ transport: params.transport
508
+ }
509
+ });
510
+ }
511
+ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders) {
512
+ return createOpenAIStreamClient({
513
+ apiKey,
514
+ baseURL: model.baseUrl,
515
+ defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders),
516
+ fetch: buildGuardedModelFetch(model)
517
+ });
518
+ }
519
+ function createOpenAIResponsesTransportStreamFn() {
520
+ return (model, context, options) => {
521
+ const eventStream = createAssistantMessageEventStream();
522
+ const stream = eventStream;
523
+ void (async () => {
524
+ const output = {
525
+ role: "assistant",
526
+ content: [],
527
+ api: model.api,
528
+ provider: model.provider,
529
+ model: model.id,
530
+ usage: {
531
+ input: 0,
532
+ output: 0,
533
+ cacheRead: 0,
534
+ cacheWrite: 0,
535
+ totalTokens: 0,
536
+ cost: {
537
+ input: 0,
538
+ output: 0,
539
+ cacheRead: 0,
540
+ cacheWrite: 0,
541
+ total: 0
542
+ }
543
+ },
544
+ stopReason: "stop",
545
+ timestamp: Date.now()
546
+ };
547
+ try {
548
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
549
+ const turnState = resolveProviderTransportTurnState(model, {
550
+ sessionId: options?.sessionId,
551
+ turnId: randomUUID(),
552
+ attempt: 1,
553
+ transport: "stream"
554
+ });
555
+ const client = createOpenAIResponsesClient(model, context, apiKey, options?.headers, turnState?.headers);
556
+ let params = buildOpenAIResponsesParams(model, context, options, turnState?.metadata);
557
+ const nextParams = await resolvePayloadOverride(options?.onPayload, params, model);
558
+ if (nextParams !== undefined) {
559
+ params = nextParams;
560
+ }
561
+ params = mergeTransportMetadata(params, turnState?.metadata);
562
+ const responseStream = await client.responses.create(params, options?.signal ? { signal: options.signal } : undefined);
563
+ stream.push({
564
+ type: "start",
565
+ partial: output
566
+ });
567
+ await processResponsesStream(responseStream, output, stream, model, {
568
+ serviceTier: options?.serviceTier,
569
+ applyServiceTierPricing
570
+ });
571
+ if (options?.signal?.aborted) {
572
+ throw new Error("Request was aborted");
573
+ }
574
+ if (output.stopReason === "aborted" || output.stopReason === "error") {
575
+ throw new Error("An unknown error occurred");
576
+ }
577
+ stream.push({
578
+ type: "done",
579
+ reason: output.stopReason,
580
+ message: output
581
+ });
582
+ stream.end();
583
+ } catch (error) {
584
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
585
+ output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
586
+ stream.push({
587
+ type: "error",
588
+ reason: output.stopReason,
589
+ error: output
590
+ });
591
+ stream.end();
592
+ }
593
+ })();
594
+ return eventStream;
595
+ };
596
+ }
597
+ function resolveCacheRetention(cacheRetention) {
598
+ if (cacheRetention === "short" || cacheRetention === "long" || cacheRetention === "none") {
599
+ return cacheRetention;
600
+ }
601
+ if (typeof process !== "undefined" && process.env.PI_CACHE_RETENTION === "long") {
602
+ return "long";
603
+ }
604
+ return "short";
605
+ }
606
+ function getPromptCacheRetention(baseUrl, cacheRetention) {
607
+ if (cacheRetention !== "long") {
608
+ return undefined;
609
+ }
610
+ return baseUrl?.includes("api.openai.com") ? "24h" : undefined;
611
+ }
612
+ function buildOpenAIResponsesParams(model, context, options, metadata) {
613
+ const compat = getCompat(model);
614
+ const supportsDeveloperRole = typeof compat.supportsDeveloperRole === "boolean" ? compat.supportsDeveloperRole : undefined;
615
+ const messages = convertResponsesMessages(model, context, new Set([
616
+ "openai",
617
+ "openai-codex",
618
+ "opencode"
619
+ ]), { supportsDeveloperRole });
620
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
621
+ const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
622
+ const params = {
623
+ model: model.id,
624
+ input: messages,
625
+ stream: true,
626
+ prompt_cache_key: cacheRetention === "none" ? undefined : options?.sessionId,
627
+ prompt_cache_retention: getPromptCacheRetention(model.baseUrl, cacheRetention),
628
+ ...metadata ? { metadata } : {}
629
+ };
630
+ if (options?.maxTokens) {
631
+ params.max_output_tokens = options.maxTokens;
632
+ }
633
+ if (options?.temperature !== undefined) {
634
+ params.temperature = options.temperature;
635
+ }
636
+ if (options?.serviceTier !== undefined && payloadPolicy.allowsServiceTier) {
637
+ params.service_tier = options.serviceTier;
638
+ }
639
+ if (context.tools) {
640
+ params.tools = convertResponsesTools(context.tools, { strict: resolveOpenAIStrictToolSetting(model, { transport: "stream" }) });
641
+ }
642
+ const reasoningEffort = options?.reasoningEffort ?? options?.reasoning ?? (model.reasoning ? "high" : undefined);
643
+ if (model.reasoning) {
644
+ if (reasoningEffort || options?.reasoningSummary) {
645
+ params.reasoning = {
646
+ effort: reasoningEffort || "medium",
647
+ summary: options?.reasoningSummary || "auto"
648
+ };
649
+ params.include = ["reasoning.encrypted_content"];
650
+ } else if (model.provider !== "github-copilot") {
651
+ params.reasoning = { effort: "none" };
652
+ }
653
+ }
654
+ applyOpenAIResponsesPayloadPolicy(params, payloadPolicy);
655
+ return params;
656
+ }
657
+ function hasToolHistory(messages) {
658
+ return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && message.content.some((block) => block.type === "toolCall"));
659
+ }
660
+ function createOpenAICompletionsClient(model, context, apiKey, optionHeaders) {
661
+ return createOpenAIStreamClient({
662
+ apiKey,
663
+ baseURL: model.baseUrl,
664
+ defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders),
665
+ fetch: buildGuardedModelFetch(model)
666
+ });
667
+ }
668
+ function createOpenAICompletionsTransportStreamFn() {
669
+ return (model, context, options) => {
670
+ const eventStream = createAssistantMessageEventStream();
671
+ const stream = eventStream;
672
+ void (async () => {
673
+ const output = {
674
+ role: "assistant",
675
+ content: [],
676
+ api: model.api,
677
+ provider: model.provider,
678
+ model: model.id,
679
+ usage: {
680
+ input: 0,
681
+ output: 0,
682
+ cacheRead: 0,
683
+ cacheWrite: 0,
684
+ totalTokens: 0,
685
+ cost: {
686
+ input: 0,
687
+ output: 0,
688
+ cacheRead: 0,
689
+ cacheWrite: 0,
690
+ total: 0
691
+ }
692
+ },
693
+ stopReason: "stop",
694
+ timestamp: Date.now()
695
+ };
696
+ try {
697
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
698
+ const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers);
699
+ let params = buildOpenAICompletionsParams(model, context, options);
700
+ const nextParams = await resolvePayloadOverride(options?.onPayload, params, model);
701
+ if (nextParams !== undefined) {
702
+ params = nextParams;
703
+ }
704
+ const responseStream = await client.chat.completions.create(params, { signal: options?.signal });
705
+ stream.push({
706
+ type: "start",
707
+ partial: output
708
+ });
709
+ await processOpenAICompletionsStream(responseStream, output, model, stream);
710
+ if (options?.signal?.aborted) {
711
+ throw new Error("Request was aborted");
712
+ }
713
+ stream.push({
714
+ type: "done",
715
+ reason: output.stopReason,
716
+ message: output
717
+ });
718
+ stream.end();
719
+ } catch (error) {
720
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
721
+ output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
722
+ stream.push({
723
+ type: "error",
724
+ reason: output.stopReason,
725
+ error: output
726
+ });
727
+ stream.end();
728
+ }
729
+ })();
730
+ return eventStream;
731
+ };
732
+ }
733
+ async function processOpenAICompletionsStream(responseStream, output, model, stream) {
734
+ let currentBlock = null;
735
+ const blockIndex = () => output.content.length - 1;
736
+ const finishCurrentBlock = () => {
737
+ if (!currentBlock) {
738
+ return;
739
+ }
740
+ if (currentBlock.type === "toolCall") {
741
+ currentBlock.arguments = parseStreamingJson(currentBlock.partialArgs);
742
+ const completed = {
743
+ ...currentBlock,
744
+ arguments: parseStreamingJson(currentBlock.partialArgs)
745
+ };
746
+ output.content[blockIndex()] = completed;
747
+ }
748
+ };
749
+ for await (const chunk of responseStream) {
750
+ output.responseId ||= chunk.id;
751
+ if (chunk.usage) {
752
+ output.usage = parseTransportChunkUsage(chunk.usage, model);
753
+ }
754
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : undefined;
755
+ if (!choice) {
756
+ continue;
757
+ }
758
+ const choiceUsage = choice.usage;
759
+ if (!chunk.usage && choiceUsage) {
760
+ output.usage = parseTransportChunkUsage(choiceUsage, model);
761
+ }
762
+ if (choice.finish_reason) {
763
+ const finishReasonResult = mapStopReason(choice.finish_reason);
764
+ output.stopReason = finishReasonResult.stopReason;
765
+ if (finishReasonResult.errorMessage) {
766
+ output.errorMessage = finishReasonResult.errorMessage;
767
+ }
768
+ }
769
+ if (!choice.delta) {
770
+ continue;
771
+ }
772
+ if (choice.delta.content) {
773
+ if (!currentBlock || currentBlock.type !== "text") {
774
+ finishCurrentBlock();
775
+ currentBlock = {
776
+ type: "text",
777
+ text: ""
778
+ };
779
+ output.content.push(currentBlock);
780
+ stream.push({
781
+ type: "text_start",
782
+ contentIndex: blockIndex(),
783
+ partial: output
784
+ });
785
+ }
786
+ currentBlock.text += choice.delta.content;
787
+ stream.push({
788
+ type: "text_delta",
789
+ contentIndex: blockIndex(),
790
+ delta: choice.delta.content,
791
+ partial: output
792
+ });
793
+ continue;
794
+ }
795
+ const reasoningFields = [
796
+ "reasoning_content",
797
+ "reasoning",
798
+ "reasoning_text"
799
+ ];
800
+ const reasoningField = reasoningFields.find((field) => {
801
+ const value = choice.delta[field];
802
+ return typeof value === "string" && value.length > 0;
803
+ });
804
+ if (reasoningField) {
805
+ if (!currentBlock || currentBlock.type !== "thinking") {
806
+ finishCurrentBlock();
807
+ currentBlock = {
808
+ type: "thinking",
809
+ thinking: "",
810
+ thinkingSignature: reasoningField
811
+ };
812
+ output.content.push(currentBlock);
813
+ stream.push({
814
+ type: "thinking_start",
815
+ contentIndex: blockIndex(),
816
+ partial: output
817
+ });
818
+ }
819
+ currentBlock.thinking += String(choice.delta[reasoningField]);
820
+ stream.push({
821
+ type: "thinking_delta",
822
+ contentIndex: blockIndex(),
823
+ delta: String(choice.delta[reasoningField]),
824
+ partial: output
825
+ });
826
+ continue;
827
+ }
828
+ if (choice.delta.tool_calls) {
829
+ for (const toolCall of choice.delta.tool_calls) {
830
+ if (!currentBlock || currentBlock.type !== "toolCall" || toolCall.id && currentBlock.id !== toolCall.id) {
831
+ finishCurrentBlock();
832
+ currentBlock = {
833
+ type: "toolCall",
834
+ id: toolCall.id || "",
835
+ name: toolCall.function?.name || "",
836
+ arguments: {},
837
+ partialArgs: ""
838
+ };
839
+ output.content.push(currentBlock);
840
+ stream.push({
841
+ type: "toolcall_start",
842
+ contentIndex: blockIndex(),
843
+ partial: output
844
+ });
845
+ }
846
+ if (currentBlock.type !== "toolCall") {
847
+ continue;
848
+ }
849
+ if (toolCall.id) {
850
+ currentBlock.id = toolCall.id;
851
+ }
852
+ if (toolCall.function?.name) {
853
+ currentBlock.name = toolCall.function.name;
854
+ }
855
+ if (toolCall.function?.arguments) {
856
+ currentBlock.partialArgs += toolCall.function.arguments;
857
+ currentBlock.arguments = parseStreamingJson(currentBlock.partialArgs);
858
+ stream.push({
859
+ type: "toolcall_delta",
860
+ contentIndex: blockIndex(),
861
+ delta: toolCall.function.arguments,
862
+ partial: output
863
+ });
864
+ }
865
+ }
866
+ }
867
+ }
868
+ finishCurrentBlock();
869
+ }
870
+ function detectCompat(model) {
871
+ const provider = model.provider;
872
+ const { capabilities, defaults: compatDefaults } = detectOpenAICompletionsCompat(model);
873
+ const endpointClass = capabilities.endpointClass;
874
+ const isDefaultRoute = endpointClass === "default";
875
+ const isMistral = capabilities.knownProviderFamily === "mistral" || endpointClass === "mistral-public" || isDefaultRoute && provider === "mistral";
876
+ const isVolcengine = provider === "volcengine" || provider === "volcengine-coding" || provider === "volcengine-plan";
877
+ const isBytePlus = provider === "byteplus" || provider === "byteplus-coding" || provider === "byteplus-plan";
878
+ const reasoningEffortMap = isMistral ? {
879
+ minimal: "none",
880
+ low: "none",
881
+ medium: "high",
882
+ high: "high",
883
+ xhigh: "high"
884
+ } : isVolcengine ? {
885
+ off: "minimal",
886
+ minimal: "minimal",
887
+ low: "low",
888
+ medium: "medium",
889
+ high: "high",
890
+ xhigh: "high"
891
+ } : {};
892
+ return {
893
+ supportsStore: compatDefaults.supportsStore,
894
+ supportsDeveloperRole: compatDefaults.supportsDeveloperRole,
895
+ supportsReasoningEffort: compatDefaults.supportsReasoningEffort,
896
+ reasoningEffortMap,
897
+ supportsUsageInStreaming: compatDefaults.supportsUsageInStreaming,
898
+ maxTokensField: compatDefaults.maxTokensField,
899
+ requiresToolResultName: false,
900
+ requiresAssistantAfterToolResult: false,
901
+ requiresThinkingAsText: false,
902
+ thinkingFormat: isBytePlus ? "modelark-thinking-type" : compatDefaults.thinkingFormat,
903
+ openRouterRouting: {},
904
+ vercelGatewayRouting: {},
905
+ supportsStrictMode: compatDefaults.supportsStrictMode
906
+ };
907
+ }
908
+ function getCompat(model) {
909
+ const detected = detectCompat(model);
910
+ const compat = model.compat ?? {};
911
+ const supportsStore = typeof compat.supportsStore === "boolean" ? compat.supportsStore : detected.supportsStore;
912
+ const supportsReasoningEffort = typeof compat.supportsReasoningEffort === "boolean" ? compat.supportsReasoningEffort : detected.supportsReasoningEffort;
913
+ return {
914
+ supportsStore,
915
+ supportsDeveloperRole: compat.supportsDeveloperRole ?? detected.supportsDeveloperRole,
916
+ supportsReasoningEffort,
917
+ reasoningEffortMap: compat.reasoningEffortMap ?? detected.reasoningEffortMap,
918
+ supportsUsageInStreaming: compat.supportsUsageInStreaming ?? detected.supportsUsageInStreaming,
919
+ maxTokensField: compat.maxTokensField ?? detected.maxTokensField,
920
+ requiresToolResultName: compat.requiresToolResultName ?? detected.requiresToolResultName,
921
+ requiresAssistantAfterToolResult: compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
922
+ requiresThinkingAsText: compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
923
+ thinkingFormat: compat.thinkingFormat ?? detected.thinkingFormat,
924
+ openRouterRouting: compat.openRouterRouting ?? {},
925
+ vercelGatewayRouting: compat.vercelGatewayRouting ?? detected.vercelGatewayRouting,
926
+ supportsStrictMode: compat.supportsStrictMode ?? detected.supportsStrictMode
927
+ };
928
+ }
929
+ function mapReasoningEffort(effort, reasoningEffortMap) {
930
+ return reasoningEffortMap[effort] ?? effort;
931
+ }
932
+ function convertTools(tools, compat, model) {
933
+ const strict = resolveOpenAIStrictToolFlagForInventory(tools, resolveOpenAIStrictToolSetting(model, {
934
+ transport: "stream",
935
+ supportsStrictMode: compat?.supportsStrictMode
936
+ }));
937
+ return tools.map((tool) => ({
938
+ type: "function",
939
+ function: {
940
+ name: tool.name,
941
+ description: tool.description,
942
+ parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true),
943
+ ...strict === undefined ? {} : { strict }
944
+ }
945
+ }));
946
+ }
947
+ function buildOpenAICompletionsParams(model, context, options) {
948
+ const compat = getCompat(model);
949
+ const completionsContext = context.systemPrompt ? {
950
+ ...context,
951
+ systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
952
+ } : context;
953
+ const params = {
954
+ model: model.id,
955
+ messages: convertMessages(model, completionsContext, compat),
956
+ stream: true
957
+ };
958
+ if (compat.supportsUsageInStreaming) {
959
+ params.stream_options = { include_usage: true };
960
+ }
961
+ if (compat.supportsStore) {
962
+ params.store = false;
963
+ }
964
+ if (options?.maxTokens) {
965
+ if (compat.maxTokensField === "max_tokens") {
966
+ params.max_tokens = options.maxTokens;
967
+ } else {
968
+ params.max_completion_tokens = options.maxTokens;
969
+ }
970
+ }
971
+ if (options?.temperature !== undefined) {
972
+ params.temperature = options.temperature;
973
+ }
974
+ if (context.tools) {
975
+ params.tools = convertTools(context.tools, compat, model);
976
+ } else if (hasToolHistory(context.messages)) {
977
+ params.tools = [];
978
+ }
979
+ if (options?.toolChoice) {
980
+ params.tool_choice = options.toolChoice;
981
+ }
982
+ const reasoningEffort = options?.reasoningEffort ?? options?.reasoning ?? (model.reasoning ? "high" : undefined);
983
+ if (compat.thinkingFormat === "modelark-thinking-type" && model.reasoning && reasoningEffort) {
984
+ params.thinking = { type: reasoningEffort === "off" ? "disabled" : "enabled" };
985
+ } else if (compat.thinkingFormat === "openrouter" && model.reasoning && reasoningEffort) {
986
+ params.reasoning = { effort: mapReasoningEffort(reasoningEffort, compat.reasoningEffortMap) };
987
+ } else if (reasoningEffort && model.reasoning && compat.supportsReasoningEffort) {
988
+ params.reasoning_effort = mapReasoningEffort(reasoningEffort, compat.reasoningEffortMap);
989
+ }
990
+ return params;
991
+ }
992
+ function parseTransportChunkUsage(rawUsage, model) {
993
+ const cachedTokens = rawUsage.prompt_tokens_details?.cached_tokens || 0;
994
+ const promptTokens = rawUsage.prompt_tokens || 0;
995
+ const input = Math.max(0, promptTokens - cachedTokens);
996
+ const outputTokens = rawUsage.completion_tokens || 0;
997
+ const usage = {
998
+ input,
999
+ output: outputTokens,
1000
+ cacheRead: cachedTokens,
1001
+ cacheWrite: 0,
1002
+ totalTokens: input + outputTokens + cachedTokens,
1003
+ cost: {
1004
+ input: 0,
1005
+ output: 0,
1006
+ cacheRead: 0,
1007
+ cacheWrite: 0,
1008
+ total: 0
1009
+ }
1010
+ };
1011
+ calculateCost(model, usage);
1012
+ return usage;
1013
+ }
1014
+ function mapStopReason(reason) {
1015
+ if (reason === null) {
1016
+ return { stopReason: "stop" };
1017
+ }
1018
+ switch (reason) {
1019
+ case "stop":
1020
+ case "end": return { stopReason: "stop" };
1021
+ case "length": return { stopReason: "length" };
1022
+ case "function_call":
1023
+ case "tool_calls": return { stopReason: "toolUse" };
1024
+ case "content_filter": return {
1025
+ stopReason: "error",
1026
+ errorMessage: "Provider finish_reason: content_filter"
1027
+ };
1028
+ case "network_error": return {
1029
+ stopReason: "error",
1030
+ errorMessage: "Provider finish_reason: network_error"
1031
+ };
1032
+ default: return {
1033
+ stopReason: "error",
1034
+ errorMessage: `Provider finish_reason: ${reason}`
1035
+ };
1036
+ }
1037
+ }
1038
+
1039
+ //#endregion
1040
+ export { createOpenAICompletionsTransportStreamFn, createOpenAIResponsesTransportStreamFn };