@hebo-ai/gateway 0.9.2 → 0.9.3

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 (213) hide show
  1. package/dist/config.d.ts +2 -0
  2. package/dist/config.js +125 -0
  3. package/dist/endpoints/chat-completions/converters.d.ts +26 -0
  4. package/dist/endpoints/chat-completions/converters.js +525 -0
  5. package/dist/endpoints/chat-completions/handler.d.ts +2 -0
  6. package/dist/endpoints/chat-completions/handler.js +152 -0
  7. package/dist/endpoints/chat-completions/index.d.ts +4 -0
  8. package/dist/endpoints/chat-completions/index.js +4 -0
  9. package/dist/endpoints/chat-completions/otel.d.ts +5 -0
  10. package/dist/endpoints/chat-completions/otel.js +178 -0
  11. package/dist/endpoints/chat-completions/schema.d.ts +1170 -0
  12. package/dist/endpoints/chat-completions/schema.js +252 -0
  13. package/dist/endpoints/conversations/converters.d.ts +8 -0
  14. package/dist/endpoints/conversations/converters.js +29 -0
  15. package/dist/endpoints/conversations/handler.d.ts +2 -0
  16. package/dist/endpoints/conversations/handler.js +259 -0
  17. package/dist/endpoints/conversations/index.d.ts +3 -0
  18. package/dist/endpoints/conversations/index.js +3 -0
  19. package/dist/endpoints/conversations/schema.d.ts +1511 -0
  20. package/dist/endpoints/conversations/schema.js +74 -0
  21. package/dist/endpoints/conversations/storage/dialects/greptime.d.ts +10 -0
  22. package/dist/endpoints/conversations/storage/dialects/greptime.js +87 -0
  23. package/dist/endpoints/conversations/storage/dialects/mysql.d.ts +12 -0
  24. package/dist/endpoints/conversations/storage/dialects/mysql.js +118 -0
  25. package/dist/endpoints/conversations/storage/dialects/postgres.d.ts +16 -0
  26. package/dist/endpoints/conversations/storage/dialects/postgres.js +185 -0
  27. package/dist/endpoints/conversations/storage/dialects/sqlite.d.ts +11 -0
  28. package/dist/endpoints/conversations/storage/dialects/sqlite.js +176 -0
  29. package/dist/endpoints/conversations/storage/dialects/types.d.ts +42 -0
  30. package/dist/endpoints/conversations/storage/dialects/types.js +0 -0
  31. package/dist/endpoints/conversations/storage/dialects/utils.d.ts +25 -0
  32. package/dist/endpoints/conversations/storage/dialects/utils.js +80 -0
  33. package/dist/endpoints/conversations/storage/memory.d.ts +25 -0
  34. package/dist/endpoints/conversations/storage/memory.js +200 -0
  35. package/dist/endpoints/conversations/storage/sql.d.ts +33 -0
  36. package/dist/endpoints/conversations/storage/sql.js +276 -0
  37. package/dist/endpoints/conversations/storage/types.d.ts +39 -0
  38. package/dist/endpoints/conversations/storage/types.js +0 -0
  39. package/dist/endpoints/embeddings/converters.d.ts +10 -0
  40. package/dist/endpoints/embeddings/converters.js +31 -0
  41. package/dist/endpoints/embeddings/handler.d.ts +2 -0
  42. package/dist/endpoints/embeddings/handler.js +99 -0
  43. package/dist/endpoints/embeddings/index.d.ts +4 -0
  44. package/dist/endpoints/embeddings/index.js +4 -0
  45. package/dist/endpoints/embeddings/otel.d.ts +5 -0
  46. package/dist/endpoints/embeddings/otel.js +29 -0
  47. package/dist/endpoints/embeddings/schema.d.ts +44 -0
  48. package/dist/endpoints/embeddings/schema.js +29 -0
  49. package/dist/endpoints/models/converters.d.ts +6 -0
  50. package/dist/endpoints/models/converters.js +42 -0
  51. package/dist/endpoints/models/handler.d.ts +2 -0
  52. package/dist/endpoints/models/handler.js +29 -0
  53. package/dist/endpoints/models/index.d.ts +3 -0
  54. package/dist/endpoints/models/index.js +3 -0
  55. package/dist/endpoints/models/schema.d.ts +42 -0
  56. package/dist/endpoints/models/schema.js +31 -0
  57. package/dist/endpoints/responses/converters.d.ts +17 -0
  58. package/dist/endpoints/responses/converters.js +1037 -0
  59. package/dist/endpoints/responses/handler.d.ts +2 -0
  60. package/dist/endpoints/responses/handler.js +141 -0
  61. package/dist/endpoints/responses/index.d.ts +4 -0
  62. package/dist/endpoints/responses/index.js +4 -0
  63. package/dist/endpoints/responses/otel.d.ts +6 -0
  64. package/dist/endpoints/responses/otel.js +226 -0
  65. package/dist/endpoints/responses/schema.d.ts +2109 -0
  66. package/dist/endpoints/responses/schema.js +314 -0
  67. package/dist/endpoints/shared/converters.d.ts +56 -0
  68. package/dist/endpoints/shared/converters.js +180 -0
  69. package/dist/endpoints/shared/schema.d.ts +70 -0
  70. package/dist/endpoints/shared/schema.js +46 -0
  71. package/dist/errors/ai-sdk.d.ts +2 -0
  72. package/dist/errors/ai-sdk.js +52 -0
  73. package/dist/errors/gateway.d.ts +5 -0
  74. package/dist/errors/gateway.js +13 -0
  75. package/dist/errors/openai.d.ts +15 -0
  76. package/dist/errors/openai.js +40 -0
  77. package/dist/errors/utils.d.ts +24 -0
  78. package/dist/errors/utils.js +46 -0
  79. package/dist/gateway.d.ts +11 -0
  80. package/dist/gateway.js +44 -0
  81. package/dist/index.d.ts +11 -0
  82. package/dist/index.js +10 -0
  83. package/dist/lifecycle.d.ts +3 -0
  84. package/dist/lifecycle.js +114 -0
  85. package/dist/logger/default.d.ts +4 -0
  86. package/dist/logger/default.js +81 -0
  87. package/dist/logger/index.d.ts +11 -0
  88. package/dist/logger/index.js +25 -0
  89. package/dist/middleware/common.d.ts +12 -0
  90. package/dist/middleware/common.js +146 -0
  91. package/dist/middleware/debug.d.ts +3 -0
  92. package/dist/middleware/debug.js +27 -0
  93. package/dist/middleware/matcher.d.ts +28 -0
  94. package/dist/middleware/matcher.js +118 -0
  95. package/dist/middleware/utils.d.ts +2 -0
  96. package/dist/middleware/utils.js +24 -0
  97. package/dist/models/amazon/index.d.ts +2 -0
  98. package/dist/models/amazon/index.js +2 -0
  99. package/dist/models/amazon/middleware.d.ts +3 -0
  100. package/dist/models/amazon/middleware.js +69 -0
  101. package/dist/models/amazon/presets.d.ts +345 -0
  102. package/dist/models/amazon/presets.js +80 -0
  103. package/dist/models/anthropic/index.d.ts +2 -0
  104. package/dist/models/anthropic/index.js +2 -0
  105. package/dist/models/anthropic/middleware.d.ts +5 -0
  106. package/dist/models/anthropic/middleware.js +128 -0
  107. package/dist/models/anthropic/presets.d.ts +711 -0
  108. package/dist/models/anthropic/presets.js +140 -0
  109. package/dist/models/catalog.d.ts +4 -0
  110. package/dist/models/catalog.js +8 -0
  111. package/dist/models/cohere/index.d.ts +2 -0
  112. package/dist/models/cohere/index.js +2 -0
  113. package/dist/models/cohere/middleware.d.ts +3 -0
  114. package/dist/models/cohere/middleware.js +62 -0
  115. package/dist/models/cohere/presets.d.ts +411 -0
  116. package/dist/models/cohere/presets.js +134 -0
  117. package/dist/models/google/index.d.ts +2 -0
  118. package/dist/models/google/index.js +2 -0
  119. package/dist/models/google/middleware.d.ts +8 -0
  120. package/dist/models/google/middleware.js +118 -0
  121. package/dist/models/google/presets.d.ts +815 -0
  122. package/dist/models/google/presets.js +184 -0
  123. package/dist/models/meta/index.d.ts +1 -0
  124. package/dist/models/meta/index.js +1 -0
  125. package/dist/models/meta/presets.d.ts +483 -0
  126. package/dist/models/meta/presets.js +105 -0
  127. package/dist/models/openai/index.d.ts +2 -0
  128. package/dist/models/openai/index.js +2 -0
  129. package/dist/models/openai/middleware.d.ts +4 -0
  130. package/dist/models/openai/middleware.js +89 -0
  131. package/dist/models/openai/presets.d.ts +1319 -0
  132. package/dist/models/openai/presets.js +277 -0
  133. package/dist/models/types.d.ts +20 -0
  134. package/dist/models/types.js +100 -0
  135. package/dist/models/voyage/index.d.ts +2 -0
  136. package/dist/models/voyage/index.js +2 -0
  137. package/dist/models/voyage/middleware.d.ts +2 -0
  138. package/dist/models/voyage/middleware.js +19 -0
  139. package/dist/models/voyage/presets.d.ts +436 -0
  140. package/dist/models/voyage/presets.js +85 -0
  141. package/dist/providers/anthropic/canonical.d.ts +3 -0
  142. package/dist/providers/anthropic/canonical.js +9 -0
  143. package/dist/providers/anthropic/index.d.ts +1 -0
  144. package/dist/providers/anthropic/index.js +1 -0
  145. package/dist/providers/bedrock/canonical.d.ts +17 -0
  146. package/dist/providers/bedrock/canonical.js +64 -0
  147. package/dist/providers/bedrock/index.d.ts +2 -0
  148. package/dist/providers/bedrock/index.js +2 -0
  149. package/dist/providers/bedrock/middleware.d.ts +5 -0
  150. package/dist/providers/bedrock/middleware.js +133 -0
  151. package/dist/providers/cohere/canonical.d.ts +3 -0
  152. package/dist/providers/cohere/canonical.js +17 -0
  153. package/dist/providers/cohere/index.d.ts +1 -0
  154. package/dist/providers/cohere/index.js +1 -0
  155. package/dist/providers/groq/canonical.d.ts +3 -0
  156. package/dist/providers/groq/canonical.js +12 -0
  157. package/dist/providers/groq/index.d.ts +2 -0
  158. package/dist/providers/groq/index.js +2 -0
  159. package/dist/providers/groq/middleware.d.ts +2 -0
  160. package/dist/providers/groq/middleware.js +30 -0
  161. package/dist/providers/openai/canonical.d.ts +3 -0
  162. package/dist/providers/openai/canonical.js +8 -0
  163. package/dist/providers/openai/index.d.ts +1 -0
  164. package/dist/providers/openai/index.js +1 -0
  165. package/dist/providers/registry.d.ts +24 -0
  166. package/dist/providers/registry.js +103 -0
  167. package/dist/providers/types.d.ts +7 -0
  168. package/dist/providers/types.js +11 -0
  169. package/dist/providers/vertex/canonical.d.ts +3 -0
  170. package/dist/providers/vertex/canonical.js +8 -0
  171. package/dist/providers/vertex/index.d.ts +2 -0
  172. package/dist/providers/vertex/index.js +2 -0
  173. package/dist/providers/vertex/middleware.d.ts +2 -0
  174. package/dist/providers/vertex/middleware.js +47 -0
  175. package/dist/providers/voyage/canonical.d.ts +3 -0
  176. package/dist/providers/voyage/canonical.js +7 -0
  177. package/dist/providers/voyage/index.d.ts +1 -0
  178. package/dist/providers/voyage/index.js +1 -0
  179. package/dist/telemetry/ai-sdk.d.ts +2 -0
  180. package/dist/telemetry/ai-sdk.js +31 -0
  181. package/dist/telemetry/baggage.d.ts +1 -0
  182. package/dist/telemetry/baggage.js +24 -0
  183. package/dist/telemetry/fetch.d.ts +2 -0
  184. package/dist/telemetry/fetch.js +49 -0
  185. package/dist/telemetry/gen-ai.d.ts +7 -0
  186. package/dist/telemetry/gen-ai.js +108 -0
  187. package/dist/telemetry/http.d.ts +3 -0
  188. package/dist/telemetry/http.js +54 -0
  189. package/dist/telemetry/index.d.ts +1 -0
  190. package/dist/telemetry/index.js +1 -0
  191. package/dist/telemetry/memory.d.ts +2 -0
  192. package/dist/telemetry/memory.js +43 -0
  193. package/dist/telemetry/span.d.ts +13 -0
  194. package/dist/telemetry/span.js +60 -0
  195. package/dist/types.d.ts +231 -0
  196. package/dist/types.js +2 -0
  197. package/dist/utils/body.d.ts +19 -0
  198. package/dist/utils/body.js +99 -0
  199. package/dist/utils/env.d.ts +2 -0
  200. package/dist/utils/env.js +7 -0
  201. package/dist/utils/headers.d.ts +4 -0
  202. package/dist/utils/headers.js +22 -0
  203. package/dist/utils/preset.d.ts +10 -0
  204. package/dist/utils/preset.js +41 -0
  205. package/dist/utils/request.d.ts +2 -0
  206. package/dist/utils/request.js +43 -0
  207. package/dist/utils/response.d.ts +6 -0
  208. package/dist/utils/response.js +55 -0
  209. package/dist/utils/stream.d.ts +9 -0
  210. package/dist/utils/stream.js +100 -0
  211. package/dist/utils/url.d.ts +4 -0
  212. package/dist/utils/url.js +21 -0
  213. package/package.json +1 -1
