@f5-sales-demo/pi-ai 19.51.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +1997 -0
  2. package/README.md +1160 -0
  3. package/package.json +135 -0
  4. package/src/api-registry.ts +95 -0
  5. package/src/auth-storage.ts +2694 -0
  6. package/src/cli.ts +493 -0
  7. package/src/index.ts +42 -0
  8. package/src/model-cache.ts +97 -0
  9. package/src/model-manager.ts +349 -0
  10. package/src/model-thinking.ts +561 -0
  11. package/src/models.json +49439 -0
  12. package/src/models.json.d.ts +9 -0
  13. package/src/models.ts +56 -0
  14. package/src/prompts/turn-aborted-guidance.md +4 -0
  15. package/src/provider-details.ts +81 -0
  16. package/src/provider-models/descriptors.ts +285 -0
  17. package/src/provider-models/google.ts +90 -0
  18. package/src/provider-models/index.ts +4 -0
  19. package/src/provider-models/openai-compat.ts +2074 -0
  20. package/src/provider-models/special.ts +106 -0
  21. package/src/providers/amazon-bedrock.ts +706 -0
  22. package/src/providers/anthropic.ts +1682 -0
  23. package/src/providers/azure-openai-responses.ts +391 -0
  24. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  25. package/src/providers/cursor/proto/agent.proto +3526 -0
  26. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  27. package/src/providers/cursor/proto/buf.yaml +17 -0
  28. package/src/providers/cursor.ts +2218 -0
  29. package/src/providers/github-copilot-headers.ts +140 -0
  30. package/src/providers/gitlab-duo.ts +381 -0
  31. package/src/providers/google-gemini-cli.ts +1133 -0
  32. package/src/providers/google-shared.ts +354 -0
  33. package/src/providers/google-vertex.ts +436 -0
  34. package/src/providers/google.ts +381 -0
  35. package/src/providers/kimi.ts +151 -0
  36. package/src/providers/openai-codex/constants.ts +43 -0
  37. package/src/providers/openai-codex/request-transformer.ts +158 -0
  38. package/src/providers/openai-codex/response-handler.ts +81 -0
  39. package/src/providers/openai-codex-responses.ts +2345 -0
  40. package/src/providers/openai-completions-compat.ts +159 -0
  41. package/src/providers/openai-completions.ts +1290 -0
  42. package/src/providers/openai-responses-shared.ts +452 -0
  43. package/src/providers/openai-responses.ts +519 -0
  44. package/src/providers/register-builtins.ts +329 -0
  45. package/src/providers/synthetic.ts +154 -0
  46. package/src/providers/transform-messages.ts +234 -0
  47. package/src/rate-limit-utils.ts +84 -0
  48. package/src/stream.ts +728 -0
  49. package/src/types.ts +546 -0
  50. package/src/usage/claude.ts +337 -0
  51. package/src/usage/gemini.ts +248 -0
  52. package/src/usage/github-copilot.ts +421 -0
  53. package/src/usage/google-antigravity.ts +200 -0
  54. package/src/usage/kimi.ts +286 -0
  55. package/src/usage/minimax-code.ts +31 -0
  56. package/src/usage/openai-codex.ts +387 -0
  57. package/src/usage/zai.ts +247 -0
  58. package/src/usage.ts +130 -0
  59. package/src/utils/abort.ts +36 -0
  60. package/src/utils/anthropic-auth.ts +293 -0
  61. package/src/utils/discovery/antigravity.ts +261 -0
  62. package/src/utils/discovery/codex.ts +371 -0
  63. package/src/utils/discovery/cursor.ts +306 -0
  64. package/src/utils/discovery/gemini.ts +248 -0
  65. package/src/utils/discovery/index.ts +5 -0
  66. package/src/utils/discovery/openai-compatible.ts +224 -0
  67. package/src/utils/event-stream.ts +209 -0
  68. package/src/utils/http-inspector.ts +165 -0
  69. package/src/utils/idle-iterator.ts +176 -0
  70. package/src/utils/json-parse.ts +28 -0
  71. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  72. package/src/utils/oauth/anthropic.ts +134 -0
  73. package/src/utils/oauth/api-key-validation.ts +92 -0
  74. package/src/utils/oauth/callback-server.ts +276 -0
  75. package/src/utils/oauth/cerebras.ts +59 -0
  76. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  77. package/src/utils/oauth/cursor.ts +157 -0
  78. package/src/utils/oauth/github-copilot.ts +358 -0
  79. package/src/utils/oauth/gitlab-duo.ts +123 -0
  80. package/src/utils/oauth/google-antigravity.ts +275 -0
  81. package/src/utils/oauth/google-gemini-cli.ts +334 -0
  82. package/src/utils/oauth/huggingface.ts +62 -0
  83. package/src/utils/oauth/index.ts +512 -0
  84. package/src/utils/oauth/kagi.ts +47 -0
  85. package/src/utils/oauth/kilo.ts +87 -0
  86. package/src/utils/oauth/kimi.ts +251 -0
  87. package/src/utils/oauth/litellm.ts +81 -0
  88. package/src/utils/oauth/lm-studio.ts +40 -0
  89. package/src/utils/oauth/minimax-code.ts +78 -0
  90. package/src/utils/oauth/moonshot.ts +59 -0
  91. package/src/utils/oauth/nanogpt.ts +51 -0
  92. package/src/utils/oauth/nvidia.ts +70 -0
  93. package/src/utils/oauth/oauth.html +199 -0
  94. package/src/utils/oauth/ollama.ts +47 -0
  95. package/src/utils/oauth/openai-codex.ts +190 -0
  96. package/src/utils/oauth/opencode.ts +49 -0
  97. package/src/utils/oauth/parallel.ts +46 -0
  98. package/src/utils/oauth/perplexity.ts +200 -0
  99. package/src/utils/oauth/pkce.ts +18 -0
  100. package/src/utils/oauth/qianfan.ts +58 -0
  101. package/src/utils/oauth/qwen-portal.ts +60 -0
  102. package/src/utils/oauth/synthetic.ts +60 -0
  103. package/src/utils/oauth/tavily.ts +46 -0
  104. package/src/utils/oauth/together.ts +59 -0
  105. package/src/utils/oauth/types.ts +89 -0
  106. package/src/utils/oauth/venice.ts +59 -0
  107. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  108. package/src/utils/oauth/vllm.ts +40 -0
  109. package/src/utils/oauth/xiaomi.ts +88 -0
  110. package/src/utils/oauth/zai.ts +60 -0
  111. package/src/utils/oauth/zenmux.ts +51 -0
  112. package/src/utils/overflow.ts +134 -0
  113. package/src/utils/retry-after.ts +110 -0
  114. package/src/utils/retry.ts +93 -0
  115. package/src/utils/schema/CONSTRAINTS.md +160 -0
  116. package/src/utils/schema/adapt.ts +20 -0
  117. package/src/utils/schema/compatibility.ts +397 -0
  118. package/src/utils/schema/dereference.ts +93 -0
  119. package/src/utils/schema/equality.ts +93 -0
  120. package/src/utils/schema/fields.ts +147 -0
  121. package/src/utils/schema/index.ts +9 -0
  122. package/src/utils/schema/normalize-cca.ts +479 -0
  123. package/src/utils/schema/sanitize-google.ts +212 -0
  124. package/src/utils/schema/strict-mode.ts +385 -0
  125. package/src/utils/schema/types.ts +5 -0
  126. package/src/utils/tool-choice.ts +81 -0
  127. package/src/utils/validation.ts +664 -0
  128. package/src/utils.ts +147 -0
