@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.
- package/dist/config.d.ts +2 -0
- package/dist/config.js +125 -0
- package/dist/endpoints/chat-completions/converters.d.ts +26 -0
- package/dist/endpoints/chat-completions/converters.js +525 -0
- package/dist/endpoints/chat-completions/handler.d.ts +2 -0
- package/dist/endpoints/chat-completions/handler.js +152 -0
- package/dist/endpoints/chat-completions/index.d.ts +4 -0
- package/dist/endpoints/chat-completions/index.js +4 -0
- package/dist/endpoints/chat-completions/otel.d.ts +5 -0
- package/dist/endpoints/chat-completions/otel.js +178 -0
- package/dist/endpoints/chat-completions/schema.d.ts +1170 -0
- package/dist/endpoints/chat-completions/schema.js +252 -0
- package/dist/endpoints/conversations/converters.d.ts +8 -0
- package/dist/endpoints/conversations/converters.js +29 -0
- package/dist/endpoints/conversations/handler.d.ts +2 -0
- package/dist/endpoints/conversations/handler.js +259 -0
- package/dist/endpoints/conversations/index.d.ts +3 -0
- package/dist/endpoints/conversations/index.js +3 -0
- package/dist/endpoints/conversations/schema.d.ts +1511 -0
- package/dist/endpoints/conversations/schema.js +74 -0
- package/dist/endpoints/conversations/storage/dialects/greptime.d.ts +10 -0
- package/dist/endpoints/conversations/storage/dialects/greptime.js +87 -0
- package/dist/endpoints/conversations/storage/dialects/mysql.d.ts +12 -0
- package/dist/endpoints/conversations/storage/dialects/mysql.js +118 -0
- package/dist/endpoints/conversations/storage/dialects/postgres.d.ts +16 -0
- package/dist/endpoints/conversations/storage/dialects/postgres.js +185 -0
- package/dist/endpoints/conversations/storage/dialects/sqlite.d.ts +11 -0
- package/dist/endpoints/conversations/storage/dialects/sqlite.js +176 -0
- package/dist/endpoints/conversations/storage/dialects/types.d.ts +42 -0
- package/dist/endpoints/conversations/storage/dialects/types.js +0 -0
- package/dist/endpoints/conversations/storage/dialects/utils.d.ts +25 -0
- package/dist/endpoints/conversations/storage/dialects/utils.js +80 -0
- package/dist/endpoints/conversations/storage/memory.d.ts +25 -0
- package/dist/endpoints/conversations/storage/memory.js +200 -0
- package/dist/endpoints/conversations/storage/sql.d.ts +33 -0
- package/dist/endpoints/conversations/storage/sql.js +276 -0
- package/dist/endpoints/conversations/storage/types.d.ts +39 -0
- package/dist/endpoints/conversations/storage/types.js +0 -0
- package/dist/endpoints/embeddings/converters.d.ts +10 -0
- package/dist/endpoints/embeddings/converters.js +31 -0
- package/dist/endpoints/embeddings/handler.d.ts +2 -0
- package/dist/endpoints/embeddings/handler.js +99 -0
- package/dist/endpoints/embeddings/index.d.ts +4 -0
- package/dist/endpoints/embeddings/index.js +4 -0
- package/dist/endpoints/embeddings/otel.d.ts +5 -0
- package/dist/endpoints/embeddings/otel.js +29 -0
- package/dist/endpoints/embeddings/schema.d.ts +44 -0
- package/dist/endpoints/embeddings/schema.js +29 -0
- package/dist/endpoints/models/converters.d.ts +6 -0
- package/dist/endpoints/models/converters.js +42 -0
- package/dist/endpoints/models/handler.d.ts +2 -0
- package/dist/endpoints/models/handler.js +29 -0
- package/dist/endpoints/models/index.d.ts +3 -0
- package/dist/endpoints/models/index.js +3 -0
- package/dist/endpoints/models/schema.d.ts +42 -0
- package/dist/endpoints/models/schema.js +31 -0
- package/dist/endpoints/responses/converters.d.ts +17 -0
- package/dist/endpoints/responses/converters.js +1037 -0
- package/dist/endpoints/responses/handler.d.ts +2 -0
- package/dist/endpoints/responses/handler.js +141 -0
- package/dist/endpoints/responses/index.d.ts +4 -0
- package/dist/endpoints/responses/index.js +4 -0
- package/dist/endpoints/responses/otel.d.ts +6 -0
- package/dist/endpoints/responses/otel.js +226 -0
- package/dist/endpoints/responses/schema.d.ts +2109 -0
- package/dist/endpoints/responses/schema.js +314 -0
- package/dist/endpoints/shared/converters.d.ts +56 -0
- package/dist/endpoints/shared/converters.js +180 -0
- package/dist/endpoints/shared/schema.d.ts +70 -0
- package/dist/endpoints/shared/schema.js +46 -0
- package/dist/errors/ai-sdk.d.ts +2 -0
- package/dist/errors/ai-sdk.js +52 -0
- package/dist/errors/gateway.d.ts +5 -0
- package/dist/errors/gateway.js +13 -0
- package/dist/errors/openai.d.ts +15 -0
- package/dist/errors/openai.js +40 -0
- package/dist/errors/utils.d.ts +24 -0
- package/dist/errors/utils.js +46 -0
- package/dist/gateway.d.ts +11 -0
- package/dist/gateway.js +44 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/lifecycle.d.ts +3 -0
- package/dist/lifecycle.js +114 -0
- package/dist/logger/default.d.ts +4 -0
- package/dist/logger/default.js +81 -0
- package/dist/logger/index.d.ts +11 -0
- package/dist/logger/index.js +25 -0
- package/dist/middleware/common.d.ts +12 -0
- package/dist/middleware/common.js +146 -0
- package/dist/middleware/debug.d.ts +3 -0
- package/dist/middleware/debug.js +27 -0
- package/dist/middleware/matcher.d.ts +28 -0
- package/dist/middleware/matcher.js +118 -0
- package/dist/middleware/utils.d.ts +2 -0
- package/dist/middleware/utils.js +24 -0
- package/dist/models/amazon/index.d.ts +2 -0
- package/dist/models/amazon/index.js +2 -0
- package/dist/models/amazon/middleware.d.ts +3 -0
- package/dist/models/amazon/middleware.js +69 -0
- package/dist/models/amazon/presets.d.ts +345 -0
- package/dist/models/amazon/presets.js +80 -0
- package/dist/models/anthropic/index.d.ts +2 -0
- package/dist/models/anthropic/index.js +2 -0
- package/dist/models/anthropic/middleware.d.ts +5 -0
- package/dist/models/anthropic/middleware.js +128 -0
- package/dist/models/anthropic/presets.d.ts +711 -0
- package/dist/models/anthropic/presets.js +140 -0
- package/dist/models/catalog.d.ts +4 -0
- package/dist/models/catalog.js +8 -0
- package/dist/models/cohere/index.d.ts +2 -0
- package/dist/models/cohere/index.js +2 -0
- package/dist/models/cohere/middleware.d.ts +3 -0
- package/dist/models/cohere/middleware.js +62 -0
- package/dist/models/cohere/presets.d.ts +411 -0
- package/dist/models/cohere/presets.js +134 -0
- package/dist/models/google/index.d.ts +2 -0
- package/dist/models/google/index.js +2 -0
- package/dist/models/google/middleware.d.ts +8 -0
- package/dist/models/google/middleware.js +118 -0
- package/dist/models/google/presets.d.ts +815 -0
- package/dist/models/google/presets.js +184 -0
- package/dist/models/meta/index.d.ts +1 -0
- package/dist/models/meta/index.js +1 -0
- package/dist/models/meta/presets.d.ts +483 -0
- package/dist/models/meta/presets.js +105 -0
- package/dist/models/openai/index.d.ts +2 -0
- package/dist/models/openai/index.js +2 -0
- package/dist/models/openai/middleware.d.ts +4 -0
- package/dist/models/openai/middleware.js +89 -0
- package/dist/models/openai/presets.d.ts +1319 -0
- package/dist/models/openai/presets.js +277 -0
- package/dist/models/types.d.ts +20 -0
- package/dist/models/types.js +100 -0
- package/dist/models/voyage/index.d.ts +2 -0
- package/dist/models/voyage/index.js +2 -0
- package/dist/models/voyage/middleware.d.ts +2 -0
- package/dist/models/voyage/middleware.js +19 -0
- package/dist/models/voyage/presets.d.ts +436 -0
- package/dist/models/voyage/presets.js +85 -0
- package/dist/providers/anthropic/canonical.d.ts +3 -0
- package/dist/providers/anthropic/canonical.js +9 -0
- package/dist/providers/anthropic/index.d.ts +1 -0
- package/dist/providers/anthropic/index.js +1 -0
- package/dist/providers/bedrock/canonical.d.ts +17 -0
- package/dist/providers/bedrock/canonical.js +64 -0
- package/dist/providers/bedrock/index.d.ts +2 -0
- package/dist/providers/bedrock/index.js +2 -0
- package/dist/providers/bedrock/middleware.d.ts +5 -0
- package/dist/providers/bedrock/middleware.js +133 -0
- package/dist/providers/cohere/canonical.d.ts +3 -0
- package/dist/providers/cohere/canonical.js +17 -0
- package/dist/providers/cohere/index.d.ts +1 -0
- package/dist/providers/cohere/index.js +1 -0
- package/dist/providers/groq/canonical.d.ts +3 -0
- package/dist/providers/groq/canonical.js +12 -0
- package/dist/providers/groq/index.d.ts +2 -0
- package/dist/providers/groq/index.js +2 -0
- package/dist/providers/groq/middleware.d.ts +2 -0
- package/dist/providers/groq/middleware.js +30 -0
- package/dist/providers/openai/canonical.d.ts +3 -0
- package/dist/providers/openai/canonical.js +8 -0
- package/dist/providers/openai/index.d.ts +1 -0
- package/dist/providers/openai/index.js +1 -0
- package/dist/providers/registry.d.ts +24 -0
- package/dist/providers/registry.js +103 -0
- package/dist/providers/types.d.ts +7 -0
- package/dist/providers/types.js +11 -0
- package/dist/providers/vertex/canonical.d.ts +3 -0
- package/dist/providers/vertex/canonical.js +8 -0
- package/dist/providers/vertex/index.d.ts +2 -0
- package/dist/providers/vertex/index.js +2 -0
- package/dist/providers/vertex/middleware.d.ts +2 -0
- package/dist/providers/vertex/middleware.js +47 -0
- package/dist/providers/voyage/canonical.d.ts +3 -0
- package/dist/providers/voyage/canonical.js +7 -0
- package/dist/providers/voyage/index.d.ts +1 -0
- package/dist/providers/voyage/index.js +1 -0
- package/dist/telemetry/ai-sdk.d.ts +2 -0
- package/dist/telemetry/ai-sdk.js +31 -0
- package/dist/telemetry/baggage.d.ts +1 -0
- package/dist/telemetry/baggage.js +24 -0
- package/dist/telemetry/fetch.d.ts +2 -0
- package/dist/telemetry/fetch.js +49 -0
- package/dist/telemetry/gen-ai.d.ts +7 -0
- package/dist/telemetry/gen-ai.js +108 -0
- package/dist/telemetry/http.d.ts +3 -0
- package/dist/telemetry/http.js +54 -0
- package/dist/telemetry/index.d.ts +1 -0
- package/dist/telemetry/index.js +1 -0
- package/dist/telemetry/memory.d.ts +2 -0
- package/dist/telemetry/memory.js +43 -0
- package/dist/telemetry/span.d.ts +13 -0
- package/dist/telemetry/span.js +60 -0
- package/dist/types.d.ts +231 -0
- package/dist/types.js +2 -0
- package/dist/utils/body.d.ts +19 -0
- package/dist/utils/body.js +99 -0
- package/dist/utils/env.d.ts +2 -0
- package/dist/utils/env.js +7 -0
- package/dist/utils/headers.d.ts +4 -0
- package/dist/utils/headers.js +22 -0
- package/dist/utils/preset.d.ts +10 -0
- package/dist/utils/preset.js +41 -0
- package/dist/utils/request.d.ts +2 -0
- package/dist/utils/request.js +43 -0
- package/dist/utils/response.d.ts +6 -0
- package/dist/utils/response.js +55 -0
- package/dist/utils/stream.d.ts +9 -0
- package/dist/utils/stream.js +100 -0
- package/dist/utils/url.d.ts +4 -0
- package/dist/utils/url.js +21 -0
- package/package.json +1 -1
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { InMemoryStorage } from "./endpoints/conversations/storage/memory";
|
|
2
|
+
import { isLogger, logger, setLoggerInstance } from "./logger";
|
|
3
|
+
import { createDefaultLogger } from "./logger/default";
|
|
4
|
+
import { installAiSdkWarningLogger } from "./telemetry/ai-sdk";
|
|
5
|
+
import { DEFAULT_CHAT_TIMEOUT_MS, kParsed, } from "./types";
|
|
6
|
+
import { DEFAULT_MAX_BODY_SIZE } from "./utils/body";
|
|
7
|
+
export const parseConfig = (config) => {
|
|
8
|
+
// If it has been parsed before, just return.
|
|
9
|
+
if (kParsed in config)
|
|
10
|
+
return config;
|
|
11
|
+
const providers = config.providers ?? {};
|
|
12
|
+
const parsedProviders = {};
|
|
13
|
+
const models = config.models ?? {};
|
|
14
|
+
const storage = config.storage ?? new InMemoryStorage();
|
|
15
|
+
// Set the global logger instance.
|
|
16
|
+
if (config.logger === undefined) {
|
|
17
|
+
setLoggerInstance(createDefaultLogger({}));
|
|
18
|
+
}
|
|
19
|
+
else if (config.logger !== null) {
|
|
20
|
+
setLoggerInstance(isLogger(config.logger) ? config.logger : createDefaultLogger(config.logger));
|
|
21
|
+
logger.info(isLogger(config.logger)
|
|
22
|
+
? `[logger] custom logger configured`
|
|
23
|
+
: `[logger] logger configured: level=${config.logger.level}`);
|
|
24
|
+
}
|
|
25
|
+
// Strip providers that are not configured.
|
|
26
|
+
for (const id in providers) {
|
|
27
|
+
const provider = providers[id];
|
|
28
|
+
if (provider === undefined) {
|
|
29
|
+
logger.warn(`[config] ${id} provider removed (undefined)`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
parsedProviders[id] = provider;
|
|
33
|
+
}
|
|
34
|
+
if (Object.keys(parsedProviders).length === 0) {
|
|
35
|
+
throw new Error("No providers configured (config.providers is empty)");
|
|
36
|
+
}
|
|
37
|
+
// Strip providers that are not configured from models.
|
|
38
|
+
const parsedModels = {};
|
|
39
|
+
const warnings = new Set();
|
|
40
|
+
for (const id in models) {
|
|
41
|
+
const model = models[id];
|
|
42
|
+
const kept = [];
|
|
43
|
+
for (const p of model.providers) {
|
|
44
|
+
if (p in parsedProviders)
|
|
45
|
+
kept.push(p);
|
|
46
|
+
else
|
|
47
|
+
warnings.add(p);
|
|
48
|
+
}
|
|
49
|
+
if (kept.length > 0)
|
|
50
|
+
parsedModels[id] = { ...model, providers: kept };
|
|
51
|
+
}
|
|
52
|
+
for (const warning of warnings) {
|
|
53
|
+
logger.warn(`[config] ${warning} provider removed (not configured)`);
|
|
54
|
+
}
|
|
55
|
+
if (Object.keys(parsedModels).length === 0) {
|
|
56
|
+
throw new Error("No models configured (config.models is empty)");
|
|
57
|
+
}
|
|
58
|
+
// Default for the telemetry settings.
|
|
59
|
+
const telemetryEnabled = config.telemetry?.enabled ?? false;
|
|
60
|
+
const telemetrySignals = telemetryEnabled
|
|
61
|
+
? {
|
|
62
|
+
http: config.telemetry?.signals?.http ?? "recommended",
|
|
63
|
+
gen_ai: config.telemetry?.signals?.gen_ai ?? "full",
|
|
64
|
+
hebo: config.telemetry?.signals?.hebo ?? "off",
|
|
65
|
+
}
|
|
66
|
+
: {
|
|
67
|
+
http: "off",
|
|
68
|
+
gen_ai: "off",
|
|
69
|
+
hebo: "off",
|
|
70
|
+
};
|
|
71
|
+
installAiSdkWarningLogger(telemetrySignals.gen_ai);
|
|
72
|
+
// Default timeouts
|
|
73
|
+
let normal;
|
|
74
|
+
let flex;
|
|
75
|
+
const t = config.timeouts;
|
|
76
|
+
if (t === null) {
|
|
77
|
+
normal = flex = undefined;
|
|
78
|
+
}
|
|
79
|
+
else if (typeof t === "number") {
|
|
80
|
+
normal = t;
|
|
81
|
+
flex = t * 3;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
if (t?.normal === null)
|
|
85
|
+
normal = undefined;
|
|
86
|
+
else if (t?.normal === undefined)
|
|
87
|
+
normal = DEFAULT_CHAT_TIMEOUT_MS;
|
|
88
|
+
else
|
|
89
|
+
normal = t.normal;
|
|
90
|
+
if (t?.flex === null)
|
|
91
|
+
flex = undefined;
|
|
92
|
+
else if (t?.flex === undefined)
|
|
93
|
+
flex = normal === undefined ? undefined : normal * 3;
|
|
94
|
+
else
|
|
95
|
+
flex = t.flex;
|
|
96
|
+
}
|
|
97
|
+
const parsedTimeouts = { normal, flex };
|
|
98
|
+
// Body size limit
|
|
99
|
+
const rawMax = config.maxBodySize;
|
|
100
|
+
let maxBodySize;
|
|
101
|
+
if (typeof rawMax === "number" && Number.isFinite(rawMax) && rawMax >= 0) {
|
|
102
|
+
maxBodySize = rawMax;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
maxBodySize = DEFAULT_MAX_BODY_SIZE;
|
|
106
|
+
if (rawMax !== undefined) {
|
|
107
|
+
logger.warn(`[config] invalid maxBodySize (${rawMax}), using default ${DEFAULT_MAX_BODY_SIZE}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Return parsed config.
|
|
111
|
+
return {
|
|
112
|
+
...config,
|
|
113
|
+
timeouts: parsedTimeouts,
|
|
114
|
+
maxBodySize,
|
|
115
|
+
telemetry: {
|
|
116
|
+
...config.telemetry,
|
|
117
|
+
enabled: telemetryEnabled,
|
|
118
|
+
signals: telemetrySignals,
|
|
119
|
+
},
|
|
120
|
+
providers: parsedProviders,
|
|
121
|
+
models: parsedModels,
|
|
122
|
+
storage,
|
|
123
|
+
[kParsed]: true,
|
|
124
|
+
};
|
|
125
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SharedV3ProviderMetadata } from "@ai-sdk/provider";
|
|
2
|
+
import type { GenerateTextResult, StreamTextResult, FinishReason, ToolSet, ModelMessage, UserContent, LanguageModelUsage, TextStreamPart, ReasoningOutput, AssistantModelMessage, ToolModelMessage, UserModelMessage } from "ai";
|
|
3
|
+
import { Output } from "ai";
|
|
4
|
+
import type { SseErrorFrame, SseFrame } from "../../utils/stream";
|
|
5
|
+
import { type TextCallOptions, type ToolChoiceOptions } from "../shared/converters";
|
|
6
|
+
import type { ChatCompletionsToolCall, ChatCompletionsTool, ChatCompletionsToolChoice, ChatCompletionsStream, ChatCompletionsContentPart, ChatCompletionsMessage, ChatCompletionsUserMessage, ChatCompletionsAssistantMessage, ChatCompletionsToolMessage, ChatCompletionsFinishReason, ChatCompletionsUsage, ChatCompletionsInputs, ChatCompletions, ChatCompletionsChunk, ChatCompletionsReasoningDetail } from "./schema";
|
|
7
|
+
export declare function convertToTextCallOptions(params: ChatCompletionsInputs): TextCallOptions;
|
|
8
|
+
export declare function convertToModelMessages(messages: ChatCompletionsMessage[]): ModelMessage[];
|
|
9
|
+
export declare function fromChatCompletionsUserMessage(message: ChatCompletionsUserMessage): UserModelMessage;
|
|
10
|
+
export declare function fromChatCompletionsAssistantMessage(message: ChatCompletionsAssistantMessage): AssistantModelMessage;
|
|
11
|
+
export declare function fromChatCompletionsToolResultMessage(message: ChatCompletionsAssistantMessage, toolById: Map<string, ChatCompletionsToolMessage>): ToolModelMessage | undefined;
|
|
12
|
+
export declare function fromChatCompletionsContent(content: ChatCompletionsContentPart[]): UserContent;
|
|
13
|
+
export declare const convertToToolSet: (tools: ChatCompletionsTool[] | undefined) => ToolSet | undefined;
|
|
14
|
+
export declare const convertToToolChoiceOptions: (toolChoice: ChatCompletionsToolChoice | undefined) => ToolChoiceOptions;
|
|
15
|
+
export declare function toChatCompletions(result: GenerateTextResult<ToolSet, Output.Output>, model: string): ChatCompletions;
|
|
16
|
+
export declare function toChatCompletionsResponse(result: GenerateTextResult<ToolSet, Output.Output>, model: string, responseInit?: ResponseInit): Response;
|
|
17
|
+
export declare function toChatCompletionsStream(result: StreamTextResult<ToolSet, Output.Output>, model: string): ChatCompletionsStream;
|
|
18
|
+
export declare function toChatCompletionsStreamResponse(result: StreamTextResult<ToolSet, Output.Output>, model: string, responseInit?: ResponseInit): Response;
|
|
19
|
+
export declare class ChatCompletionsTransformStream extends TransformStream<TextStreamPart<ToolSet>, SseFrame<ChatCompletionsChunk> | SseErrorFrame> {
|
|
20
|
+
constructor(model: string);
|
|
21
|
+
}
|
|
22
|
+
export declare const toChatCompletionsAssistantMessage: (result: GenerateTextResult<ToolSet, Output.Output>) => ChatCompletionsAssistantMessage;
|
|
23
|
+
export declare function toReasoningDetail(reasoning: ReasoningOutput, id: string, index: number): ChatCompletionsReasoningDetail;
|
|
24
|
+
export declare function toChatCompletionsUsage(usage: LanguageModelUsage): ChatCompletionsUsage;
|
|
25
|
+
export declare function toChatCompletionsToolCall(id: string, name: string, args: unknown, providerMetadata?: SharedV3ProviderMetadata): ChatCompletionsToolCall;
|
|
26
|
+
export declare const toChatCompletionsFinishReason: (finishReason: FinishReason) => ChatCompletionsFinishReason;
|
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { Output, jsonSchema, tool } from "ai";
|
|
2
|
+
import { GatewayError } from "../../errors/gateway";
|
|
3
|
+
import { toResponse } from "../../utils/response";
|
|
4
|
+
import { parseJsonOrText, parseReasoningOptions, parsePromptCachingOptions, resolveResponseServiceTier, normalizeToolName, stripEmptyKeys, parseBase64, parseImageInput, extractReasoningMetadata, } from "../shared/converters";
|
|
5
|
+
// --- Request Flow ---
|
|
6
|
+
export function convertToTextCallOptions(params) {
|
|
7
|
+
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;
|
|
8
|
+
Object.assign(rest, parseReasoningOptions(reasoning_effort, reasoning));
|
|
9
|
+
Object.assign(rest, parsePromptCachingOptions(prompt_cache_key, prompt_cache_retention, cache_control));
|
|
10
|
+
if (extra_body) {
|
|
11
|
+
for (const v of Object.values(extra_body)) {
|
|
12
|
+
Object.assign(rest, v);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const { toolChoice, activeTools } = convertToToolChoiceOptions(tool_choice);
|
|
16
|
+
return {
|
|
17
|
+
messages: convertToModelMessages(messages),
|
|
18
|
+
tools: convertToToolSet(tools),
|
|
19
|
+
toolChoice,
|
|
20
|
+
activeTools,
|
|
21
|
+
output: convertToOutput(response_format),
|
|
22
|
+
temperature,
|
|
23
|
+
maxOutputTokens: max_completion_tokens ?? max_tokens,
|
|
24
|
+
frequencyPenalty: frequency_penalty,
|
|
25
|
+
presencePenalty: presence_penalty,
|
|
26
|
+
seed,
|
|
27
|
+
stopSequences: stop ? (Array.isArray(stop) ? stop : [stop]) : undefined,
|
|
28
|
+
topP: top_p,
|
|
29
|
+
providerOptions: {
|
|
30
|
+
unknown: rest,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function convertToOutput(responseFormat) {
|
|
35
|
+
if (!responseFormat || responseFormat.type === "text") {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const { name, description, schema } = responseFormat.json_schema;
|
|
39
|
+
return Output.object({
|
|
40
|
+
name,
|
|
41
|
+
description,
|
|
42
|
+
schema: jsonSchema(schema),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export function convertToModelMessages(messages) {
|
|
46
|
+
const modelMessages = [];
|
|
47
|
+
const toolById = indexToolMessages(messages);
|
|
48
|
+
for (const message of messages) {
|
|
49
|
+
if (message.role === "tool")
|
|
50
|
+
continue;
|
|
51
|
+
if (message.role === "system") {
|
|
52
|
+
if (message.cache_control) {
|
|
53
|
+
message.providerOptions = {
|
|
54
|
+
unknown: { cache_control: message.cache_control },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
modelMessages.push(message);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (message.role === "user") {
|
|
61
|
+
modelMessages.push(fromChatCompletionsUserMessage(message));
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
modelMessages.push(fromChatCompletionsAssistantMessage(message));
|
|
65
|
+
const toolResult = fromChatCompletionsToolResultMessage(message, toolById);
|
|
66
|
+
if (toolResult)
|
|
67
|
+
modelMessages.push(toolResult);
|
|
68
|
+
}
|
|
69
|
+
return modelMessages;
|
|
70
|
+
}
|
|
71
|
+
function indexToolMessages(messages) {
|
|
72
|
+
const map = new Map();
|
|
73
|
+
for (const m of messages) {
|
|
74
|
+
if (m.role === "tool")
|
|
75
|
+
map.set(m.tool_call_id, m);
|
|
76
|
+
}
|
|
77
|
+
return map;
|
|
78
|
+
}
|
|
79
|
+
export function fromChatCompletionsUserMessage(message) {
|
|
80
|
+
const out = {
|
|
81
|
+
role: "user",
|
|
82
|
+
content: Array.isArray(message.content)
|
|
83
|
+
? fromChatCompletionsContent(message.content)
|
|
84
|
+
: message.content,
|
|
85
|
+
};
|
|
86
|
+
if (message.cache_control) {
|
|
87
|
+
out.providerOptions = {
|
|
88
|
+
unknown: { cache_control: message.cache_control },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
export function fromChatCompletionsAssistantMessage(message) {
|
|
94
|
+
const { tool_calls, role, content, extra_content, reasoning_details, cache_control } = message;
|
|
95
|
+
const parts = [];
|
|
96
|
+
if (reasoning_details?.length) {
|
|
97
|
+
for (const detail of reasoning_details) {
|
|
98
|
+
if (detail.text && detail.type === "reasoning.text") {
|
|
99
|
+
parts.push({
|
|
100
|
+
type: "reasoning",
|
|
101
|
+
text: detail.text,
|
|
102
|
+
providerOptions: detail.signature
|
|
103
|
+
? {
|
|
104
|
+
unknown: {
|
|
105
|
+
signature: detail.signature,
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
: undefined,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else if (detail.type === "reasoning.encrypted" && detail.data) {
|
|
112
|
+
parts.push({
|
|
113
|
+
type: "reasoning",
|
|
114
|
+
text: "",
|
|
115
|
+
providerOptions: {
|
|
116
|
+
unknown: {
|
|
117
|
+
redactedData: detail.data,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (content !== undefined && content !== null) {
|
|
125
|
+
const inputContent = typeof content === "string"
|
|
126
|
+
? [{ type: "text", text: content }]
|
|
127
|
+
: content;
|
|
128
|
+
for (const part of inputContent) {
|
|
129
|
+
if (part.type === "text") {
|
|
130
|
+
const textPart = {
|
|
131
|
+
type: "text",
|
|
132
|
+
text: part.text,
|
|
133
|
+
};
|
|
134
|
+
if (part.cache_control) {
|
|
135
|
+
textPart.providerOptions = {
|
|
136
|
+
unknown: { cache_control: part.cache_control },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
parts.push(textPart);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (tool_calls?.length) {
|
|
144
|
+
for (const tc of tool_calls) {
|
|
145
|
+
// oxlint-disable-next-line no-shadow
|
|
146
|
+
const { id, function: fn, extra_content } = tc;
|
|
147
|
+
const out = {
|
|
148
|
+
type: "tool-call",
|
|
149
|
+
toolCallId: id,
|
|
150
|
+
toolName: fn.name,
|
|
151
|
+
input: parseJsonOrText(fn.arguments).value,
|
|
152
|
+
};
|
|
153
|
+
if (extra_content) {
|
|
154
|
+
out.providerOptions = extra_content;
|
|
155
|
+
}
|
|
156
|
+
parts.push(out);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const out = {
|
|
160
|
+
role,
|
|
161
|
+
content: parts.length > 0 ? parts : (content ?? ""),
|
|
162
|
+
};
|
|
163
|
+
if (extra_content) {
|
|
164
|
+
out.providerOptions = extra_content;
|
|
165
|
+
}
|
|
166
|
+
if (cache_control) {
|
|
167
|
+
(out.providerOptions ??= {})["unknown"] = { cache_control };
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
export function fromChatCompletionsToolResultMessage(message, toolById) {
|
|
172
|
+
const toolCalls = message.tool_calls ?? [];
|
|
173
|
+
if (toolCalls.length === 0)
|
|
174
|
+
return undefined;
|
|
175
|
+
const toolResultParts = [];
|
|
176
|
+
for (const tc of toolCalls) {
|
|
177
|
+
const toolMsg = toolById.get(tc.id);
|
|
178
|
+
if (!toolMsg)
|
|
179
|
+
continue;
|
|
180
|
+
toolResultParts.push({
|
|
181
|
+
type: "tool-result",
|
|
182
|
+
toolCallId: tc.id,
|
|
183
|
+
toolName: tc.function.name,
|
|
184
|
+
output: parseToolResult(toolMsg.content),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return toolResultParts.length > 0 ? { role: "tool", content: toolResultParts } : undefined;
|
|
188
|
+
}
|
|
189
|
+
export function fromChatCompletionsContent(content) {
|
|
190
|
+
return content.map((part) => {
|
|
191
|
+
switch (part.type) {
|
|
192
|
+
case "image_url":
|
|
193
|
+
return fromImageUrlPart(part.image_url.url, part.cache_control);
|
|
194
|
+
case "file":
|
|
195
|
+
return fromFilePart(part.file.data, part.file.media_type, part.file.filename, part.cache_control);
|
|
196
|
+
case "input_audio":
|
|
197
|
+
return fromFilePart(part.input_audio.data, `audio/${part.input_audio.format}`, undefined, part.cache_control);
|
|
198
|
+
case "text": {
|
|
199
|
+
const out = {
|
|
200
|
+
type: "text",
|
|
201
|
+
text: part.text,
|
|
202
|
+
};
|
|
203
|
+
if (part.cache_control) {
|
|
204
|
+
out.providerOptions = {
|
|
205
|
+
unknown: { cache_control: part.cache_control },
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
default:
|
|
211
|
+
throw new GatewayError(`Unsupported content part type: ${part.type}`, 400);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function fromImageUrlPart(url, cacheControl) {
|
|
216
|
+
const { image, mediaType } = parseImageInput(url);
|
|
217
|
+
if (image instanceof URL) {
|
|
218
|
+
const out = {
|
|
219
|
+
type: "image",
|
|
220
|
+
image,
|
|
221
|
+
};
|
|
222
|
+
if (cacheControl) {
|
|
223
|
+
out.providerOptions = {
|
|
224
|
+
unknown: { cache_control: cacheControl },
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
return fromFilePart(image, mediaType ?? "image/jpeg", undefined, cacheControl);
|
|
230
|
+
}
|
|
231
|
+
function fromFilePart(base64Data, mediaType, filename, cacheControl) {
|
|
232
|
+
const data = parseBase64(base64Data);
|
|
233
|
+
if (mediaType.startsWith("image/")) {
|
|
234
|
+
const out = {
|
|
235
|
+
type: "image",
|
|
236
|
+
image: data,
|
|
237
|
+
mediaType,
|
|
238
|
+
};
|
|
239
|
+
if (cacheControl) {
|
|
240
|
+
out.providerOptions = {
|
|
241
|
+
unknown: { cache_control: cacheControl },
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
const out = {
|
|
247
|
+
type: "file",
|
|
248
|
+
data: data,
|
|
249
|
+
filename,
|
|
250
|
+
mediaType,
|
|
251
|
+
};
|
|
252
|
+
if (cacheControl) {
|
|
253
|
+
out.providerOptions = {
|
|
254
|
+
unknown: { cache_control: cacheControl },
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
export const convertToToolSet = (tools) => {
|
|
260
|
+
if (!tools) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
const toolSet = {};
|
|
264
|
+
for (const t of tools) {
|
|
265
|
+
toolSet[t.function.name] = tool({
|
|
266
|
+
description: t.function.description,
|
|
267
|
+
inputSchema: jsonSchema(t.function.parameters),
|
|
268
|
+
strict: t.function.strict,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return toolSet;
|
|
272
|
+
};
|
|
273
|
+
export const convertToToolChoiceOptions = (toolChoice) => {
|
|
274
|
+
if (!toolChoice) {
|
|
275
|
+
return {};
|
|
276
|
+
}
|
|
277
|
+
if (toolChoice === "none" || toolChoice === "auto" || toolChoice === "required") {
|
|
278
|
+
return { toolChoice };
|
|
279
|
+
}
|
|
280
|
+
// FUTURE: this is right now google specific, which is not supported by AI SDK, until then,
|
|
281
|
+
// we temporarily map it to auto for now
|
|
282
|
+
// https://docs.cloud.google.com/vertex-ai/generative-ai/docs/migrate/openai/overview
|
|
283
|
+
if (toolChoice === "validated") {
|
|
284
|
+
return { toolChoice: "auto" };
|
|
285
|
+
}
|
|
286
|
+
if (toolChoice.type === "allowed_tools") {
|
|
287
|
+
return {
|
|
288
|
+
toolChoice: toolChoice.allowed_tools.mode,
|
|
289
|
+
activeTools: toolChoice.allowed_tools.tools.map((toolRef) => toolRef.function.name),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
toolChoice: {
|
|
294
|
+
type: "tool",
|
|
295
|
+
toolName: toolChoice.function.name,
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
function parseToolResult(content) {
|
|
300
|
+
if (Array.isArray(content)) {
|
|
301
|
+
return {
|
|
302
|
+
type: "content",
|
|
303
|
+
value: content.map((part) => ({
|
|
304
|
+
type: "text",
|
|
305
|
+
text: part.text,
|
|
306
|
+
})),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
return parseJsonOrText(content);
|
|
310
|
+
}
|
|
311
|
+
// --- Response Flow ---
|
|
312
|
+
export function toChatCompletions(result, model) {
|
|
313
|
+
return {
|
|
314
|
+
id: "chatcmpl-" + crypto.randomUUID(),
|
|
315
|
+
object: "chat.completion",
|
|
316
|
+
created: Math.floor(Date.now() / 1000),
|
|
317
|
+
model,
|
|
318
|
+
choices: [
|
|
319
|
+
{
|
|
320
|
+
index: 0,
|
|
321
|
+
message: toChatCompletionsAssistantMessage(result),
|
|
322
|
+
finish_reason: toChatCompletionsFinishReason(result.finishReason),
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
usage: result.totalUsage ? toChatCompletionsUsage(result.totalUsage) : null,
|
|
326
|
+
provider_metadata: result.providerMetadata,
|
|
327
|
+
service_tier: resolveResponseServiceTier(result.providerMetadata),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
export function toChatCompletionsResponse(result, model, responseInit) {
|
|
331
|
+
return toResponse(toChatCompletions(result, model), responseInit);
|
|
332
|
+
}
|
|
333
|
+
export function toChatCompletionsStream(result, model) {
|
|
334
|
+
return result.fullStream.pipeThrough(new ChatCompletionsTransformStream(model));
|
|
335
|
+
}
|
|
336
|
+
export function toChatCompletionsStreamResponse(result, model, responseInit) {
|
|
337
|
+
return toResponse(toChatCompletionsStream(result, model), responseInit);
|
|
338
|
+
}
|
|
339
|
+
export class ChatCompletionsTransformStream extends TransformStream {
|
|
340
|
+
constructor(model) {
|
|
341
|
+
const streamId = `chatcmpl-${crypto.randomUUID()}`;
|
|
342
|
+
const creationTime = Math.floor(Date.now() / 1000);
|
|
343
|
+
let toolCallIndexCounter = 0;
|
|
344
|
+
const reasoningIdToIndex = new Map();
|
|
345
|
+
let finishProviderMetadata;
|
|
346
|
+
const createChunk = (delta, provider_metadata, finish_reason, usage) => {
|
|
347
|
+
if (provider_metadata) {
|
|
348
|
+
delta.extra_content = provider_metadata;
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
data: {
|
|
352
|
+
id: streamId,
|
|
353
|
+
object: "chat.completion.chunk",
|
|
354
|
+
created: creationTime,
|
|
355
|
+
model,
|
|
356
|
+
choices: [
|
|
357
|
+
{
|
|
358
|
+
index: 0,
|
|
359
|
+
delta,
|
|
360
|
+
finish_reason: finish_reason ?? null,
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
usage: usage ?? null,
|
|
364
|
+
service_tier: resolveResponseServiceTier(provider_metadata),
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
};
|
|
368
|
+
super({
|
|
369
|
+
transform(part, controller) {
|
|
370
|
+
// Omit lifecycle (start/end) and intermediate events; /chat/completions
|
|
371
|
+
// is a stateless stream of deltas. Tool calls are emitted once fully-formed.
|
|
372
|
+
// oxlint-disable-next-line switch-exhaustiveness-check
|
|
373
|
+
switch (part.type) {
|
|
374
|
+
case "text-delta": {
|
|
375
|
+
controller.enqueue(createChunk({ role: "assistant", content: part.text }, part.providerMetadata));
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
case "reasoning-delta": {
|
|
379
|
+
let index = reasoningIdToIndex.get(part.id);
|
|
380
|
+
if (index === undefined) {
|
|
381
|
+
index = reasoningIdToIndex.size;
|
|
382
|
+
reasoningIdToIndex.set(part.id, index);
|
|
383
|
+
}
|
|
384
|
+
controller.enqueue(createChunk({
|
|
385
|
+
reasoning: part.text,
|
|
386
|
+
reasoning_details: [
|
|
387
|
+
toReasoningDetail({
|
|
388
|
+
type: "reasoning",
|
|
389
|
+
text: part.text,
|
|
390
|
+
providerMetadata: part.providerMetadata,
|
|
391
|
+
}, part.id, index),
|
|
392
|
+
],
|
|
393
|
+
}, part.providerMetadata));
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
case "tool-call": {
|
|
397
|
+
const toolCall = toChatCompletionsToolCall(part.toolCallId, part.toolName, part.input, part.providerMetadata);
|
|
398
|
+
toolCall.index = toolCallIndexCounter++;
|
|
399
|
+
controller.enqueue(createChunk({
|
|
400
|
+
tool_calls: [toolCall],
|
|
401
|
+
}));
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
case "finish-step": {
|
|
405
|
+
finishProviderMetadata = part.providerMetadata;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case "finish": {
|
|
409
|
+
controller.enqueue(createChunk({}, finishProviderMetadata, toChatCompletionsFinishReason(part.finishReason), toChatCompletionsUsage(part.totalUsage)));
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
case "error": {
|
|
413
|
+
controller.enqueue({
|
|
414
|
+
data: part.error instanceof Error ? part.error : new Error(String(part.error)),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
export const toChatCompletionsAssistantMessage = (result) => {
|
|
423
|
+
const message = {
|
|
424
|
+
role: "assistant",
|
|
425
|
+
content: null,
|
|
426
|
+
};
|
|
427
|
+
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
428
|
+
message.tool_calls = result.toolCalls.map((toolCall) => toChatCompletionsToolCall(toolCall.toolCallId, toolCall.toolName, toolCall.input, toolCall.providerMetadata));
|
|
429
|
+
}
|
|
430
|
+
const reasoningDetails = [];
|
|
431
|
+
for (const part of result.content) {
|
|
432
|
+
if (part.type === "text") {
|
|
433
|
+
if (message.content === null) {
|
|
434
|
+
message.content = part.text;
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
message.content += part.text;
|
|
438
|
+
}
|
|
439
|
+
if (part.providerMetadata) {
|
|
440
|
+
message.extra_content = part.providerMetadata;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
else if (part.type === "reasoning") {
|
|
444
|
+
reasoningDetails.push(toReasoningDetail(part, `reasoning-${crypto.randomUUID()}`, reasoningDetails.length));
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (result.reasoningText) {
|
|
448
|
+
message.reasoning = result.reasoningText;
|
|
449
|
+
}
|
|
450
|
+
if (reasoningDetails.length > 0) {
|
|
451
|
+
message.reasoning_details = reasoningDetails;
|
|
452
|
+
}
|
|
453
|
+
if (!message.content && !message.tool_calls) {
|
|
454
|
+
// some models return just reasoning without tool calls or content
|
|
455
|
+
message.content = "";
|
|
456
|
+
}
|
|
457
|
+
return message;
|
|
458
|
+
};
|
|
459
|
+
export function toReasoningDetail(reasoning, id, index) {
|
|
460
|
+
const { redactedData, signature } = extractReasoningMetadata(reasoning.providerMetadata);
|
|
461
|
+
if (redactedData) {
|
|
462
|
+
return {
|
|
463
|
+
id,
|
|
464
|
+
index,
|
|
465
|
+
type: "reasoning.encrypted",
|
|
466
|
+
data: redactedData,
|
|
467
|
+
format: "unknown",
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
return {
|
|
471
|
+
id,
|
|
472
|
+
index,
|
|
473
|
+
type: "reasoning.text",
|
|
474
|
+
text: reasoning.text,
|
|
475
|
+
signature,
|
|
476
|
+
format: "unknown",
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
export function toChatCompletionsUsage(usage) {
|
|
480
|
+
const out = {};
|
|
481
|
+
const prompt = usage.inputTokens;
|
|
482
|
+
if (prompt !== undefined)
|
|
483
|
+
out.prompt_tokens = prompt;
|
|
484
|
+
const completion = usage.outputTokens;
|
|
485
|
+
if (completion !== undefined)
|
|
486
|
+
out.completion_tokens = completion;
|
|
487
|
+
if (prompt !== undefined || completion !== undefined || usage.totalTokens !== undefined) {
|
|
488
|
+
out.total_tokens = usage.totalTokens ?? (prompt ?? 0) + (completion ?? 0);
|
|
489
|
+
}
|
|
490
|
+
const reasoning = usage.outputTokenDetails?.reasoningTokens;
|
|
491
|
+
if (reasoning !== undefined)
|
|
492
|
+
out.completion_tokens_details = { reasoning_tokens: reasoning };
|
|
493
|
+
const cached = usage.inputTokenDetails?.cacheReadTokens;
|
|
494
|
+
const cacheWrite = usage.inputTokenDetails?.cacheWriteTokens;
|
|
495
|
+
if (cached !== undefined || cacheWrite !== undefined) {
|
|
496
|
+
out.prompt_tokens_details = {};
|
|
497
|
+
if (cached !== undefined) {
|
|
498
|
+
out.prompt_tokens_details.cached_tokens = cached;
|
|
499
|
+
}
|
|
500
|
+
if (cacheWrite !== undefined) {
|
|
501
|
+
out.prompt_tokens_details.cache_write_tokens = cacheWrite;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return out;
|
|
505
|
+
}
|
|
506
|
+
export function toChatCompletionsToolCall(id, name, args, providerMetadata) {
|
|
507
|
+
const out = {
|
|
508
|
+
id,
|
|
509
|
+
type: "function",
|
|
510
|
+
function: {
|
|
511
|
+
name: normalizeToolName(name),
|
|
512
|
+
arguments: typeof args === "string" ? args : JSON.stringify(stripEmptyKeys(args)),
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
if (providerMetadata) {
|
|
516
|
+
out.extra_content = providerMetadata;
|
|
517
|
+
}
|
|
518
|
+
return out;
|
|
519
|
+
}
|
|
520
|
+
export const toChatCompletionsFinishReason = (finishReason) => {
|
|
521
|
+
if (finishReason === "error" || finishReason === "other") {
|
|
522
|
+
return "stop";
|
|
523
|
+
}
|
|
524
|
+
return finishReason.replaceAll("-", "_");
|
|
525
|
+
};
|