@hebo-ai/gateway 0.6.2 → 0.8.0-rc0

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 (168) hide show
  1. package/README.md +134 -7
  2. package/package.json +46 -1
  3. package/dist/config.d.ts +0 -2
  4. package/dist/config.js +0 -81
  5. package/dist/endpoints/chat-completions/converters.d.ts +0 -43
  6. package/dist/endpoints/chat-completions/converters.js +0 -625
  7. package/dist/endpoints/chat-completions/handler.d.ts +0 -2
  8. package/dist/endpoints/chat-completions/handler.js +0 -149
  9. package/dist/endpoints/chat-completions/index.d.ts +0 -4
  10. package/dist/endpoints/chat-completions/index.js +0 -4
  11. package/dist/endpoints/chat-completions/otel.d.ts +0 -5
  12. package/dist/endpoints/chat-completions/otel.js +0 -171
  13. package/dist/endpoints/chat-completions/schema.d.ts +0 -1188
  14. package/dist/endpoints/chat-completions/schema.js +0 -298
  15. package/dist/endpoints/embeddings/converters.d.ts +0 -10
  16. package/dist/endpoints/embeddings/converters.js +0 -31
  17. package/dist/endpoints/embeddings/handler.d.ts +0 -2
  18. package/dist/endpoints/embeddings/handler.js +0 -104
  19. package/dist/endpoints/embeddings/index.d.ts +0 -4
  20. package/dist/endpoints/embeddings/index.js +0 -4
  21. package/dist/endpoints/embeddings/otel.d.ts +0 -5
  22. package/dist/endpoints/embeddings/otel.js +0 -29
  23. package/dist/endpoints/embeddings/schema.d.ts +0 -44
  24. package/dist/endpoints/embeddings/schema.js +0 -29
  25. package/dist/endpoints/models/converters.d.ts +0 -6
  26. package/dist/endpoints/models/converters.js +0 -42
  27. package/dist/endpoints/models/handler.d.ts +0 -2
  28. package/dist/endpoints/models/handler.js +0 -29
  29. package/dist/endpoints/models/index.d.ts +0 -3
  30. package/dist/endpoints/models/index.js +0 -3
  31. package/dist/endpoints/models/schema.d.ts +0 -42
  32. package/dist/endpoints/models/schema.js +0 -31
  33. package/dist/errors/ai-sdk.d.ts +0 -2
  34. package/dist/errors/ai-sdk.js +0 -52
  35. package/dist/errors/gateway.d.ts +0 -5
  36. package/dist/errors/gateway.js +0 -13
  37. package/dist/errors/openai.d.ts +0 -20
  38. package/dist/errors/openai.js +0 -40
  39. package/dist/errors/utils.d.ts +0 -22
  40. package/dist/errors/utils.js +0 -44
  41. package/dist/gateway.d.ts +0 -9
  42. package/dist/gateway.js +0 -40
  43. package/dist/index.d.ts +0 -14
  44. package/dist/index.js +0 -13
  45. package/dist/lifecycle.d.ts +0 -2
  46. package/dist/lifecycle.js +0 -98
  47. package/dist/logger/default.d.ts +0 -4
  48. package/dist/logger/default.js +0 -81
  49. package/dist/logger/index.d.ts +0 -11
  50. package/dist/logger/index.js +0 -25
  51. package/dist/middleware/common.d.ts +0 -12
  52. package/dist/middleware/common.js +0 -146
  53. package/dist/middleware/debug.d.ts +0 -3
  54. package/dist/middleware/debug.js +0 -27
  55. package/dist/middleware/matcher.d.ts +0 -28
  56. package/dist/middleware/matcher.js +0 -118
  57. package/dist/middleware/utils.d.ts +0 -2
  58. package/dist/middleware/utils.js +0 -24
  59. package/dist/models/amazon/index.d.ts +0 -2
  60. package/dist/models/amazon/index.js +0 -2
  61. package/dist/models/amazon/middleware.d.ts +0 -3
  62. package/dist/models/amazon/middleware.js +0 -68
  63. package/dist/models/amazon/presets.d.ts +0 -345
  64. package/dist/models/amazon/presets.js +0 -80
  65. package/dist/models/anthropic/index.d.ts +0 -2
  66. package/dist/models/anthropic/index.js +0 -2
  67. package/dist/models/anthropic/middleware.d.ts +0 -5
  68. package/dist/models/anthropic/middleware.js +0 -127
  69. package/dist/models/anthropic/presets.d.ts +0 -711
  70. package/dist/models/anthropic/presets.js +0 -135
  71. package/dist/models/catalog.d.ts +0 -4
  72. package/dist/models/catalog.js +0 -8
  73. package/dist/models/cohere/index.d.ts +0 -2
  74. package/dist/models/cohere/index.js +0 -2
  75. package/dist/models/cohere/middleware.d.ts +0 -3
  76. package/dist/models/cohere/middleware.js +0 -62
  77. package/dist/models/cohere/presets.d.ts +0 -411
  78. package/dist/models/cohere/presets.js +0 -134
  79. package/dist/models/google/index.d.ts +0 -2
  80. package/dist/models/google/index.js +0 -2
  81. package/dist/models/google/middleware.d.ts +0 -8
  82. package/dist/models/google/middleware.js +0 -111
  83. package/dist/models/google/presets.d.ts +0 -375
  84. package/dist/models/google/presets.js +0 -82
  85. package/dist/models/meta/index.d.ts +0 -1
  86. package/dist/models/meta/index.js +0 -1
  87. package/dist/models/meta/presets.d.ts +0 -483
  88. package/dist/models/meta/presets.js +0 -95
  89. package/dist/models/openai/index.d.ts +0 -2
  90. package/dist/models/openai/index.js +0 -2
  91. package/dist/models/openai/middleware.d.ts +0 -4
  92. package/dist/models/openai/middleware.js +0 -88
  93. package/dist/models/openai/presets.d.ts +0 -959
  94. package/dist/models/openai/presets.js +0 -213
  95. package/dist/models/types.d.ts +0 -20
  96. package/dist/models/types.js +0 -84
  97. package/dist/models/voyage/index.d.ts +0 -2
  98. package/dist/models/voyage/index.js +0 -2
  99. package/dist/models/voyage/middleware.d.ts +0 -2
  100. package/dist/models/voyage/middleware.js +0 -19
  101. package/dist/models/voyage/presets.d.ts +0 -436
  102. package/dist/models/voyage/presets.js +0 -85
  103. package/dist/providers/anthropic/canonical.d.ts +0 -3
  104. package/dist/providers/anthropic/canonical.js +0 -9
  105. package/dist/providers/anthropic/index.d.ts +0 -1
  106. package/dist/providers/anthropic/index.js +0 -1
  107. package/dist/providers/bedrock/canonical.d.ts +0 -17
  108. package/dist/providers/bedrock/canonical.js +0 -61
  109. package/dist/providers/bedrock/index.d.ts +0 -2
  110. package/dist/providers/bedrock/index.js +0 -2
  111. package/dist/providers/bedrock/middleware.d.ts +0 -4
  112. package/dist/providers/bedrock/middleware.js +0 -104
  113. package/dist/providers/cohere/canonical.d.ts +0 -3
  114. package/dist/providers/cohere/canonical.js +0 -17
  115. package/dist/providers/cohere/index.d.ts +0 -1
  116. package/dist/providers/cohere/index.js +0 -1
  117. package/dist/providers/groq/canonical.d.ts +0 -3
  118. package/dist/providers/groq/canonical.js +0 -12
  119. package/dist/providers/groq/index.d.ts +0 -1
  120. package/dist/providers/groq/index.js +0 -1
  121. package/dist/providers/openai/canonical.d.ts +0 -3
  122. package/dist/providers/openai/canonical.js +0 -8
  123. package/dist/providers/openai/index.d.ts +0 -1
  124. package/dist/providers/openai/index.js +0 -1
  125. package/dist/providers/registry.d.ts +0 -24
  126. package/dist/providers/registry.js +0 -103
  127. package/dist/providers/types.d.ts +0 -7
  128. package/dist/providers/types.js +0 -11
  129. package/dist/providers/vertex/canonical.d.ts +0 -3
  130. package/dist/providers/vertex/canonical.js +0 -8
  131. package/dist/providers/vertex/index.d.ts +0 -1
  132. package/dist/providers/vertex/index.js +0 -1
  133. package/dist/providers/voyage/canonical.d.ts +0 -3
  134. package/dist/providers/voyage/canonical.js +0 -7
  135. package/dist/providers/voyage/index.d.ts +0 -1
  136. package/dist/providers/voyage/index.js +0 -1
  137. package/dist/telemetry/ai-sdk.d.ts +0 -2
  138. package/dist/telemetry/ai-sdk.js +0 -31
  139. package/dist/telemetry/baggage.d.ts +0 -1
  140. package/dist/telemetry/baggage.js +0 -24
  141. package/dist/telemetry/fetch.d.ts +0 -2
  142. package/dist/telemetry/fetch.js +0 -49
  143. package/dist/telemetry/gen-ai.d.ts +0 -6
  144. package/dist/telemetry/gen-ai.js +0 -78
  145. package/dist/telemetry/http.d.ts +0 -3
  146. package/dist/telemetry/http.js +0 -54
  147. package/dist/telemetry/index.d.ts +0 -1
  148. package/dist/telemetry/index.js +0 -1
  149. package/dist/telemetry/memory.d.ts +0 -2
  150. package/dist/telemetry/memory.js +0 -43
  151. package/dist/telemetry/span.d.ts +0 -13
  152. package/dist/telemetry/span.js +0 -60
  153. package/dist/telemetry/stream.d.ts +0 -3
  154. package/dist/telemetry/stream.js +0 -58
  155. package/dist/types.d.ts +0 -176
  156. package/dist/types.js +0 -1
  157. package/dist/utils/env.d.ts +0 -2
  158. package/dist/utils/env.js +0 -7
  159. package/dist/utils/headers.d.ts +0 -4
  160. package/dist/utils/headers.js +0 -22
  161. package/dist/utils/preset.d.ts +0 -10
  162. package/dist/utils/preset.js +0 -42
  163. package/dist/utils/request.d.ts +0 -2
  164. package/dist/utils/request.js +0 -43
  165. package/dist/utils/response.d.ts +0 -3
  166. package/dist/utils/response.js +0 -70
  167. package/dist/utils/url.d.ts +0 -4
  168. package/dist/utils/url.js +0 -21
