@hebo-ai/gateway 0.4.2 → 0.5.0
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/README.md +8 -6
- package/dist/endpoints/chat-completions/converters.d.ts +3 -1
- package/dist/endpoints/chat-completions/converters.js +121 -90
- package/dist/endpoints/chat-completions/otel.js +7 -0
- package/dist/endpoints/chat-completions/schema.d.ts +400 -76
- package/dist/endpoints/chat-completions/schema.js +80 -36
- package/dist/endpoints/embeddings/schema.d.ts +1 -1
- package/dist/endpoints/embeddings/schema.js +1 -1
- package/dist/errors/gateway.js +1 -0
- package/dist/logger/default.d.ts +0 -1
- package/dist/logger/default.js +30 -6
- package/dist/middleware/utils.js +1 -0
- package/dist/models/amazon/middleware.js +1 -0
- package/dist/models/anthropic/middleware.d.ts +2 -0
- package/dist/models/anthropic/middleware.js +77 -16
- package/dist/models/google/middleware.js +17 -0
- package/dist/models/google/presets.d.ts +387 -0
- package/dist/models/google/presets.js +9 -2
- package/dist/models/openai/middleware.js +1 -0
- package/dist/models/types.d.ts +1 -1
- package/dist/models/types.js +1 -0
- package/dist/providers/bedrock/index.d.ts +1 -0
- package/dist/providers/bedrock/index.js +1 -0
- package/dist/providers/bedrock/middleware.d.ts +2 -0
- package/dist/providers/bedrock/middleware.js +35 -0
- package/package.json +19 -21
- package/src/endpoints/chat-completions/converters.test.ts +219 -0
- package/src/endpoints/chat-completions/converters.ts +144 -104
- package/src/endpoints/chat-completions/handler.test.ts +87 -0
- package/src/endpoints/chat-completions/otel.ts +6 -0
- package/src/endpoints/chat-completions/schema.ts +85 -43
- package/src/endpoints/embeddings/schema.ts +1 -1
- package/src/errors/gateway.ts +2 -0
- package/src/logger/default.ts +34 -8
- package/src/middleware/utils.ts +1 -0
- package/src/models/amazon/middleware.ts +1 -0
- package/src/models/anthropic/middleware.test.ts +332 -1
- package/src/models/anthropic/middleware.ts +83 -19
- package/src/models/google/middleware.test.ts +31 -0
- package/src/models/google/middleware.ts +18 -0
- package/src/models/google/presets.ts +13 -2
- package/src/models/openai/middleware.ts +1 -0
- package/src/models/types.ts +1 -0
- package/src/providers/bedrock/index.ts +1 -0
- package/src/providers/bedrock/middleware.test.ts +73 -0
- package/src/providers/bedrock/middleware.ts +43 -0
|
@@ -7,7 +7,7 @@ export const ChatCompletionsContentPartImageSchema = z.object({
|
|
|
7
7
|
type: z.literal("image_url"),
|
|
8
8
|
image_url: z.object({
|
|
9
9
|
url: z.string(),
|
|
10
|
-
detail: z.
|
|
10
|
+
detail: z.enum(["low", "high", "auto"]).optional(),
|
|
11
11
|
}),
|
|
12
12
|
});
|
|
13
13
|
export const ChatCompletionsContentPartFileSchema = z.object({
|
|
@@ -18,6 +18,33 @@ export const ChatCompletionsContentPartFileSchema = z.object({
|
|
|
18
18
|
filename: z.string().optional(),
|
|
19
19
|
}),
|
|
20
20
|
});
|
|
21
|
+
export const ChatCompletionsContentPartAudioSchema = z.object({
|
|
22
|
+
type: z.literal("input_audio"),
|
|
23
|
+
input_audio: z.object({
|
|
24
|
+
data: z.string(),
|
|
25
|
+
// only wav and mp3 are official by OpenAI, rest is taken from Gemini support:
|
|
26
|
+
// https://docs.cloud.google.com/vertex-ai/generative-ai/docs/multimodal/audio-understanding
|
|
27
|
+
format: z.enum([
|
|
28
|
+
"x-aac",
|
|
29
|
+
"flac",
|
|
30
|
+
"mp3",
|
|
31
|
+
"m4a",
|
|
32
|
+
"mpeg",
|
|
33
|
+
"mpga",
|
|
34
|
+
"mp4",
|
|
35
|
+
"ogg",
|
|
36
|
+
"pcm",
|
|
37
|
+
"wav",
|
|
38
|
+
"webm",
|
|
39
|
+
]),
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
export const ChatCompletionsContentPartSchema = z.discriminatedUnion("type", [
|
|
43
|
+
ChatCompletionsContentPartTextSchema,
|
|
44
|
+
ChatCompletionsContentPartImageSchema,
|
|
45
|
+
ChatCompletionsContentPartFileSchema,
|
|
46
|
+
ChatCompletionsContentPartAudioSchema,
|
|
47
|
+
]);
|
|
21
48
|
export const ChatCompletionsToolCallSchema = z.object({
|
|
22
49
|
type: z.literal("function"),
|
|
23
50
|
id: z.string(),
|
|
@@ -25,7 +52,10 @@ export const ChatCompletionsToolCallSchema = z.object({
|
|
|
25
52
|
arguments: z.string(),
|
|
26
53
|
name: z.string(),
|
|
27
54
|
}),
|
|
28
|
-
extra_content: z
|
|
55
|
+
extra_content: z
|
|
56
|
+
.record(z.string(), z.record(z.string(), z.unknown()))
|
|
57
|
+
.optional()
|
|
58
|
+
.meta({ extension: true }),
|
|
29
59
|
});
|
|
30
60
|
export const ChatCompletionsSystemMessageSchema = z.object({
|
|
31
61
|
role: z.literal("system"),
|
|
@@ -34,14 +64,7 @@ export const ChatCompletionsSystemMessageSchema = z.object({
|
|
|
34
64
|
});
|
|
35
65
|
export const ChatCompletionsUserMessageSchema = z.object({
|
|
36
66
|
role: z.literal("user"),
|
|
37
|
-
content: z.union([
|
|
38
|
-
z.string(),
|
|
39
|
-
z.array(z.union([
|
|
40
|
-
ChatCompletionsContentPartTextSchema,
|
|
41
|
-
ChatCompletionsContentPartImageSchema,
|
|
42
|
-
ChatCompletionsContentPartFileSchema,
|
|
43
|
-
])),
|
|
44
|
-
]),
|
|
67
|
+
content: z.union([z.string(), z.array(ChatCompletionsContentPartSchema)]),
|
|
45
68
|
name: z.string().optional(),
|
|
46
69
|
});
|
|
47
70
|
export const ChatCompletionsReasoningDetailSchema = z.object({
|
|
@@ -56,8 +79,9 @@ export const ChatCompletionsReasoningDetailSchema = z.object({
|
|
|
56
79
|
});
|
|
57
80
|
export const ChatCompletionsAssistantMessageSchema = z.object({
|
|
58
81
|
role: z.literal("assistant"),
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
content: z
|
|
83
|
+
.union([z.string(), z.null(), z.array(ChatCompletionsContentPartTextSchema)])
|
|
84
|
+
.optional(),
|
|
61
85
|
name: z.string().optional(),
|
|
62
86
|
// FUTURE: This should also support Custom Tool Calls
|
|
63
87
|
tool_calls: z.array(ChatCompletionsToolCallSchema).optional(),
|
|
@@ -67,15 +91,17 @@ export const ChatCompletionsAssistantMessageSchema = z.object({
|
|
|
67
91
|
.array(ChatCompletionsReasoningDetailSchema)
|
|
68
92
|
.optional()
|
|
69
93
|
.meta({ extension: true }),
|
|
70
|
-
extra_content: z
|
|
94
|
+
extra_content: z
|
|
95
|
+
.record(z.string(), z.record(z.string(), z.unknown()))
|
|
96
|
+
.optional()
|
|
97
|
+
.meta({ extension: true }),
|
|
71
98
|
});
|
|
72
99
|
export const ChatCompletionsToolMessageSchema = z.object({
|
|
73
100
|
role: z.literal("tool"),
|
|
74
|
-
|
|
75
|
-
content: z.string(),
|
|
101
|
+
content: z.union([z.string(), z.array(ChatCompletionsContentPartTextSchema)]),
|
|
76
102
|
tool_call_id: z.string(),
|
|
77
103
|
});
|
|
78
|
-
export const ChatCompletionsMessageSchema = z.
|
|
104
|
+
export const ChatCompletionsMessageSchema = z.discriminatedUnion("role", [
|
|
79
105
|
ChatCompletionsSystemMessageSchema,
|
|
80
106
|
ChatCompletionsUserMessageSchema,
|
|
81
107
|
ChatCompletionsAssistantMessageSchema,
|
|
@@ -86,14 +112,12 @@ export const ChatCompletionsToolSchema = z.object({
|
|
|
86
112
|
function: z.object({
|
|
87
113
|
name: z.string(),
|
|
88
114
|
description: z.string().optional(),
|
|
89
|
-
parameters: z.record(z.string(), z.
|
|
115
|
+
parameters: z.record(z.string(), z.unknown()),
|
|
90
116
|
// Missing strict parameter
|
|
91
117
|
}),
|
|
92
118
|
});
|
|
93
119
|
export const ChatCompletionsToolChoiceSchema = z.union([
|
|
94
|
-
z.
|
|
95
|
-
z.literal("auto"),
|
|
96
|
-
z.literal("required"),
|
|
120
|
+
z.enum(["none", "auto", "required", "validated"]),
|
|
97
121
|
// FUTURE: missing AllowedTools and CustomToolChoice
|
|
98
122
|
z.object({
|
|
99
123
|
type: z.literal("function"),
|
|
@@ -102,13 +126,14 @@ export const ChatCompletionsToolChoiceSchema = z.union([
|
|
|
102
126
|
}),
|
|
103
127
|
}),
|
|
104
128
|
]);
|
|
105
|
-
export const ChatCompletionsReasoningEffortSchema = z.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
129
|
+
export const ChatCompletionsReasoningEffortSchema = z.enum([
|
|
130
|
+
"none",
|
|
131
|
+
"minimal",
|
|
132
|
+
"low",
|
|
133
|
+
"medium",
|
|
134
|
+
"high",
|
|
135
|
+
"xhigh",
|
|
136
|
+
"max",
|
|
112
137
|
]);
|
|
113
138
|
export const ChatCompletionsReasoningConfigSchema = z.object({
|
|
114
139
|
enabled: z.optional(z.boolean()),
|
|
@@ -116,6 +141,24 @@ export const ChatCompletionsReasoningConfigSchema = z.object({
|
|
|
116
141
|
max_tokens: z.optional(z.number()),
|
|
117
142
|
exclude: z.optional(z.boolean()),
|
|
118
143
|
});
|
|
144
|
+
export const ChatCompletionsResponseFormatJsonSchema = z.object({
|
|
145
|
+
// FUTURE: consider support for legacy json_object (if demand)
|
|
146
|
+
type: z.literal("json_schema"),
|
|
147
|
+
json_schema: z.object({
|
|
148
|
+
name: z.string(),
|
|
149
|
+
description: z.string().optional(),
|
|
150
|
+
schema: z.record(z.string(), z.unknown()),
|
|
151
|
+
// FUTURE: consider support for non-strict mode (for providers that support it)
|
|
152
|
+
strict: z.boolean().optional(),
|
|
153
|
+
}),
|
|
154
|
+
});
|
|
155
|
+
export const ChatCompletionsResponseFormatTextSchema = z.object({
|
|
156
|
+
type: z.literal("text"),
|
|
157
|
+
});
|
|
158
|
+
export const ChatCompletionsResponseFormatSchema = z.discriminatedUnion("type", [
|
|
159
|
+
ChatCompletionsResponseFormatJsonSchema,
|
|
160
|
+
ChatCompletionsResponseFormatTextSchema,
|
|
161
|
+
]);
|
|
119
162
|
const ChatCompletionsInputsSchema = z.object({
|
|
120
163
|
messages: z.array(ChatCompletionsMessageSchema),
|
|
121
164
|
tools: z
|
|
@@ -132,6 +175,7 @@ const ChatCompletionsInputsSchema = z.object({
|
|
|
132
175
|
seed: z.int().optional(),
|
|
133
176
|
stop: z.union([z.string(), z.array(z.string())]).optional(),
|
|
134
177
|
top_p: z.number().min(0).max(1.0).optional(),
|
|
178
|
+
response_format: ChatCompletionsResponseFormatSchema.optional(),
|
|
135
179
|
reasoning_effort: ChatCompletionsReasoningEffortSchema.optional(),
|
|
136
180
|
// Extensions
|
|
137
181
|
reasoning: ChatCompletionsReasoningConfigSchema.optional().meta({ extension: true }),
|
|
@@ -141,18 +185,18 @@ export const ChatCompletionsBodySchema = z.looseObject({
|
|
|
141
185
|
stream: z.boolean().optional(),
|
|
142
186
|
...ChatCompletionsInputsSchema.shape,
|
|
143
187
|
});
|
|
144
|
-
export const ChatCompletionsFinishReasonSchema = z.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
188
|
+
export const ChatCompletionsFinishReasonSchema = z.enum([
|
|
189
|
+
"stop",
|
|
190
|
+
"length",
|
|
191
|
+
"content_filter",
|
|
192
|
+
"tool_calls",
|
|
149
193
|
]);
|
|
150
194
|
export const ChatCompletionsChoiceSchema = z.object({
|
|
151
195
|
index: z.int().nonnegative(),
|
|
152
196
|
message: ChatCompletionsAssistantMessageSchema,
|
|
153
197
|
finish_reason: ChatCompletionsFinishReasonSchema,
|
|
154
198
|
// FUTURE: model this out
|
|
155
|
-
logprobs: z.
|
|
199
|
+
logprobs: z.unknown().optional(),
|
|
156
200
|
});
|
|
157
201
|
export const ChatCompletionsUsageSchema = z.object({
|
|
158
202
|
prompt_tokens: z.int().nonnegative().optional(),
|
|
@@ -179,7 +223,7 @@ export const ChatCompletionsSchema = z.object({
|
|
|
179
223
|
choices: z.array(ChatCompletionsChoiceSchema),
|
|
180
224
|
usage: ChatCompletionsUsageSchema.nullable(),
|
|
181
225
|
// Extensions
|
|
182
|
-
provider_metadata: z.
|
|
226
|
+
provider_metadata: z.unknown().optional().meta({ extension: true }),
|
|
183
227
|
});
|
|
184
228
|
export const ChatCompletionsToolCallDeltaSchema = ChatCompletionsToolCallSchema.partial().extend({
|
|
185
229
|
index: z.int().nonnegative(),
|
|
@@ -192,7 +236,7 @@ export const ChatCompletionsChoiceDeltaSchema = z.object({
|
|
|
192
236
|
delta: ChatCompletionsAssistantMessageDeltaSchema,
|
|
193
237
|
finish_reason: ChatCompletionsFinishReasonSchema.nullable(),
|
|
194
238
|
// FUTURE: model this out
|
|
195
|
-
logprobs: z.
|
|
239
|
+
logprobs: z.unknown().optional(),
|
|
196
240
|
});
|
|
197
241
|
export const ChatCompletionsChunkSchema = z.object({
|
|
198
242
|
id: z.string(),
|
|
@@ -202,5 +246,5 @@ export const ChatCompletionsChunkSchema = z.object({
|
|
|
202
246
|
choices: z.array(ChatCompletionsChoiceDeltaSchema),
|
|
203
247
|
usage: ChatCompletionsUsageSchema.nullable(),
|
|
204
248
|
// Extensions
|
|
205
|
-
provider_metadata: z.
|
|
249
|
+
provider_metadata: z.unknown().optional().meta({ extension: true }),
|
|
206
250
|
});
|
|
@@ -33,6 +33,6 @@ export declare const EmbeddingsSchema: z.ZodObject<{
|
|
|
33
33
|
prompt_tokens: z.ZodOptional<z.ZodInt>;
|
|
34
34
|
total_tokens: z.ZodOptional<z.ZodInt>;
|
|
35
35
|
}, z.core.$strip>>;
|
|
36
|
-
provider_metadata: z.ZodOptional<z.
|
|
36
|
+
provider_metadata: z.ZodOptional<z.ZodUnknown>;
|
|
37
37
|
}, z.core.$strip>;
|
|
38
38
|
export type Embeddings = z.infer<typeof EmbeddingsSchema>;
|
|
@@ -22,5 +22,5 @@ export const EmbeddingsSchema = z.object({
|
|
|
22
22
|
model: z.string(),
|
|
23
23
|
usage: EmbeddingsUsageSchema.nullable(),
|
|
24
24
|
// Extensions
|
|
25
|
-
provider_metadata: z.
|
|
25
|
+
provider_metadata: z.unknown().optional().meta({ extension: true }),
|
|
26
26
|
});
|
package/dist/errors/gateway.js
CHANGED
|
@@ -5,6 +5,7 @@ export class GatewayError extends Error {
|
|
|
5
5
|
constructor(error, status, code, cause) {
|
|
6
6
|
const isError = error instanceof Error;
|
|
7
7
|
super(isError ? error.message : String(error));
|
|
8
|
+
this.name = "GatewayError";
|
|
8
9
|
this.cause = cause ?? (isError ? error : undefined);
|
|
9
10
|
this.status = status;
|
|
10
11
|
this.code = code ?? STATUS_CODE(status);
|
package/dist/logger/default.d.ts
CHANGED
package/dist/logger/default.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { serializeError } from "serialize-error";
|
|
2
1
|
import { isProduction, isTest } from "../utils/env";
|
|
3
|
-
|
|
2
|
+
const getDefaultLogLevel = () => isTest() ? "silent" : isProduction() ? "info" : "debug";
|
|
4
3
|
const noop = () => { };
|
|
5
4
|
const LEVEL = {
|
|
6
5
|
trace: 5,
|
|
@@ -12,6 +11,31 @@ const LEVEL = {
|
|
|
12
11
|
};
|
|
13
12
|
const LEVELS = Object.keys(LEVEL);
|
|
14
13
|
const isRecord = (value) => typeof value === "object" && value !== null && !(value instanceof Error);
|
|
14
|
+
function serializeError(err, _seen) {
|
|
15
|
+
if (!(err instanceof Error))
|
|
16
|
+
return { message: String(err) };
|
|
17
|
+
const seen = _seen ?? new WeakSet();
|
|
18
|
+
if (seen.has(err))
|
|
19
|
+
return { name: err.name, message: err.message, circular: true };
|
|
20
|
+
seen.add(err);
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const k of Object.getOwnPropertyNames(err)) {
|
|
23
|
+
if (k.startsWith("_"))
|
|
24
|
+
continue;
|
|
25
|
+
let val;
|
|
26
|
+
try {
|
|
27
|
+
val = err[k];
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
val = "[Unreadable]";
|
|
31
|
+
}
|
|
32
|
+
if (typeof val === "bigint")
|
|
33
|
+
val = `${val}n`;
|
|
34
|
+
// FUTURE: check for circular references within val
|
|
35
|
+
out[String(k)] = val instanceof Error ? serializeError(val, seen) : val;
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
15
39
|
const buildLogObject = (level, args) => {
|
|
16
40
|
if (args.length === 0)
|
|
17
41
|
return {};
|
|
@@ -20,11 +44,11 @@ const buildLogObject = (level, args) => {
|
|
|
20
44
|
let err;
|
|
21
45
|
let msg;
|
|
22
46
|
if (first instanceof Error) {
|
|
23
|
-
err = first;
|
|
47
|
+
err = serializeError(first);
|
|
24
48
|
}
|
|
25
49
|
else if (isRecord(first)) {
|
|
26
50
|
if (first["err"] !== undefined) {
|
|
27
|
-
err = first["err"];
|
|
51
|
+
err = serializeError(first["err"]);
|
|
28
52
|
delete first["err"];
|
|
29
53
|
}
|
|
30
54
|
obj = first;
|
|
@@ -36,13 +60,13 @@ const buildLogObject = (level, args) => {
|
|
|
36
60
|
msg = String(second);
|
|
37
61
|
}
|
|
38
62
|
if (err && msg === undefined) {
|
|
39
|
-
msg = err
|
|
63
|
+
msg = err["message"];
|
|
40
64
|
}
|
|
41
65
|
return {
|
|
42
66
|
level,
|
|
43
67
|
time: Date.now(),
|
|
44
68
|
...(msg ? { msg } : {}),
|
|
45
|
-
...(err ? { err
|
|
69
|
+
...(err ? { err } : {}),
|
|
46
70
|
...obj,
|
|
47
71
|
};
|
|
48
72
|
};
|
package/dist/middleware/utils.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { LanguageModelMiddleware } from "ai";
|
|
2
|
+
import type { ChatCompletionsReasoningEffort } from "../../endpoints/chat-completions/schema";
|
|
3
|
+
export declare function mapClaudeReasoningEffort(effort: ChatCompletionsReasoningEffort, modelId: string): "low" | "high" | "medium" | "max";
|
|
2
4
|
export declare const claudeReasoningMiddleware: LanguageModelMiddleware;
|
|
@@ -1,15 +1,54 @@
|
|
|
1
1
|
import { modelMiddlewareMatcher } from "../../middleware/matcher";
|
|
2
2
|
import { calculateReasoningBudgetFromEffort } from "../../middleware/utils";
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
const isClaude = (family, version) => {
|
|
4
|
+
const dashed = version.replace(".", "-");
|
|
5
|
+
return (modelId) => modelId.includes(`claude-${family}-${version}`) ||
|
|
6
|
+
modelId.includes(`claude-${family}-${dashed}`);
|
|
7
|
+
};
|
|
8
|
+
const isOpus46 = isClaude("opus", "4.6");
|
|
9
|
+
const isOpus45 = isClaude("opus", "4.5");
|
|
10
|
+
const isOpus4 = isClaude("opus", "4");
|
|
11
|
+
const isSonnet46 = isClaude("sonnet", "4.6");
|
|
12
|
+
const isSonnet45 = isClaude("sonnet", "4.5");
|
|
13
|
+
export function mapClaudeReasoningEffort(effort, modelId) {
|
|
14
|
+
if (isOpus46(modelId)) {
|
|
15
|
+
switch (effort) {
|
|
16
|
+
case "none":
|
|
17
|
+
case "minimal":
|
|
18
|
+
case "low":
|
|
19
|
+
return "low";
|
|
20
|
+
case "medium":
|
|
21
|
+
return "medium";
|
|
22
|
+
case "high":
|
|
23
|
+
return "high";
|
|
24
|
+
case "xhigh":
|
|
25
|
+
case "max":
|
|
26
|
+
return "max";
|
|
27
|
+
}
|
|
10
28
|
}
|
|
11
|
-
|
|
29
|
+
switch (effort) {
|
|
30
|
+
case "none":
|
|
31
|
+
case "minimal":
|
|
32
|
+
case "low":
|
|
33
|
+
return "low";
|
|
34
|
+
case "medium":
|
|
35
|
+
return "medium";
|
|
36
|
+
case "high":
|
|
37
|
+
case "xhigh":
|
|
38
|
+
case "max":
|
|
39
|
+
return "high";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function getMaxOutputTokens(modelId) {
|
|
43
|
+
if (isOpus46(modelId))
|
|
44
|
+
return 128_000;
|
|
45
|
+
if (isOpus45(modelId))
|
|
46
|
+
return 64_000;
|
|
47
|
+
if (isOpus4(modelId))
|
|
48
|
+
return 32_000;
|
|
49
|
+
return 64_000;
|
|
12
50
|
}
|
|
51
|
+
// https://platform.claude.com/docs/en/build-with-claude/effort
|
|
13
52
|
export const claudeReasoningMiddleware = {
|
|
14
53
|
specificationVersion: "v3",
|
|
15
54
|
// eslint-disable-next-line require-await
|
|
@@ -21,20 +60,42 @@ export const claudeReasoningMiddleware = {
|
|
|
21
60
|
if (!reasoning)
|
|
22
61
|
return params;
|
|
23
62
|
const target = (params.providerOptions["anthropic"] ??= {});
|
|
63
|
+
const modelId = model.modelId;
|
|
64
|
+
const clampedMaxTokens = reasoning.max_tokens && Math.min(reasoning.max_tokens, getMaxOutputTokens(modelId));
|
|
24
65
|
if (!reasoning.enabled) {
|
|
25
66
|
target["thinking"] = { type: "disabled" };
|
|
26
67
|
}
|
|
27
|
-
else if (reasoning.max_tokens) {
|
|
28
|
-
target["thinking"] = {
|
|
29
|
-
type: "enabled",
|
|
30
|
-
budgetTokens: Math.min(reasoning.max_tokens, getMaxOutputTokens(model.modelId)),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
68
|
else if (reasoning.effort) {
|
|
34
|
-
|
|
69
|
+
if (isOpus46(modelId)) {
|
|
70
|
+
target["thinking"] = clampedMaxTokens
|
|
71
|
+
? { type: "adaptive", budgetTokens: clampedMaxTokens }
|
|
72
|
+
: { type: "adaptive" };
|
|
73
|
+
target["effort"] = mapClaudeReasoningEffort(reasoning.effort, modelId);
|
|
74
|
+
}
|
|
75
|
+
else if (isSonnet46(modelId)) {
|
|
76
|
+
target["thinking"] = clampedMaxTokens
|
|
77
|
+
? { type: "enabled", budgetTokens: clampedMaxTokens }
|
|
78
|
+
: { type: "adaptive" };
|
|
79
|
+
target["effort"] = mapClaudeReasoningEffort(reasoning.effort, modelId);
|
|
80
|
+
}
|
|
81
|
+
else if (isOpus45(modelId) || isSonnet45(modelId)) {
|
|
82
|
+
target["thinking"] = { type: "enabled" };
|
|
83
|
+
if (clampedMaxTokens)
|
|
84
|
+
target["thinking"]["budgetTokens"] = clampedMaxTokens;
|
|
85
|
+
target["effort"] = mapClaudeReasoningEffort(reasoning.effort, modelId);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// FUTURE: warn that reasoning.max_tokens was computed
|
|
89
|
+
target["thinking"] = {
|
|
90
|
+
type: "enabled",
|
|
91
|
+
budgetTokens: calculateReasoningBudgetFromEffort(reasoning.effort, params.maxOutputTokens ?? getMaxOutputTokens(modelId), 1024),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else if (clampedMaxTokens) {
|
|
35
96
|
target["thinking"] = {
|
|
36
97
|
type: "enabled",
|
|
37
|
-
budgetTokens:
|
|
98
|
+
budgetTokens: clampedMaxTokens,
|
|
38
99
|
};
|
|
39
100
|
}
|
|
40
101
|
else {
|
|
@@ -16,7 +16,22 @@ export const geminiDimensionsMiddleware = {
|
|
|
16
16
|
return params;
|
|
17
17
|
},
|
|
18
18
|
};
|
|
19
|
+
// https://ai.google.dev/gemini-api/docs/thinking#thinking-levels
|
|
19
20
|
export function mapGeminiReasoningEffort(effort, modelId) {
|
|
21
|
+
if (modelId.includes("gemini-3.1-pro")) {
|
|
22
|
+
switch (effort) {
|
|
23
|
+
case "none":
|
|
24
|
+
case "minimal":
|
|
25
|
+
case "low":
|
|
26
|
+
return "low";
|
|
27
|
+
case "medium":
|
|
28
|
+
return "medium";
|
|
29
|
+
case "high":
|
|
30
|
+
case "xhigh":
|
|
31
|
+
case "max":
|
|
32
|
+
return "high";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
20
35
|
if (modelId.includes("gemini-3-pro")) {
|
|
21
36
|
switch (effort) {
|
|
22
37
|
case "none":
|
|
@@ -26,6 +41,7 @@ export function mapGeminiReasoningEffort(effort, modelId) {
|
|
|
26
41
|
case "medium":
|
|
27
42
|
case "high":
|
|
28
43
|
case "xhigh":
|
|
44
|
+
case "max":
|
|
29
45
|
return "high";
|
|
30
46
|
}
|
|
31
47
|
}
|
|
@@ -40,6 +56,7 @@ export function mapGeminiReasoningEffort(effort, modelId) {
|
|
|
40
56
|
return "medium";
|
|
41
57
|
case "high":
|
|
42
58
|
case "xhigh":
|
|
59
|
+
case "max":
|
|
43
60
|
return "high";
|
|
44
61
|
}
|
|
45
62
|
}
|