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