@@ -0,0 +1,519 @@
1
+ import { $env, structuredCloneJSON } from "@f5xc-salesdemos/pi-utils";
2
+ import OpenAI from "openai";
3
+ import type {
4
+ Tool as OpenAITool,
5
+ ResponseCreateParamsStreaming,
6
+ ResponseInput,
7
+ } from "openai/resources/responses/responses";
8
+ import { getEnvApiKey } from "../stream";
9
+ import {
10
+ type Api,
11
+ type AssistantMessage,
12
+ type CacheRetention,
13
+ type Context,
14
+ isSpecialServiceTier,
15
+ type MessageAttribution,
16
+ type Model,
17
+ type ProviderSessionState,
18
+ type ServiceTier,
19
+ type StreamFunction,
20
+ type StreamOptions,
21
+ type Tool,
22
+ type ToolChoice,
23
+ } from "../types";
24
+ import {
25
+ createOpenAIResponsesHistoryPayload,
26
+ getOpenAIResponsesHistoryItems,
27
+ getOpenAIResponsesHistoryPayload,
28
+ resolveCacheRetention,
29
+ sanitizeOpenAIResponsesHistoryItemsForReplay,
30
+ } from "../utils";
31
+ import { createAbortSourceTracker } from "../utils/abort";
32
+ import { AssistantMessageEventStream } from "../utils/event-stream";
33
+ import { finalizeErrorMessage, type RawHttpRequestDump, rewriteCopilotAuthError } from "../utils/http-inspector";
34
+ import {
35
+ createFirstEventWatchdog,
36
+ getOpenAIStreamIdleTimeoutMs,
37
+ getStreamFirstEventTimeoutMs,
38
+ iterateWithIdleTimeout,
39
+ markFirstStreamEvent,
40
+ } from "../utils/idle-iterator";
41
+ import { parseGitHubCopilotApiKey } from "../utils/oauth/github-copilot";
42
+ import { adaptSchemaForStrict, NO_STRICT } from "../utils/schema";
43
+ import { mapToOpenAIResponsesToolChoice } from "../utils/tool-choice";
44
+ import {
45
+ buildCopilotDynamicHeaders,
46
+ hasCopilotVisionInput,
47
+ resolveGitHubCopilotBaseUrl,
48
+ } from "./github-copilot-headers";
49
+ import {
50
+ appendResponsesToolResultMessages,
51
+ collectKnownCallIds,
52
+ convertResponsesAssistantMessage,
53
+ convertResponsesInputContent,
54
+ normalizeResponsesToolCallIdForTransform,
55
+ processResponsesStream,
56
+ } from "./openai-responses-shared";
57
+ import { transformMessages } from "./transform-messages";
58
+
59
+ /**
60
+ * Get prompt cache retention based on cacheRetention and base URL.
61
+ * Only applies to direct OpenAI API calls (api.openai.com).
62
+ */
63
+ function getPromptCacheRetention(baseUrl: string, cacheRetention: CacheRetention): "24h" | undefined {
64
+ if (cacheRetention !== "long") {
65
+ return undefined;
66
+ }
67
+ if (baseUrl.includes("api.openai.com")) {
68
+ return "24h";
69
+ }
70
+ return undefined;
71
+ }
72
+
73
+ // OpenAI Responses-specific options
74
+ export interface OpenAIResponsesOptions extends StreamOptions {
75
+ reasoning?: "minimal" | "low" | "medium" | "high" | "xhigh";
76
+ reasoningSummary?: "auto" | "detailed" | "concise" | null;
77
+ serviceTier?: ServiceTier;
78
+ toolChoice?: ToolChoice;
79
+ /**
80
+ * Enforce strict tool call/result pairing when building Responses API inputs.
81
+ * Azure OpenAI and GitHub Copilot Responses paths require tool results to match prior tool calls.
82
+ */
83
+ strictResponsesPairing?: boolean;
84
+ }
85
+
86
+ const OPENAI_RESPONSES_PROVIDER_SESSION_STATE_PREFIX = "openai-responses:";
87
+ const OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE =
88
+ "OpenAI responses stream timed out while waiting for the first event";
89
+
90
+ interface OpenAIResponsesProviderSessionState extends ProviderSessionState {
91
+ nativeHistoryReplayWarmed: boolean;
92
+ }
93
+
94
+ function createOpenAIResponsesProviderSessionState(): OpenAIResponsesProviderSessionState {
95
+ const state: OpenAIResponsesProviderSessionState = {
96
+ nativeHistoryReplayWarmed: false,
97
+ close: () => {
98
+ state.nativeHistoryReplayWarmed = false;
99
+ },
100
+ };
101
+ return state;
102
+ }
103
+
104
+ function getOpenAIResponsesProviderSessionStateKey(model: Model<"openai-responses">): string {
105
+ return `${OPENAI_RESPONSES_PROVIDER_SESSION_STATE_PREFIX}${model.provider}`;
106
+ }
107
+
108
+ function getOpenAIResponsesProviderSessionState(
109
+ model: Model<"openai-responses">,
110
+ providerSessionState: Map<string, ProviderSessionState> | undefined,
111
+ ): OpenAIResponsesProviderSessionState | undefined {
112
+ if (!providerSessionState) return undefined;
113
+ const key = getOpenAIResponsesProviderSessionStateKey(model);
114
+ const existing = providerSessionState.get(key) as OpenAIResponsesProviderSessionState | undefined;
115
+ if (existing) return existing;
116
+ const created = createOpenAIResponsesProviderSessionState();
117
+ providerSessionState.set(key, created);
118
+ return created;
119
+ }
120
+
121
+ function canReplayOpenAIResponsesNativeHistory(
122
+ providerSessionState: OpenAIResponsesProviderSessionState | undefined,
123
+ ): boolean {
124
+ return providerSessionState?.nativeHistoryReplayWarmed ?? true;
125
+ }
126
+
127
+ type OpenAIResponsesSamplingParams = ResponseCreateParamsStreaming & {
128
+ top_p?: number;
129
+ top_k?: number;
130
+ min_p?: number;
131
+ presence_penalty?: number;
132
+ repetition_penalty?: number;
133
+ };
134
+
135
+ /**
136
+ * Generate function for OpenAI Responses API
137
+ */
138
+ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
139
+ model: Model<"openai-responses">,
140
+ context: Context,
141
+ options?: OpenAIResponsesOptions,
142
+ ): AssistantMessageEventStream => {
143
+ const stream = new AssistantMessageEventStream();
144
+
145
+ // Start async processing
146
+ (async () => {
147
+ const startTime = Date.now();
148
+ let firstTokenTime: number | undefined;
149
+
150
+ const output: AssistantMessage = {
151
+ role: "assistant",
152
+ content: [],
153
+ api: "openai-responses" as Api,
154
+ provider: model.provider,
155
+ model: model.id,
156
+ usage: {
157
+ input: 0,
158
+ output: 0,
159
+ cacheRead: 0,
160
+ cacheWrite: 0,
161
+ totalTokens: 0,
162
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
163
+ },
164
+ stopReason: "stop",
165
+ timestamp: Date.now(),
166
+ };
167
+ let rawRequestDump: RawHttpRequestDump | undefined;
168
+ const abortTracker = createAbortSourceTracker(options?.signal);
169
+ const firstEventTimeoutAbortError = new Error(OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE);
170
+ const { requestAbortController, requestSignal } = abortTracker;
171
+
172
+ try {
173
+ // Create OpenAI client
174
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
175
+ const { client, copilotPremiumRequests, baseUrl } = createClient(
176
+ model,
177
+ context,
178
+ apiKey,
179
+ options?.headers,
180
+ options?.initiatorOverride,
181
+ );
182
+ const providerSessionState = getOpenAIResponsesProviderSessionState(model, options?.providerSessionState);
183
+ const { params } = buildParams(model, context, options, providerSessionState, baseUrl);
184
+ const idleTimeoutMs = getOpenAIStreamIdleTimeoutMs();
185
+ options?.onPayload?.(params);
186
+ rawRequestDump = {
187
+ provider: model.provider,
188
+ api: output.api,
189
+ model: model.id,
190
+ method: "POST",
191
+ url: `${baseUrl ?? "https://api.openai.com/v1"}/responses`,
192
+ body: params,
193
+ };
194
+ const openaiStream = await client.responses.create(params, { signal: requestSignal });
195
+ const firstEventWatchdog = createFirstEventWatchdog(
196
+ options?.streamFirstEventTimeoutMs ?? getStreamFirstEventTimeoutMs(idleTimeoutMs),
197
+ () => abortTracker.abortLocally(firstEventTimeoutAbortError),
198
+ );
199
+ if (copilotPremiumRequests !== undefined) output.usage.premiumRequests = copilotPremiumRequests;
200
+ stream.push({ type: "start", partial: output });
201
+
202
+ const nativeOutputItems: Array<Record<string, unknown>> = [];
203
+ await processResponsesStream(
204
+ iterateWithIdleTimeout(markFirstStreamEvent(openaiStream, firstEventWatchdog), {
205
+ idleTimeoutMs,
206
+ errorMessage: "OpenAI responses stream stalled while waiting for the next event",
207
+ onIdle: () => requestAbortController.abort(),
208
+ }),
209
+ output,
210
+ stream,
211
+ model,
212
+ {
213
+ onFirstToken: () => {
214
+ if (!firstTokenTime) firstTokenTime = Date.now();
215
+ },
216
+ onOutputItemDone: item => {
217
+ nativeOutputItems.push(structuredCloneJSON<unknown>(item) as unknown as Record<string, unknown>);
218
+ },
219
+ },
220
+ );
221
+ if (copilotPremiumRequests !== undefined) output.usage.premiumRequests = copilotPremiumRequests;
222
+
223
+ const firstEventTimeoutError = abortTracker.getLocalAbortReason();
224
+ if (firstEventTimeoutError) {
225
+ throw firstEventTimeoutError;
226
+ }
227
+ if (abortTracker.wasCallerAbort()) {
228
+ throw new Error("Request was aborted");
229
+ }
230
+
231
+ if (output.stopReason === "aborted" || output.stopReason === "error") {
232
+ throw new Error("An unknown error occurred");
233
+ }
234
+
235
+ output.providerPayload = createOpenAIResponsesHistoryPayload(model.provider, nativeOutputItems);
236
+ if (providerSessionState) providerSessionState.nativeHistoryReplayWarmed = true;
237
+
238
+ output.duration = Date.now() - startTime;
239
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
240
+ stream.push({ type: "done", reason: output.stopReason, message: output });
241
+ stream.end();
242
+ } catch (error) {
243
+ for (const block of output.content) delete (block as { index?: number }).index;
244
+ const firstEventTimeoutError = abortTracker.getLocalAbortReason();
245
+ output.stopReason = abortTracker.wasCallerAbort() ? "aborted" : "error";
246
+ output.errorMessage = firstEventTimeoutError?.message ?? (await finalizeErrorMessage(error, rawRequestDump));
247
+ output.errorMessage = rewriteCopilotAuthError(output.errorMessage, error, model.provider);
248
+ output.duration = Date.now() - startTime;
249
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
250
+ stream.push({ type: "error", reason: output.stopReason, error: output });
251
+ stream.end();
252
+ }
253
+ })();
254
+
255
+ return stream;
256
+ };
257
+
258
+ function createClient(
259
+ model: Model<"openai-responses">,
260
+ context: Context,
261
+ apiKey?: string,
262
+ extraHeaders?: Record<string, string>,
263
+ initiatorOverride?: MessageAttribution,
264
+ ): {
265
+ client: OpenAI;
266
+ copilotPremiumRequests: number | undefined;
267
+ baseUrl: string | undefined;
268
+ } {
269
+ if (!apiKey) {
270
+ if (!$env.OPENAI_API_KEY) {
271
+ throw new Error(
272
+ "OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.",
273
+ );
274
+ }
275
+ apiKey = $env.OPENAI_API_KEY;
276
+ }
277
+ const rawApiKey = apiKey;
278
+
279
+ const headers = { ...(model.headers ?? {}), ...(extraHeaders ?? {}) };
280
+ let copilotPremiumRequests: number | undefined;
281
+
282
+ let baseUrl = model.baseUrl;
283
+ if (model.provider === "github-copilot") {
284
+ apiKey = parseGitHubCopilotApiKey(rawApiKey).accessToken;
285
+ const hasImages = hasCopilotVisionInput(context.messages);
286
+ const copilot = buildCopilotDynamicHeaders({
287
+ messages: context.messages,
288
+ hasImages,
289
+ premiumMultiplier: model.premiumMultiplier,
290
+ headers,
291
+ initiatorOverride,
292
+ });
293
+ Object.assign(headers, copilot.headers);
294
+ copilotPremiumRequests = copilot.premiumRequests;
295
+ baseUrl = resolveGitHubCopilotBaseUrl(model.baseUrl, rawApiKey) ?? model.baseUrl;
296
+ }
297
+ return {
298
+ client: new OpenAI({
299
+ apiKey,
300
+ baseURL: baseUrl,
301
+ dangerouslyAllowBrowser: true,
302
+ maxRetries: 5,
303
+ defaultHeaders: headers,
304
+ }),
305
+ copilotPremiumRequests,
306
+ baseUrl,
307
+ };
308
+ }
309
+
310
+ function buildParams(
311
+ model: Model<"openai-responses">,
312
+ context: Context,
313
+ options: OpenAIResponsesOptions | undefined,
314
+ providerSessionState: OpenAIResponsesProviderSessionState | undefined,
315
+ resolvedBaseUrl?: string,
316
+ ): { conversationMessages: ResponseInput; params: OpenAIResponsesSamplingParams } {
317
+ const strictResponsesPairing =
318
+ options?.strictResponsesPairing ??
319
+ (isAzureOpenAIBaseUrl(model.baseUrl ?? "") || model.provider === "github-copilot");
320
+ const conversationMessages = convertConversationMessages(
321
+ model,
322
+ context,
323
+ strictResponsesPairing,
324
+ providerSessionState,
325
+ );
326
+ const messages: ResponseInput = [...conversationMessages];
327
+
328
+ if (context.systemPrompt) {
329
+ const role = model.reasoning && supportsDeveloperRole(resolvedBaseUrl ?? model) ? "developer" : "system";
330
+ messages.unshift({
331
+ role,
332
+ content: context.systemPrompt.toWellFormed(),
333
+ });
334
+ }
335
+
336
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
337
+ const promptCacheKey = cacheRetention === "none" ? undefined : options?.sessionId;
338
+ const params: OpenAIResponsesSamplingParams = {
339
+ model: model.id,
340
+ input: messages,
341
+ stream: true,
342
+ prompt_cache_key: promptCacheKey,
343
+ prompt_cache_retention: promptCacheKey ? getPromptCacheRetention(model.baseUrl, cacheRetention) : undefined,
344
+ store: false,
345
+ };
346
+
347
+ if (options?.maxTokens) {
348
+ params.max_output_tokens = options?.maxTokens;
349
+ }
350
+
351
+ if (options?.temperature !== undefined) {
352
+ params.temperature = options?.temperature;
353
+ }
354
+ if (options?.topP !== undefined) {
355
+ params.top_p = options.topP;
356
+ }
357
+ if (options?.topK !== undefined) {
358
+ params.top_k = options.topK;
359
+ }
360
+ if (options?.minP !== undefined) {
361
+ params.min_p = options.minP;
362
+ }
363
+ if (options?.presencePenalty !== undefined) {
364
+ params.presence_penalty = options.presencePenalty;
365
+ }
366
+ if (options?.repetitionPenalty !== undefined) {
367
+ params.repetition_penalty = options.repetitionPenalty;
368
+ }
369
+ if (isSpecialServiceTier(options?.serviceTier)) {
370
+ params.service_tier = options.serviceTier;
371
+ }
372
+
373
+ if (context.tools) {
374
+ params.tools = convertTools(context.tools, supportsStrictMode(model));
375
+ if (options?.toolChoice) {
376
+ params.tool_choice = mapToOpenAIResponsesToolChoice(options.toolChoice);
377
+ }
378
+ }
379
+
380
+ if (model.reasoning) {
381
+ // Always request encrypted reasoning content so reasoning items can be
382
+ // replayed in multi-turn conversations when store is false (items aren't
383
+ // persisted server-side, so we must include the full content).
384
+ // See: https://github.com/f5xc-salesdemos/xcsh/issues/41
385
+ params.include = ["reasoning.encrypted_content"];
386
+
387
+ if (options?.reasoning || options?.reasoningSummary) {
388
+ params.reasoning = {
389
+ effort: options?.reasoning || "medium",
390
+ summary: options?.reasoningSummary || "auto",
391
+ };
392
+ } else if (model.name.startsWith("gpt-5")) {
393
+ // Jesus Christ, see https://community.openai.com/t/need-reasoning-false-option-for-gpt-5/1351588/7
394
+ messages.push({
395
+ role: "developer",
396
+ content: [
397
+ {
398
+ type: "input_text",
399
+ text: "# Juice: 0 !important",
400
+ },
401
+ ],
402
+ });
403
+ }
404
+ }
405
+
406
+ return { conversationMessages, params };
407
+ }
408
+
409
+ function isAzureOpenAIBaseUrl(baseUrl: string): boolean {
410
+ return baseUrl.includes(".openai.azure.com") || baseUrl.includes("azure.com/openai");
411
+ }
412
+
413
+ function supportsStrictMode(model: Model<"openai-responses">): boolean {
414
+ if (model.provider === "openai" || model.provider === "azure" || model.provider === "github-copilot") return true;
415
+
416
+ const baseUrl = model.baseUrl.toLowerCase();
417
+ return (
418
+ baseUrl.includes("api.openai.com") ||
419
+ baseUrl.includes(".openai.azure.com") ||
420
+ baseUrl.includes("models.inference.ai.azure.com")
421
+ );
422
+ }
423
+
424
+ export function supportsDeveloperRole(modelOrBaseUrl: Pick<Model, "provider" | "baseUrl"> | string): boolean {
425
+ const baseUrl =
426
+ typeof modelOrBaseUrl === "string" ? modelOrBaseUrl.toLowerCase() : (modelOrBaseUrl.baseUrl ?? "").toLowerCase();
427
+ return (
428
+ baseUrl.includes("api.openai.com") ||
429
+ baseUrl.includes(".openai.azure.com") ||
430
+ baseUrl.includes("azure.com/openai") ||
431
+ baseUrl.includes("models.inference.ai.azure.com") ||
432
+ baseUrl.includes("githubcopilot.com") ||
433
+ baseUrl.includes("copilot-api.")
434
+ );
435
+ }
436
+
437
+ function convertConversationMessages(
438
+ model: Model<"openai-responses">,
439
+ context: Context,
440
+ strictResponsesPairing: boolean,
441
+ providerSessionState: OpenAIResponsesProviderSessionState | undefined,
442
+ ): ResponseInput {
443
+ const messages: ResponseInput = [];
444
+ let knownCallIds = new Set<string>();
445
+ const shouldReplayNativeHistory = canReplayOpenAIResponsesNativeHistory(providerSessionState);
446
+ const transformedMessages = transformMessages(context.messages, model, normalizeResponsesToolCallIdForTransform);
447
+
448
+ let msgIndex = 0;
449
+ for (const msg of transformedMessages) {
450
+ if (msg.role === "user" || msg.role === "developer") {
451
+ const providerPayload = (msg as { providerPayload?: AssistantMessage["providerPayload"] }).providerPayload;
452
+ const historyItems = getOpenAIResponsesHistoryItems(providerPayload, model.provider);
453
+ const shouldReplayPayloadItems =
454
+ shouldReplayNativeHistory ||
455
+ (historyItems?.some(item => {
456
+ if (!item || typeof item !== "object") return false;
457
+ const candidate = item as { type?: unknown };
458
+ return candidate.type === "compaction" || candidate.type === "compaction_summary";
459
+ }) ??
460
+ false);
461
+ if (historyItems && shouldReplayPayloadItems) {
462
+ messages.push(...sanitizeOpenAIResponsesHistoryItemsForReplay(historyItems));
463
+ knownCallIds = collectKnownCallIds(messages);
464
+ msgIndex++;
465
+ continue;
466
+ }
467
+ const content = convertResponsesInputContent(msg.content, model.input.includes("image"));
468
+ if (!content) continue;
469
+ messages.push({ role: "user", content });
470
+ } else if (msg.role === "assistant") {
471
+ const assistantMsg = msg as AssistantMessage;
472
+ const providerPayload = shouldReplayNativeHistory
473
+ ? getOpenAIResponsesHistoryPayload(assistantMsg.providerPayload, model.provider, assistantMsg.provider)
474
+ : undefined;
475
+ const historyItems = providerPayload?.items;
476
+ if (historyItems) {
477
+ const sanitizedHistoryItems = sanitizeOpenAIResponsesHistoryItemsForReplay(historyItems);
478
+ if (providerPayload?.dt) {
479
+ messages.push(...sanitizedHistoryItems);
480
+ } else {
481
+ messages.splice(0, messages.length, ...sanitizedHistoryItems);
482
+ }
483
+ knownCallIds = collectKnownCallIds(messages);
484
+ msgIndex++;
485
+ continue;
486
+ }
487
+
488
+ const outputItems = convertResponsesAssistantMessage(
489
+ assistantMsg,
490
+ model,
491
+ msgIndex,
492
+ knownCallIds,
493
+ shouldReplayNativeHistory,
494
+ );
495
+ if (outputItems.length === 0) continue;
496
+ messages.push(...outputItems);
497
+ } else if (msg.role === "toolResult") {
498
+ appendResponsesToolResultMessages(messages, msg, model, strictResponsesPairing, knownCallIds);
499
+ }
500
+ msgIndex++;
501
+ }
502
+
503
+ return messages;
504
+ }
505
+
506
+ function convertTools(tools: Tool[], strictMode: boolean): OpenAITool[] {
507
+ return tools.map(tool => {
508
+ const strict = !NO_STRICT && strictMode && tool.strict !== false;
509
+ const baseParameters = tool.parameters as unknown as Record<string, unknown>;
510
+ const { schema: parameters, strict: effectiveStrict } = adaptSchemaForStrict(baseParameters, strict);
511
+ return {
512
+ type: "function",
513
+ name: tool.name,
514
+ description: tool.description || "",
515
+ parameters,
516
+ ...(effectiveStrict && { strict: true }),
517
+ } as OpenAITool;
518
+ });
519
+ }