@@ -0,0 +1,1037 @@
1
+ import { Output, jsonSchema, stepCountIs, tool } from "ai";
2
+ import { v7 as uuidv7 } from "uuid";
3
+ import { GatewayError } from "../../errors/gateway";
4
+ import { toResponse } from "../../utils/response";
5
+ import { parseJsonOrText, parseReasoningOptions, parsePromptCachingOptions, resolveResponseServiceTier, normalizeToolName, stripEmptyKeys, parseBase64, parseImageInput, extractReasoningMetadata, } from "../shared/converters";
6
+ // --- Helpers ---
7
+ function parseUrl(url) {
8
+ try {
9
+ return new URL(url);
10
+ }
11
+ catch (error) {
12
+ throw new GatewayError("Invalid URL", 400, undefined, error);
13
+ }
14
+ }
15
+ // --- Request Flow ---
16
+ export function convertToTextCallOptions(params) {
17
+ const { input, instructions, tools, tool_choice, text, temperature, top_p, frequency_penalty, presence_penalty, max_output_tokens, max_tool_calls, reasoning_effort, reasoning, prompt_cache_key, extra_body, cache_control, ...rest } = params;
18
+ Object.assign(rest, parseReasoningOptions(reasoning_effort, reasoning));
19
+ Object.assign(rest, parsePromptCachingOptions(prompt_cache_key, undefined, cache_control));
20
+ if (extra_body) {
21
+ for (const v of Object.values(extra_body)) {
22
+ Object.assign(rest, v);
23
+ }
24
+ }
25
+ const { toolChoice: tc, activeTools } = convertToToolChoiceOptions(tool_choice);
26
+ return {
27
+ messages: convertToModelMessages(input, instructions),
28
+ tools: convertToToolSet(tools),
29
+ toolChoice: tc,
30
+ activeTools,
31
+ output: convertToOutput(text),
32
+ temperature,
33
+ maxOutputTokens: max_output_tokens,
34
+ stopWhen: max_tool_calls === undefined ? undefined : stepCountIs(max_tool_calls),
35
+ frequencyPenalty: frequency_penalty,
36
+ presencePenalty: presence_penalty,
37
+ topP: top_p,
38
+ providerOptions: {
39
+ unknown: rest,
40
+ },
41
+ };
42
+ }
43
+ function convertToOutput(text) {
44
+ if (!text?.format || text.format.type === "text") {
45
+ // FUTURE: Support text.verbosity when AI SDK adds top-level support
46
+ return undefined;
47
+ }
48
+ const { name, description, schema } = text.format;
49
+ return Output.object({
50
+ name,
51
+ description,
52
+ schema: jsonSchema(schema),
53
+ });
54
+ }
55
+ export function convertToModelMessages(input, instructions) {
56
+ const modelMessages = [];
57
+ if (instructions) {
58
+ modelMessages.push({
59
+ role: "system",
60
+ content: instructions,
61
+ });
62
+ }
63
+ if (typeof input === "string") {
64
+ modelMessages.push({
65
+ role: "user",
66
+ content: input,
67
+ });
68
+ return modelMessages;
69
+ }
70
+ const toolOutputByCallId = indexToolOutputs(input);
71
+ for (const item of input) {
72
+ if (item.type === "function_call_output")
73
+ continue;
74
+ if (item.type === "message") {
75
+ modelMessages.push(fromMessageItem(item));
76
+ continue;
77
+ }
78
+ if (item.type === "function_call") {
79
+ modelMessages.push(fromFunctionCallItem(item));
80
+ const toolResult = fromFunctionCallOutputItem(item, toolOutputByCallId);
81
+ if (toolResult)
82
+ modelMessages.push(toolResult);
83
+ continue;
84
+ }
85
+ if (item.type === "reasoning") {
86
+ modelMessages.push(fromReasoningItem(item));
87
+ continue;
88
+ }
89
+ }
90
+ return modelMessages;
91
+ }
92
+ function fromReasoningItem(item) {
93
+ const parts = [];
94
+ if (!item.summary || item.summary.length === 0) {
95
+ return { role: "assistant", content: parts };
96
+ }
97
+ let providerOptions;
98
+ if (item.extra_content || item.encrypted_content) {
99
+ providerOptions = item.extra_content ?? { unknown: {} };
100
+ if (item.encrypted_content) {
101
+ (providerOptions ??= {})["unknown"] = { redactedData: item.encrypted_content };
102
+ }
103
+ }
104
+ for (const s of item.summary) {
105
+ parts.push({
106
+ type: "reasoning",
107
+ text: s.text,
108
+ providerOptions,
109
+ });
110
+ }
111
+ return { role: "assistant", content: parts };
112
+ }
113
+ function indexToolOutputs(items) {
114
+ const map = new Map();
115
+ for (const item of items) {
116
+ if (item.type === "function_call_output") {
117
+ map.set(item.call_id, item);
118
+ }
119
+ }
120
+ return map;
121
+ }
122
+ function fromMessageItem(item) {
123
+ switch (item.role) {
124
+ case "system":
125
+ case "developer": {
126
+ const out = {
127
+ role: "system",
128
+ content: typeof item.content === "string"
129
+ ? item.content
130
+ : item.content
131
+ // FUTURE: Support multimodal content in system messages (currently limited to
132
+ // text by AI SDK)
133
+ .filter((p) => p.type === "input_text")
134
+ .map((p) => p.text)
135
+ .join(""),
136
+ };
137
+ if (item.extra_content) {
138
+ out.providerOptions = item.extra_content;
139
+ }
140
+ if (item.cache_control) {
141
+ (out.providerOptions ??= {})["unknown"] = { cache_control: item.cache_control };
142
+ }
143
+ return out;
144
+ }
145
+ case "user":
146
+ return fromUserMessageItem(item);
147
+ case "assistant":
148
+ return fromAssistantMessageItem(item);
149
+ }
150
+ throw new GatewayError("Unsupported message role", 400);
151
+ }
152
+ function fromUserMessageItem(item) {
153
+ const out = {
154
+ role: "user",
155
+ content: fromInputContent(item.content),
156
+ };
157
+ if (item.extra_content) {
158
+ out.providerOptions = item.extra_content;
159
+ }
160
+ if (item.cache_control) {
161
+ (out.providerOptions ??= {})["unknown"] = { cache_control: item.cache_control };
162
+ }
163
+ return out;
164
+ }
165
+ function fromImageInput(url) {
166
+ const { image, mediaType } = parseImageInput(url);
167
+ return {
168
+ type: "image",
169
+ image,
170
+ mediaType,
171
+ };
172
+ }
173
+ function fromFileInput(data, filename) {
174
+ return {
175
+ type: "file",
176
+ data: parseBase64(data),
177
+ filename,
178
+ mediaType: "application/octet-stream",
179
+ };
180
+ }
181
+ /**
182
+ * Converts input content (string or multimodal parts) into UserContent for messages.
183
+ * Uses unified ImagePart/FilePart schemas for the array case.
184
+ */
185
+ function fromInputContent(content) {
186
+ if (typeof content === "string") {
187
+ return content;
188
+ }
189
+ const result = [];
190
+ for (const part of content) {
191
+ switch (part.type) {
192
+ case "input_text":
193
+ result.push({ type: "text", text: part.text });
194
+ break;
195
+ case "input_image": {
196
+ if (part.image_url !== undefined) {
197
+ result.push(fromImageInput(part.image_url));
198
+ }
199
+ else if (part.file_id !== undefined) {
200
+ result.push({ type: "image", image: part.file_id });
201
+ }
202
+ break;
203
+ }
204
+ case "input_file": {
205
+ if (part.file_data !== undefined) {
206
+ result.push(fromFileInput(part.file_data, part.filename));
207
+ }
208
+ else if (part.file_url !== undefined) {
209
+ result.push({
210
+ type: "file",
211
+ data: parseUrl(part.file_url),
212
+ filename: part.filename,
213
+ mediaType: "application/octet-stream",
214
+ });
215
+ }
216
+ else if (part.file_id !== undefined) {
217
+ result.push({
218
+ type: "file",
219
+ data: part.file_id,
220
+ filename: part.filename,
221
+ mediaType: "application/octet-stream",
222
+ });
223
+ }
224
+ break;
225
+ }
226
+ case "input_audio": {
227
+ const out = {
228
+ type: "file",
229
+ data: parseBase64(part.input_audio.data),
230
+ mediaType: `audio/${part.input_audio.format}`,
231
+ };
232
+ if (part.cache_control) {
233
+ out.providerOptions = {
234
+ unknown: { cache_control: part.cache_control },
235
+ };
236
+ }
237
+ result.push(out);
238
+ break;
239
+ }
240
+ default:
241
+ throw new GatewayError(`Unsupported content part type: ${part.type}`, 400);
242
+ }
243
+ }
244
+ return result;
245
+ }
246
+ function fromAssistantMessageItem(item) {
247
+ let content;
248
+ if (typeof item.content === "string") {
249
+ content = item.content;
250
+ }
251
+ else {
252
+ const parts = [];
253
+ for (const part of item.content) {
254
+ if (part.type === "output_text") {
255
+ parts.push({ type: "text", text: part.text });
256
+ }
257
+ }
258
+ content = parts.length > 0 ? parts : "";
259
+ }
260
+ const out = { role: "assistant", content };
261
+ if (item.extra_content) {
262
+ out.providerOptions = item.extra_content;
263
+ }
264
+ if (item.cache_control) {
265
+ (out.providerOptions ??= {})["unknown"] = { cache_control: item.cache_control };
266
+ }
267
+ return out;
268
+ }
269
+ function fromFunctionCallItem(item) {
270
+ const toolCall = {
271
+ type: "tool-call",
272
+ toolCallId: item.call_id,
273
+ toolName: item.name,
274
+ input: parseJsonOrText(item.arguments).value,
275
+ };
276
+ if (item.extra_content) {
277
+ toolCall.providerOptions = item.extra_content;
278
+ }
279
+ if (item.cache_control) {
280
+ (toolCall.providerOptions ??= {})["unknown"] = { cache_control: item.cache_control };
281
+ }
282
+ return { role: "assistant", content: [toolCall] };
283
+ }
284
+ /**
285
+ * Converts a tool result (string or multimodal parts) into the schema required by ToolResultPart.
286
+ */
287
+ function fromToolOutput(output) {
288
+ if (typeof output === "string") {
289
+ return parseJsonOrText(output);
290
+ }
291
+ const value = [];
292
+ for (const part of output) {
293
+ if (part.type === "input_text") {
294
+ value.push({ type: "text", text: part.text });
295
+ continue;
296
+ }
297
+ if (part.type === "input_image") {
298
+ if (part.image_url !== undefined) {
299
+ const { image, mediaType } = parseImageInput(part.image_url);
300
+ if (image instanceof URL) {
301
+ value.push({ type: "image-url", url: image.toString() });
302
+ }
303
+ else {
304
+ value.push({
305
+ type: "image-data",
306
+ data: image,
307
+ mediaType: mediaType,
308
+ });
309
+ }
310
+ }
311
+ else if (part.file_id !== undefined) {
312
+ value.push({ type: "image-file-id", fileId: part.file_id });
313
+ }
314
+ continue;
315
+ }
316
+ if (part.type === "input_file") {
317
+ if (part.file_data !== undefined) {
318
+ value.push({
319
+ type: "file-data",
320
+ data: part.file_data,
321
+ mediaType: "application/octet-stream",
322
+ filename: part.filename,
323
+ });
324
+ }
325
+ else if (part.file_url !== undefined) {
326
+ value.push({ type: "file-url", url: part.file_url });
327
+ }
328
+ else if (part.file_id !== undefined) {
329
+ value.push({ type: "file-id", fileId: part.file_id });
330
+ }
331
+ continue;
332
+ }
333
+ if (part.type === "input_audio") {
334
+ value.push({
335
+ type: "file-data",
336
+ data: part.input_audio.data,
337
+ mediaType: `audio/${part.input_audio.format}`,
338
+ });
339
+ continue;
340
+ }
341
+ }
342
+ return {
343
+ type: "content",
344
+ value,
345
+ };
346
+ }
347
+ function fromFunctionCallOutputItem(item, toolOutputByCallId) {
348
+ const output = toolOutputByCallId.get(item.call_id);
349
+ if (!output)
350
+ return undefined;
351
+ return {
352
+ role: "tool",
353
+ content: [
354
+ {
355
+ type: "tool-result",
356
+ toolCallId: item.call_id,
357
+ toolName: item.name,
358
+ output: fromToolOutput(output.output),
359
+ },
360
+ ],
361
+ };
362
+ }
363
+ export const convertToToolSet = (tools) => {
364
+ if (!tools) {
365
+ return undefined;
366
+ }
367
+ const toolSet = {};
368
+ for (const t of tools) {
369
+ toolSet[t.name] = tool({
370
+ description: t.description,
371
+ inputSchema: jsonSchema(t.parameters),
372
+ strict: t.strict,
373
+ });
374
+ }
375
+ return toolSet;
376
+ };
377
+ export const convertToToolChoiceOptions = (toolChoice) => {
378
+ if (!toolChoice)
379
+ return {};
380
+ if (toolChoice === "none" ||
381
+ toolChoice === "auto" ||
382
+ toolChoice === "required" ||
383
+ toolChoice === "validated") {
384
+ // FUTURE: this is right now google specific, which is not supported by AI SDK, until then,
385
+ // we temporarily map it to auto for now
386
+ // https://docs.cloud.google.com/vertex-ai/generative-ai/docs/migrate/openai/overview
387
+ return { toolChoice: toolChoice === "validated" ? "auto" : toolChoice };
388
+ }
389
+ if ("type" in toolChoice && toolChoice.type === "allowed_tools") {
390
+ return {
391
+ toolChoice: toolChoice.allowed_tools.mode,
392
+ activeTools: toolChoice.allowed_tools.tools.map((toolRef) => toolRef.name),
393
+ };
394
+ }
395
+ return {
396
+ toolChoice: {
397
+ type: "tool",
398
+ toolName: toolChoice.name,
399
+ },
400
+ };
401
+ };
402
+ // --- Response Flow ---
403
+ export function toResponses(result, model, metadata) {
404
+ const now = Math.floor(Date.now() / 1000);
405
+ const output = toOutputItems(result);
406
+ const status = toResponsesStatus(result.finishReason);
407
+ return {
408
+ id: uuidv7(),
409
+ object: "response",
410
+ status,
411
+ model,
412
+ output,
413
+ usage: result.totalUsage ? toResponsesUsage(result.totalUsage) : null,
414
+ incomplete_details: status === "incomplete" ? { reason: toIncompleteReason(result.finishReason) } : null,
415
+ created_at: now,
416
+ completed_at: status === "completed" ? now : null,
417
+ service_tier: resolveResponseServiceTier(result.providerMetadata),
418
+ metadata,
419
+ provider_metadata: result.providerMetadata,
420
+ };
421
+ }
422
+ export function toResponsesResponse(result, model, metadata, responseInit) {
423
+ return toResponse(toResponses(result, model, metadata), responseInit);
424
+ }
425
+ export function toResponsesStream(result, model, metadata) {
426
+ return result.fullStream.pipeThrough(new ResponsesTransformStream(model, metadata));
427
+ }
428
+ export function toResponsesStreamResponse(result, model, metadata, responseInit) {
429
+ return toResponse(toResponsesStream(result, model, metadata), responseInit);
430
+ }
431
+ function toOutputItems(result) {
432
+ const output = [];
433
+ // Add reasoning items
434
+ for (const part of result.content) {
435
+ if (part.type === "reasoning") {
436
+ output.push(toReasoningOutputItem(part));
437
+ }
438
+ }
439
+ // Add function call items
440
+ if (result.toolCalls && result.toolCalls.length > 0) {
441
+ for (const tc of result.toolCalls) {
442
+ output.push(toFunctionCallItem(tc.toolCallId, tc.toolName, tc.input, tc.providerMetadata));
443
+ }
444
+ }
445
+ // Add message output item
446
+ const textParts = [];
447
+ for (const part of result.content) {
448
+ if (part.type === "text") {
449
+ textParts.push({
450
+ type: "output_text",
451
+ text: part.text,
452
+ annotations: [],
453
+ });
454
+ }
455
+ }
456
+ if (textParts.length > 0 || result.toolCalls.length === 0) {
457
+ const msgItem = {
458
+ type: "message",
459
+ id: uuidv7(),
460
+ role: "assistant",
461
+ status: "completed",
462
+ content: textParts.length > 0 ? textParts : [{ type: "output_text", text: "", annotations: [] }],
463
+ };
464
+ if (result.providerMetadata) {
465
+ msgItem.extra_content = result.providerMetadata;
466
+ }
467
+ output.push(msgItem);
468
+ }
469
+ return output;
470
+ }
471
+ function toReasoningOutputItem(reasoning) {
472
+ const item = {
473
+ type: "reasoning",
474
+ id: uuidv7(),
475
+ summary: [],
476
+ status: "completed",
477
+ };
478
+ if (reasoning.text) {
479
+ item.summary = [{ type: "summary_text", text: reasoning.text }];
480
+ }
481
+ const providerMetadata = reasoning.providerMetadata ?? {};
482
+ item.extra_content = providerMetadata;
483
+ const { redactedData } = extractReasoningMetadata(providerMetadata);
484
+ if (redactedData) {
485
+ item.encrypted_content = redactedData;
486
+ }
487
+ return item;
488
+ }
489
+ function toFunctionCallItem(toolCallId, toolName, input, providerMetadata, status = "completed") {
490
+ const item = {
491
+ type: "function_call",
492
+ id: uuidv7(),
493
+ call_id: toolCallId,
494
+ name: normalizeToolName(toolName),
495
+ arguments: typeof input === "string"
496
+ ? input
497
+ : JSON.stringify(stripEmptyKeys(input)),
498
+ status,
499
+ };
500
+ if (providerMetadata) {
501
+ item.extra_content = providerMetadata;
502
+ }
503
+ return item;
504
+ }
505
+ export function toResponsesUsage(usage) {
506
+ const result = {
507
+ input_tokens: usage.inputTokens ?? 0,
508
+ output_tokens: usage.outputTokens ?? 0,
509
+ total_tokens: usage.totalTokens ?? (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0),
510
+ };
511
+ if (usage.inputTokenDetails?.cacheReadTokens !== undefined) {
512
+ result.input_tokens_details = {
513
+ cached_tokens: usage.inputTokenDetails.cacheReadTokens,
514
+ };
515
+ }
516
+ if (usage.outputTokenDetails?.reasoningTokens !== undefined) {
517
+ result.output_tokens_details = {
518
+ reasoning_tokens: usage.outputTokenDetails.reasoningTokens,
519
+ };
520
+ }
521
+ return result;
522
+ }
523
+ function toResponsesStatus(finishReason) {
524
+ switch (finishReason) {
525
+ case "stop":
526
+ case "tool-calls":
527
+ return "completed";
528
+ case "length":
529
+ case "content-filter":
530
+ return "incomplete";
531
+ case "error":
532
+ case "other":
533
+ return "failed";
534
+ default:
535
+ return "completed";
536
+ }
537
+ }
538
+ function toIncompleteReason(finishReason) {
539
+ // This switch only handles reasons that result in an "incomplete" status
540
+ // ('stop', 'tool-calls', 'error', 'other' are handled by their respective statuses).
541
+ // oxlint-disable-next-line switch-exhaustiveness-check
542
+ switch (finishReason) {
543
+ case "length":
544
+ return "max_output_tokens";
545
+ case "content-filter":
546
+ return "content_filter";
547
+ default:
548
+ return "unknown";
549
+ }
550
+ }
551
+ // --- Streaming ---
552
+ export class ResponsesTransformStream extends TransformStream {
553
+ constructor(model, metadata) {
554
+ const responseId = uuidv7();
555
+ const creationTime = Math.floor(Date.now() / 1000);
556
+ let outputIndex = 0;
557
+ let messageItem;
558
+ let messageOutputIndex = -1;
559
+ let contentIndex = 0;
560
+ let reasoningItem;
561
+ let reasoningOutputIndex = -1;
562
+ let summaryIndex = 0;
563
+ let finishProviderMetadata;
564
+ const outputItems = [];
565
+ const inProgressToolCalls = new Map();
566
+ const createResponse = (status, usage, completedAt, incompleteDetails, serviceTier, providerMetadata) => ({
567
+ id: responseId,
568
+ object: "response",
569
+ status,
570
+ model,
571
+ output: outputItems.map((item) => {
572
+ if (item.type === "message") {
573
+ return {
574
+ type: "message",
575
+ id: item.id,
576
+ role: item.role,
577
+ status: item.status,
578
+ content: item.content.map((p) => ({
579
+ type: "output_text",
580
+ text: p.text,
581
+ annotations: p.annotations ? p.annotations.slice() : [],
582
+ })),
583
+ extra_content: item.extra_content,
584
+ };
585
+ }
586
+ if (item.type === "reasoning") {
587
+ return {
588
+ type: "reasoning",
589
+ id: item.id,
590
+ status: item.status,
591
+ summary: item.summary.map((s) => ({
592
+ type: "summary_text",
593
+ text: s.text,
594
+ })),
595
+ extra_content: item.extra_content,
596
+ encrypted_content: item.encrypted_content,
597
+ };
598
+ }
599
+ if (item.type === "function_call") {
600
+ return {
601
+ type: "function_call",
602
+ id: item.id,
603
+ call_id: item.call_id,
604
+ name: item.name,
605
+ arguments: item.arguments,
606
+ status: item.status,
607
+ extra_content: item.extra_content,
608
+ };
609
+ }
610
+ return item;
611
+ }),
612
+ usage,
613
+ created_at: creationTime,
614
+ completed_at: completedAt,
615
+ incomplete_details: incompleteDetails,
616
+ service_tier: serviceTier,
617
+ metadata,
618
+ provider_metadata: providerMetadata,
619
+ });
620
+ const initMessageItem = (controller, providerMetadata) => {
621
+ if (messageItem)
622
+ return;
623
+ messageItem = {
624
+ type: "message",
625
+ id: uuidv7(),
626
+ role: "assistant",
627
+ status: "in_progress",
628
+ content: [],
629
+ };
630
+ if (providerMetadata) {
631
+ messageItem.extra_content = providerMetadata;
632
+ }
633
+ messageOutputIndex = outputIndex++;
634
+ outputItems.push(messageItem);
635
+ controller.enqueue({
636
+ event: "response.output_item.added",
637
+ data: {
638
+ type: "response.output_item.added",
639
+ output_index: messageOutputIndex,
640
+ item: {
641
+ type: "message",
642
+ id: messageItem.id,
643
+ role: "assistant",
644
+ status: "in_progress",
645
+ content: [],
646
+ extra_content: messageItem.extra_content,
647
+ },
648
+ },
649
+ });
650
+ };
651
+ const closeReasoningItem = (controller) => {
652
+ if (reasoningItem && reasoningItem.summary.length > 0) {
653
+ const lastSummaryPart = reasoningItem.summary[summaryIndex];
654
+ if (lastSummaryPart) {
655
+ controller.enqueue({
656
+ event: "response.reasoning_summary_part.done",
657
+ data: {
658
+ type: "response.reasoning_summary_part.done",
659
+ item_id: reasoningItem.id,
660
+ output_index: reasoningOutputIndex,
661
+ summary_index: summaryIndex,
662
+ part: lastSummaryPart,
663
+ },
664
+ });
665
+ }
666
+ }
667
+ if (reasoningItem) {
668
+ reasoningItem.status = "completed";
669
+ controller.enqueue({
670
+ event: "response.output_item.done",
671
+ data: {
672
+ type: "response.output_item.done",
673
+ output_index: reasoningOutputIndex,
674
+ item: reasoningItem,
675
+ },
676
+ });
677
+ reasoningItem = undefined;
678
+ }
679
+ };
680
+ const closeMessageItem = (controller) => {
681
+ if (messageItem && messageItem.content.length > 0) {
682
+ const lastPart = messageItem.content[contentIndex];
683
+ if (lastPart) {
684
+ controller.enqueue({
685
+ event: "response.content_part.done",
686
+ data: {
687
+ type: "response.content_part.done",
688
+ item_id: messageItem.id,
689
+ output_index: messageOutputIndex,
690
+ content_index: contentIndex,
691
+ part: lastPart,
692
+ },
693
+ });
694
+ }
695
+ }
696
+ if (messageItem) {
697
+ messageItem.status = "completed";
698
+ controller.enqueue({
699
+ event: "response.output_item.done",
700
+ data: {
701
+ type: "response.output_item.done",
702
+ output_index: messageOutputIndex,
703
+ item: messageItem,
704
+ },
705
+ });
706
+ messageItem = undefined;
707
+ }
708
+ };
709
+ super({
710
+ start(controller) {
711
+ controller.enqueue({
712
+ event: "response.created",
713
+ data: {
714
+ type: "response.created",
715
+ response: createResponse("in_progress", null, null),
716
+ },
717
+ });
718
+ controller.enqueue({
719
+ event: "response.in_progress",
720
+ data: {
721
+ type: "response.in_progress",
722
+ response: createResponse("in_progress", null, null),
723
+ },
724
+ });
725
+ },
726
+ transform(part, controller) {
727
+ // We explicitly omit several stream part types from the AI SDK:
728
+ // - 'text-end': Item closure is handled generically on 'finish' / 'finish-step'
729
+ // - 'tool-result', 'tool-error', 'tool-output-denied': These only occur if the server
730
+ // executes the tools on behalf of the user. Our gateway only relays the model's
731
+ // request to call a tool back to the client.
732
+ // - 'start', 'start-step': Metadata events that do not map to our output items.
733
+ // - 'abort': Stream cancellation is handled at the network/abort-controller level.
734
+ // - 'source', 'file': No current schema support in the Responses format.
735
+ // oxlint-disable-next-line switch-exhaustiveness-check
736
+ switch (part.type) {
737
+ case "tool-input-start": {
738
+ const item = {
739
+ type: "function_call",
740
+ id: uuidv7(),
741
+ call_id: part.id,
742
+ name: normalizeToolName(part.toolName),
743
+ arguments: "",
744
+ status: "in_progress",
745
+ };
746
+ if (part.providerMetadata) {
747
+ item.extra_content = part.providerMetadata;
748
+ }
749
+ const fnOutputIndex = outputIndex++;
750
+ outputItems.push(item);
751
+ inProgressToolCalls.set(part.id, {
752
+ outputIndex: fnOutputIndex,
753
+ item,
754
+ accumulatedArgs: "",
755
+ });
756
+ controller.enqueue({
757
+ event: "response.output_item.added",
758
+ data: {
759
+ type: "response.output_item.added",
760
+ output_index: fnOutputIndex,
761
+ item: {
762
+ type: "function_call",
763
+ id: item.id,
764
+ call_id: item.call_id,
765
+ name: item.name,
766
+ arguments: "",
767
+ status: "in_progress",
768
+ extra_content: item.extra_content,
769
+ },
770
+ },
771
+ });
772
+ break;
773
+ }
774
+ case "tool-input-delta": {
775
+ const inProgress = inProgressToolCalls.get(part.id);
776
+ if (!inProgress)
777
+ break;
778
+ inProgress.accumulatedArgs += part.delta;
779
+ inProgress.item.arguments = inProgress.accumulatedArgs;
780
+ controller.enqueue({
781
+ event: "response.function_call_arguments.delta",
782
+ data: {
783
+ type: "response.function_call_arguments.delta",
784
+ output_index: inProgress.outputIndex,
785
+ item_id: inProgress.item.id,
786
+ call_id: part.id,
787
+ delta: part.delta,
788
+ },
789
+ });
790
+ break;
791
+ }
792
+ case "tool-input-end": {
793
+ const inProgress = inProgressToolCalls.get(part.id);
794
+ if (!inProgress)
795
+ break;
796
+ controller.enqueue({
797
+ event: "response.function_call_arguments.done",
798
+ data: {
799
+ type: "response.function_call_arguments.done",
800
+ output_index: inProgress.outputIndex,
801
+ item_id: inProgress.item.id,
802
+ call_id: part.id,
803
+ arguments: inProgress.accumulatedArgs,
804
+ },
805
+ });
806
+ break;
807
+ }
808
+ case "reasoning-start": {
809
+ if (reasoningItem)
810
+ break;
811
+ reasoningItem = {
812
+ type: "reasoning",
813
+ id: uuidv7(),
814
+ status: "in_progress",
815
+ summary: [],
816
+ };
817
+ const providerMetadata = part.providerMetadata;
818
+ if (providerMetadata) {
819
+ reasoningItem.extra_content = providerMetadata;
820
+ const { redactedData } = extractReasoningMetadata(providerMetadata);
821
+ if (redactedData) {
822
+ reasoningItem.encrypted_content = redactedData;
823
+ }
824
+ }
825
+ reasoningOutputIndex = outputIndex++;
826
+ outputItems.push(reasoningItem);
827
+ controller.enqueue({
828
+ event: "response.output_item.added",
829
+ data: {
830
+ type: "response.output_item.added",
831
+ output_index: reasoningOutputIndex,
832
+ item: {
833
+ type: "reasoning",
834
+ id: reasoningItem.id,
835
+ status: "in_progress",
836
+ summary: [],
837
+ extra_content: reasoningItem.extra_content,
838
+ encrypted_content: reasoningItem.encrypted_content,
839
+ },
840
+ },
841
+ });
842
+ break;
843
+ }
844
+ case "reasoning-delta": {
845
+ if (summaryIndex === reasoningItem.summary.length) {
846
+ const summaryPart = {
847
+ type: "summary_text",
848
+ text: "",
849
+ };
850
+ reasoningItem.summary.push(summaryPart);
851
+ controller.enqueue({
852
+ event: "response.reasoning_summary_part.added",
853
+ data: {
854
+ type: "response.reasoning_summary_part.added",
855
+ item_id: reasoningItem.id,
856
+ output_index: reasoningOutputIndex,
857
+ summary_index: summaryIndex,
858
+ part: {
859
+ type: "summary_text",
860
+ text: "",
861
+ },
862
+ },
863
+ });
864
+ }
865
+ reasoningItem.summary[summaryIndex].text += part.text;
866
+ controller.enqueue({
867
+ event: "response.reasoning_summary_text.delta",
868
+ data: {
869
+ type: "response.reasoning_summary_text.delta",
870
+ item_id: reasoningItem.id,
871
+ output_index: reasoningOutputIndex,
872
+ summary_index: summaryIndex,
873
+ delta: part.text,
874
+ },
875
+ });
876
+ break;
877
+ }
878
+ case "reasoning-end": {
879
+ closeReasoningItem(controller);
880
+ break;
881
+ }
882
+ case "text-start": {
883
+ initMessageItem(controller, part.providerMetadata);
884
+ break;
885
+ }
886
+ case "text-delta": {
887
+ if (contentIndex === messageItem.content.length) {
888
+ const textPart = {
889
+ type: "output_text",
890
+ text: "",
891
+ annotations: [],
892
+ };
893
+ messageItem.content.push(textPart);
894
+ controller.enqueue({
895
+ event: "response.content_part.added",
896
+ data: {
897
+ type: "response.content_part.added",
898
+ item_id: messageItem.id,
899
+ output_index: messageOutputIndex,
900
+ content_index: contentIndex,
901
+ part: {
902
+ type: "output_text",
903
+ text: "",
904
+ annotations: [],
905
+ },
906
+ },
907
+ });
908
+ }
909
+ messageItem.content[contentIndex].text += part.text;
910
+ controller.enqueue({
911
+ event: "response.output_text.delta",
912
+ data: {
913
+ type: "response.output_text.delta",
914
+ item_id: messageItem.id,
915
+ output_index: messageOutputIndex,
916
+ content_index: contentIndex,
917
+ delta: part.text,
918
+ },
919
+ });
920
+ break;
921
+ }
922
+ case "tool-call": {
923
+ const inProgress = inProgressToolCalls.get(part.toolCallId);
924
+ let fnItem;
925
+ let fnOutputIndex;
926
+ if (inProgress) {
927
+ fnItem = inProgress.item;
928
+ fnOutputIndex = inProgress.outputIndex;
929
+ // Update with final parsed input if possible
930
+ fnItem.arguments =
931
+ typeof part.input === "string"
932
+ ? part.input
933
+ : JSON.stringify(stripEmptyKeys(part.input));
934
+ fnItem.status = "completed";
935
+ if (part.providerMetadata) {
936
+ fnItem.extra_content = part.providerMetadata;
937
+ }
938
+ inProgressToolCalls.delete(part.toolCallId);
939
+ }
940
+ else {
941
+ fnItem = toFunctionCallItem(part.toolCallId, part.toolName, part.input, part.providerMetadata);
942
+ fnOutputIndex = outputIndex++;
943
+ outputItems.push(fnItem);
944
+ controller.enqueue({
945
+ event: "response.output_item.added",
946
+ data: {
947
+ type: "response.output_item.added",
948
+ output_index: fnOutputIndex,
949
+ item: {
950
+ type: "function_call",
951
+ id: fnItem.id,
952
+ call_id: fnItem.call_id,
953
+ name: fnItem.name,
954
+ arguments: fnItem.arguments,
955
+ status: "completed",
956
+ extra_content: fnItem.extra_content,
957
+ },
958
+ },
959
+ });
960
+ }
961
+ controller.enqueue({
962
+ event: "response.output_item.done",
963
+ data: {
964
+ type: "response.output_item.done",
965
+ output_index: fnOutputIndex,
966
+ item: fnItem,
967
+ },
968
+ });
969
+ break;
970
+ }
971
+ case "finish-step": {
972
+ finishProviderMetadata = part.providerMetadata;
973
+ break;
974
+ }
975
+ case "finish": {
976
+ // Ensure empty message item is emitted if no tool calls and no text
977
+ if (!messageItem && !outputItems.some((i) => i.type === "function_call")) {
978
+ initMessageItem(controller);
979
+ const textPart = {
980
+ type: "output_text",
981
+ text: "",
982
+ annotations: [],
983
+ };
984
+ messageItem.content.push(textPart);
985
+ controller.enqueue({
986
+ event: "response.content_part.added",
987
+ data: {
988
+ type: "response.content_part.added",
989
+ item_id: messageItem.id,
990
+ output_index: messageOutputIndex,
991
+ content_index: contentIndex,
992
+ part: {
993
+ type: "output_text",
994
+ text: "",
995
+ annotations: [],
996
+ },
997
+ },
998
+ });
999
+ }
1000
+ closeReasoningItem(controller);
1001
+ closeMessageItem(controller);
1002
+ const status = toResponsesStatus(part.finishReason);
1003
+ const usage = part.totalUsage ? toResponsesUsage(part.totalUsage) : null;
1004
+ const now = Math.floor(Date.now() / 1000);
1005
+ const incompleteDetails = status === "incomplete" ? { reason: toIncompleteReason(part.finishReason) } : null;
1006
+ const finalResponse = createResponse(status, usage, status === "completed" ? now : null, incompleteDetails, resolveResponseServiceTier(finishProviderMetadata), finishProviderMetadata);
1007
+ if (status === "failed") {
1008
+ controller.enqueue({
1009
+ event: "response.failed",
1010
+ data: {
1011
+ type: "response.failed",
1012
+ response: finalResponse,
1013
+ },
1014
+ });
1015
+ }
1016
+ else {
1017
+ controller.enqueue({
1018
+ event: "response.completed",
1019
+ data: {
1020
+ type: "response.completed",
1021
+ response: finalResponse,
1022
+ },
1023
+ });
1024
+ }
1025
+ break;
1026
+ }
1027
+ case "error": {
1028
+ controller.enqueue({
1029
+ data: part.error instanceof Error ? part.error : new Error(String(part.error)),
1030
+ });
1031
+ break;
1032
+ }
1033
+ }
1034
+ },
1035
+ });
1036
+ }
1037
+ }