@@ -1,625 +0,0 @@
1
- import { Output, jsonSchema, tool } from "ai";
2
- import { z } from "zod";
3
- import { GatewayError } from "../../errors/gateway";
4
- import { OpenAIError, toOpenAIError } from "../../errors/openai";
5
- import { toResponse } from "../../utils/response";
6
- import { parseDataUrl } from "../../utils/url";
7
- // --- Request Flow ---
8
- export function convertToTextCallOptions(params) {
9
- const { messages, tools, tool_choice, temperature, max_tokens, max_completion_tokens, response_format, reasoning_effort, reasoning, prompt_cache_key, prompt_cache_retention, extra_body, cache_control, frequency_penalty, presence_penalty, seed, stop, top_p, ...rest } = params;
10
- Object.assign(rest, parseReasoningOptions(reasoning_effort, reasoning));
11
- Object.assign(rest, parsePromptCachingOptions(prompt_cache_key, prompt_cache_retention, cache_control));
12
- if (extra_body) {
13
- for (const v of Object.values(extra_body)) {
14
- Object.assign(rest, v);
15
- }
16
- }
17
- const { toolChoice, activeTools } = convertToToolChoiceOptions(tool_choice);
18
- return {
19
- messages: convertToModelMessages(messages),
20
- tools: convertToToolSet(tools),
21
- toolChoice,
22
- activeTools,
23
- output: convertToOutput(response_format),
24
- temperature,
25
- maxOutputTokens: max_completion_tokens ?? max_tokens,
26
- frequencyPenalty: frequency_penalty,
27
- presencePenalty: presence_penalty,
28
- seed,
29
- stopSequences: stop ? (Array.isArray(stop) ? stop : [stop]) : undefined,
30
- topP: top_p,
31
- providerOptions: {
32
- unknown: rest,
33
- },
34
- };
35
- }
36
- function convertToOutput(responseFormat) {
37
- if (!responseFormat || responseFormat.type === "text") {
38
- return;
39
- }
40
- const { name, description, schema } = responseFormat.json_schema;
41
- return Output.object({
42
- name,
43
- description,
44
- schema: jsonSchema(schema),
45
- });
46
- }
47
- export function convertToModelMessages(messages) {
48
- const modelMessages = [];
49
- const toolById = indexToolMessages(messages);
50
- for (const message of messages) {
51
- if (message.role === "tool")
52
- continue;
53
- if (message.role === "system") {
54
- if (message.cache_control) {
55
- message.providerOptions = {
56
- unknown: { cache_control: message.cache_control },
57
- };
58
- }
59
- modelMessages.push(message);
60
- continue;
61
- }
62
- if (message.role === "user") {
63
- modelMessages.push(fromChatCompletionsUserMessage(message));
64
- continue;
65
- }
66
- modelMessages.push(fromChatCompletionsAssistantMessage(message));
67
- const toolResult = fromChatCompletionsToolResultMessage(message, toolById);
68
- if (toolResult)
69
- modelMessages.push(toolResult);
70
- }
71
- return modelMessages;
72
- }
73
- function indexToolMessages(messages) {
74
- const map = new Map();
75
- for (const m of messages) {
76
- if (m.role === "tool")
77
- map.set(m.tool_call_id, m);
78
- }
79
- return map;
80
- }
81
- export function fromChatCompletionsUserMessage(message) {
82
- const out = {
83
- role: "user",
84
- content: Array.isArray(message.content)
85
- ? fromChatCompletionsContent(message.content)
86
- : message.content,
87
- };
88
- if (message.cache_control) {
89
- out.providerOptions = {
90
- unknown: { cache_control: message.cache_control },
91
- };
92
- }
93
- return out;
94
- }
95
- export function fromChatCompletionsAssistantMessage(message) {
96
- const { tool_calls, role, content, extra_content, reasoning_details, cache_control } = message;
97
- const parts = [];
98
- if (reasoning_details?.length) {
99
- for (const detail of reasoning_details) {
100
- if (detail.text && detail.type === "reasoning.text") {
101
- parts.push({
102
- type: "reasoning",
103
- text: detail.text,
104
- providerOptions: detail.signature
105
- ? {
106
- unknown: {
107
- signature: detail.signature,
108
- },
109
- }
110
- : undefined,
111
- });
112
- }
113
- else if (detail.type === "reasoning.encrypted" && detail.data) {
114
- parts.push({
115
- type: "reasoning",
116
- text: "",
117
- providerOptions: {
118
- unknown: {
119
- redactedData: detail.data,
120
- },
121
- },
122
- });
123
- }
124
- }
125
- }
126
- if (content !== undefined && content !== null) {
127
- const inputContent = typeof content === "string"
128
- ? [{ type: "text", text: content }]
129
- : content;
130
- for (const part of inputContent) {
131
- if (part.type === "text") {
132
- const textPart = {
133
- type: "text",
134
- text: part.text,
135
- };
136
- if (part.cache_control) {
137
- textPart.providerOptions = {
138
- unknown: { cache_control: part.cache_control },
139
- };
140
- }
141
- parts.push(textPart);
142
- }
143
- }
144
- }
145
- if (tool_calls?.length) {
146
- for (const tc of tool_calls) {
147
- // oxlint-disable-next-line no-shadow
148
- const { id, function: fn, extra_content } = tc;
149
- const out = {
150
- type: "tool-call",
151
- toolCallId: id,
152
- toolName: fn.name,
153
- input: parseJsonOrText(fn.arguments).value,
154
- };
155
- if (extra_content) {
156
- out.providerOptions = extra_content;
157
- }
158
- parts.push(out);
159
- }
160
- }
161
- const out = {
162
- role,
163
- content: parts.length > 0 ? parts : (content ?? ""),
164
- };
165
- if (extra_content) {
166
- out.providerOptions = extra_content;
167
- }
168
- if (cache_control) {
169
- ((out.providerOptions ??= { unknown: {} })["unknown"] ??= {})["cache_control"] = cache_control;
170
- }
171
- return out;
172
- }
173
- export function fromChatCompletionsToolResultMessage(message, toolById) {
174
- const toolCalls = message.tool_calls ?? [];
175
- if (toolCalls.length === 0)
176
- return undefined;
177
- const toolResultParts = [];
178
- for (const tc of toolCalls) {
179
- const toolMsg = toolById.get(tc.id);
180
- if (!toolMsg)
181
- continue;
182
- toolResultParts.push({
183
- type: "tool-result",
184
- toolCallId: tc.id,
185
- toolName: tc.function.name,
186
- output: parseToolResult(toolMsg.content),
187
- });
188
- }
189
- return toolResultParts.length > 0 ? { role: "tool", content: toolResultParts } : undefined;
190
- }
191
- export function fromChatCompletionsContent(content) {
192
- return content.map((part) => {
193
- switch (part.type) {
194
- case "image_url":
195
- return fromImageUrlPart(part.image_url.url, part.cache_control);
196
- case "file":
197
- return fromFilePart(part.file.data, part.file.media_type, part.file.filename, part.cache_control);
198
- case "input_audio":
199
- return fromFilePart(part.input_audio.data, `audio/${part.input_audio.format}`, undefined, part.cache_control);
200
- case "text": {
201
- const out = {
202
- type: "text",
203
- text: part.text,
204
- };
205
- if (part.cache_control) {
206
- out.providerOptions = {
207
- unknown: { cache_control: part.cache_control },
208
- };
209
- }
210
- return out;
211
- }
212
- default:
213
- throw new Error(`Unhandled content part type: ${part.type}`);
214
- }
215
- });
216
- }
217
- function fromImageUrlPart(url, cacheControl) {
218
- if (url.startsWith("data:")) {
219
- const { mimeType, dataStart } = parseDataUrl(url);
220
- if (!mimeType || dataStart <= "data:".length || dataStart >= url.length) {
221
- throw new GatewayError("Invalid data URL", 400);
222
- }
223
- return fromFilePart(url.slice(dataStart), mimeType, undefined, cacheControl);
224
- }
225
- const out = {
226
- type: "image",
227
- image: new URL(url),
228
- };
229
- if (cacheControl) {
230
- out.providerOptions = {
231
- unknown: { cache_control: cacheControl },
232
- };
233
- }
234
- return out;
235
- }
236
- function fromFilePart(base64Data, mediaType, filename, cacheControl) {
237
- if (mediaType.startsWith("image/")) {
238
- const out = {
239
- type: "image",
240
- image: z.util.base64ToUint8Array(base64Data),
241
- mediaType,
242
- };
243
- if (cacheControl) {
244
- out.providerOptions = {
245
- unknown: { cache_control: cacheControl },
246
- };
247
- }
248
- return out;
249
- }
250
- const out = {
251
- type: "file",
252
- data: z.util.base64ToUint8Array(base64Data),
253
- filename,
254
- mediaType,
255
- };
256
- if (cacheControl) {
257
- out.providerOptions = {
258
- unknown: { cache_control: cacheControl },
259
- };
260
- }
261
- return out;
262
- }
263
- export const convertToToolSet = (tools) => {
264
- if (!tools) {
265
- return;
266
- }
267
- const toolSet = {};
268
- for (const t of tools) {
269
- toolSet[t.function.name] = tool({
270
- description: t.function.description,
271
- inputSchema: jsonSchema(t.function.parameters),
272
- strict: t.function.strict,
273
- });
274
- }
275
- return toolSet;
276
- };
277
- export const convertToToolChoiceOptions = (toolChoice) => {
278
- if (!toolChoice) {
279
- return {};
280
- }
281
- if (toolChoice === "none" || toolChoice === "auto" || toolChoice === "required") {
282
- return { toolChoice };
283
- }
284
- // FUTURE: this is right now google specific, which is not supported by AI SDK, until then, we temporarily map it to auto for now https://docs.cloud.google.com/vertex-ai/generative-ai/docs/migrate/openai/overview
285
- if (toolChoice === "validated") {
286
- return { toolChoice: "auto" };
287
- }
288
- if (toolChoice.type === "allowed_tools") {
289
- return {
290
- toolChoice: toolChoice.allowed_tools.mode,
291
- activeTools: toolChoice.allowed_tools.tools.map((toolRef) => toolRef.function.name),
292
- };
293
- }
294
- return {
295
- toolChoice: {
296
- type: "tool",
297
- toolName: toolChoice.function.name,
298
- },
299
- };
300
- };
301
- function parseToolResult(content) {
302
- if (Array.isArray(content)) {
303
- return {
304
- type: "content",
305
- value: content.map((part) => ({
306
- type: "text",
307
- text: part.text,
308
- })),
309
- };
310
- }
311
- return parseJsonOrText(content);
312
- }
313
- function parseJsonOrText(content) {
314
- try {
315
- // oxlint-disable-next-line no-unsafe-assignment
316
- return { type: "json", value: JSON.parse(content) };
317
- }
318
- catch {
319
- return { type: "text", value: content };
320
- }
321
- }
322
- function parseReasoningOptions(reasoning_effort, reasoning) {
323
- const effort = reasoning?.effort ?? reasoning_effort;
324
- const max_tokens = reasoning?.max_tokens;
325
- if (reasoning?.enabled === false || effort === "none") {
326
- return { reasoning: { enabled: false }, reasoning_effort: "none" };
327
- }
328
- if (!reasoning && effort === undefined)
329
- return {};
330
- const out = { reasoning: {} };
331
- if (effort) {
332
- out.reasoning.enabled = true;
333
- out.reasoning.effort = effort;
334
- out.reasoning_effort = effort;
335
- }
336
- if (max_tokens) {
337
- out.reasoning.enabled = true;
338
- out.reasoning.max_tokens = max_tokens;
339
- }
340
- if (out.reasoning.enabled) {
341
- out.reasoning.exclude = reasoning?.exclude;
342
- }
343
- return out;
344
- }
345
- function parsePromptCachingOptions(prompt_cache_key, prompt_cache_retention, cache_control) {
346
- const out = {};
347
- let retention = prompt_cache_retention;
348
- if (!retention && cache_control?.ttl) {
349
- retention = cache_control.ttl === "24h" ? "24h" : "in_memory";
350
- }
351
- let control = cache_control;
352
- if (!control && retention) {
353
- control = {
354
- type: "ephemeral",
355
- ttl: retention === "24h" ? "24h" : "5m",
356
- };
357
- }
358
- if (prompt_cache_key)
359
- out["prompt_cache_key"] = prompt_cache_key;
360
- if (retention)
361
- out["prompt_cache_retention"] = retention;
362
- if (control)
363
- out["cache_control"] = control;
364
- return out;
365
- }
366
- // --- Response Flow ---
367
- export function toChatCompletions(result, model) {
368
- return {
369
- id: "chatcmpl-" + crypto.randomUUID(),
370
- object: "chat.completion",
371
- created: Math.floor(Date.now() / 1000),
372
- model,
373
- choices: [
374
- {
375
- index: 0,
376
- message: toChatCompletionsAssistantMessage(result),
377
- finish_reason: toChatCompletionsFinishReason(result.finishReason),
378
- },
379
- ],
380
- usage: result.totalUsage ? toChatCompletionsUsage(result.totalUsage) : null,
381
- provider_metadata: result.providerMetadata,
382
- };
383
- }
384
- export function toChatCompletionsResponse(result, model, responseInit) {
385
- return toResponse(toChatCompletions(result, model), responseInit);
386
- }
387
- export function toChatCompletionsStream(result, model, wrapErrors) {
388
- return result.fullStream.pipeThrough(new ChatCompletionsStream(model, wrapErrors));
389
- }
390
- export function toChatCompletionsStreamResponse(result, model, responseInit) {
391
- return toResponse(toChatCompletionsStream(result, model, true), responseInit);
392
- }
393
- export class ChatCompletionsStream extends TransformStream {
394
- constructor(model, wrapErrors) {
395
- const streamId = `chatcmpl-${crypto.randomUUID()}`;
396
- const creationTime = Math.floor(Date.now() / 1000);
397
- let toolCallIndexCounter = 0;
398
- const reasoningIdToIndex = new Map();
399
- let finishProviderMetadata;
400
- const createChunk = (delta, provider_metadata, finish_reason, usage) => {
401
- if (provider_metadata) {
402
- delta.extra_content = provider_metadata;
403
- }
404
- return {
405
- id: streamId,
406
- object: "chat.completion.chunk",
407
- created: creationTime,
408
- model,
409
- choices: [
410
- {
411
- index: 0,
412
- delta,
413
- finish_reason: finish_reason ?? null,
414
- },
415
- ],
416
- usage: usage ?? null,
417
- };
418
- };
419
- super({
420
- transform(part, controller) {
421
- // oxlint-disable-next-line switch-exhaustiveness-check
422
- switch (part.type) {
423
- case "text-delta": {
424
- controller.enqueue(createChunk({ role: "assistant", content: part.text }, part.providerMetadata));
425
- break;
426
- }
427
- case "reasoning-delta": {
428
- let index = reasoningIdToIndex.get(part.id);
429
- if (index === undefined) {
430
- index = reasoningIdToIndex.size;
431
- reasoningIdToIndex.set(part.id, index);
432
- }
433
- controller.enqueue(createChunk({
434
- reasoning: part.text,
435
- reasoning_details: [
436
- toReasoningDetail({
437
- type: "reasoning",
438
- text: part.text,
439
- providerMetadata: part.providerMetadata,
440
- }, part.id, index),
441
- ],
442
- }, part.providerMetadata));
443
- break;
444
- }
445
- case "tool-call": {
446
- const toolCall = toChatCompletionsToolCall(part.toolCallId, part.toolName, part.input, part.providerMetadata);
447
- toolCall.index = toolCallIndexCounter++;
448
- controller.enqueue(createChunk({
449
- tool_calls: [toolCall],
450
- }));
451
- break;
452
- }
453
- case "finish-step": {
454
- finishProviderMetadata = part.providerMetadata;
455
- break;
456
- }
457
- case "finish": {
458
- controller.enqueue(createChunk({}, finishProviderMetadata, toChatCompletionsFinishReason(part.finishReason), toChatCompletionsUsage(part.totalUsage)));
459
- break;
460
- }
461
- case "error": {
462
- let err;
463
- if (wrapErrors) {
464
- err = toOpenAIError(part.error);
465
- }
466
- else if (part.error instanceof Error) {
467
- err = part.error;
468
- }
469
- else {
470
- err = new Error(String(part.error));
471
- }
472
- controller.enqueue(err);
473
- }
474
- }
475
- },
476
- });
477
- }
478
- }
479
- export const toChatCompletionsAssistantMessage = (result) => {
480
- const message = {
481
- role: "assistant",
482
- content: null,
483
- };
484
- if (result.toolCalls && result.toolCalls.length > 0) {
485
- message.tool_calls = result.toolCalls.map((toolCall) => toChatCompletionsToolCall(toolCall.toolCallId, toolCall.toolName, toolCall.input, toolCall.providerMetadata));
486
- }
487
- const reasoningDetails = [];
488
- for (const part of result.content) {
489
- if (part.type === "text") {
490
- if (message.content === null) {
491
- message.content = part.text;
492
- }
493
- else {
494
- message.content += part.text;
495
- }
496
- if (part.providerMetadata) {
497
- message.extra_content = part.providerMetadata;
498
- }
499
- }
500
- else if (part.type === "reasoning") {
501
- reasoningDetails.push(toReasoningDetail(part, `reasoning-${crypto.randomUUID()}`, reasoningDetails.length));
502
- }
503
- }
504
- if (result.reasoningText) {
505
- message.reasoning = result.reasoningText;
506
- }
507
- if (reasoningDetails.length > 0) {
508
- message.reasoning_details = reasoningDetails;
509
- }
510
- if (!message.content && !message.tool_calls) {
511
- // some models return just reasoning without tool calls or content
512
- message.content = "";
513
- }
514
- return message;
515
- };
516
- export function toReasoningDetail(reasoning, id, index) {
517
- const providerMetadata = reasoning.providerMetadata ?? {};
518
- let redactedData;
519
- let signature;
520
- for (const metadata of Object.values(providerMetadata)) {
521
- if (metadata && typeof metadata === "object") {
522
- if ("redactedData" in metadata && typeof metadata["redactedData"] === "string") {
523
- redactedData = metadata["redactedData"];
524
- }
525
- if ("signature" in metadata && typeof metadata["signature"] === "string") {
526
- signature = metadata["signature"];
527
- }
528
- }
529
- }
530
- if (redactedData) {
531
- return {
532
- id,
533
- index,
534
- type: "reasoning.encrypted",
535
- data: redactedData,
536
- format: "unknown",
537
- };
538
- }
539
- return {
540
- id,
541
- index,
542
- type: "reasoning.text",
543
- text: reasoning.text,
544
- signature,
545
- format: "unknown",
546
- };
547
- }
548
- export function toChatCompletionsUsage(usage) {
549
- const out = {};
550
- const prompt = usage.inputTokens;
551
- if (prompt !== undefined)
552
- out.prompt_tokens = prompt;
553
- const completion = usage.outputTokens;
554
- if (completion !== undefined)
555
- out.completion_tokens = completion;
556
- if (prompt !== undefined || completion !== undefined || usage.totalTokens !== undefined) {
557
- out.total_tokens = usage.totalTokens ?? (prompt ?? 0) + (completion ?? 0);
558
- }
559
- const reasoning = usage.outputTokenDetails?.reasoningTokens;
560
- if (reasoning !== undefined)
561
- out.completion_tokens_details = { reasoning_tokens: reasoning };
562
- const cached = usage.inputTokenDetails?.cacheReadTokens;
563
- const cacheWrite = usage.inputTokenDetails?.cacheWriteTokens;
564
- if (cached !== undefined || cacheWrite !== undefined) {
565
- out.prompt_tokens_details = {};
566
- if (cached !== undefined) {
567
- out.prompt_tokens_details.cached_tokens = cached;
568
- }
569
- if (cacheWrite !== undefined) {
570
- out.prompt_tokens_details.cache_write_tokens = cacheWrite;
571
- }
572
- }
573
- return out;
574
- }
575
- export function toChatCompletionsToolCall(id, name, args, providerMetadata) {
576
- const out = {
577
- id,
578
- type: "function",
579
- function: {
580
- name: normalizeToolName(name),
581
- arguments: typeof args === "string" ? args : JSON.stringify(stripEmptyKeys(args)),
582
- },
583
- };
584
- if (providerMetadata) {
585
- out.extra_content = providerMetadata;
586
- }
587
- return out;
588
- }
589
- function normalizeToolName(name) {
590
- // some models hallucinate invalid characters
591
- // normalize to valid characters [^A-Za-z0-9_-.] (non regex for perf)
592
- // https://modelcontextprotocol.io/specification/draft/server/tools#tool-names
593
- let out = "";
594
- for (let i = 0; i < name.length; i++) {
595
- if (out.length === 128)
596
- break;
597
- // oxlint-disable-next-line unicorn/prefer-code-point
598
- const c = name.charCodeAt(i);
599
- if ((c >= 48 && c <= 57) ||
600
- (c >= 65 && c <= 90) ||
601
- (c >= 97 && c <= 122) ||
602
- c === 95 ||
603
- c === 45 ||
604
- c === 46) {
605
- out += name[i];
606
- }
607
- else {
608
- out += "_";
609
- }
610
- }
611
- return out;
612
- }
613
- function stripEmptyKeys(obj) {
614
- if (!obj || typeof obj !== "object" || Array.isArray(obj))
615
- return obj;
616
- // some models hallucinate empty parameters
617
- delete obj[""];
618
- return obj;
619
- }
620
- export const toChatCompletionsFinishReason = (finishReason) => {
621
- if (finishReason === "error" || finishReason === "other") {
622
- return "stop";
623
- }
624
- return finishReason.replaceAll("-", "_");
625
- };
@@ -1,2 +0,0 @@
1
- import type { GatewayConfig, Endpoint } from "../../types";
2
- export declare const chatCompletions: (config: GatewayConfig) => Endpoint;