babelgate 0.0.0 → 1.1.1
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/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +467 -1
- package/dist/app.js +40 -0
- package/dist/cli.js +203 -0
- package/dist/config/env-config.js +99 -0
- package/dist/index.js +60 -0
- package/dist/middleware/error-handler.js +158 -0
- package/dist/middleware/request-logger.js +66 -0
- package/dist/middleware/require-api-key.js +80 -0
- package/dist/middleware/resolve-source.js +28 -0
- package/dist/routes/proxy.js +74 -0
- package/dist/services/http-client.js +220 -0
- package/dist/services/proxy.service.js +329 -0
- package/dist/services/stream-pipeline.js +119 -0
- package/dist/services/upstream-errors.js +34 -0
- package/dist/transformers/adapters/anthropic.adapter.js +292 -0
- package/dist/transformers/adapters/anthropic.messages.js +261 -0
- package/dist/transformers/adapters/anthropic.stream.js +378 -0
- package/dist/transformers/adapters/gemini.adapter.js +418 -0
- package/dist/transformers/adapters/gemini.schema.js +61 -0
- package/dist/transformers/adapters/gemini.stream.js +341 -0
- package/dist/transformers/adapters/gemini.tools.js +246 -0
- package/dist/transformers/adapters/index.js +141 -0
- package/dist/transformers/adapters/ollama.adapter.js +288 -0
- package/dist/transformers/adapters/ollama.stream.js +294 -0
- package/dist/transformers/adapters/ollama.tools.js +107 -0
- package/dist/transformers/adapters/openai.adapter.js +460 -0
- package/dist/transformers/adapters/openai.stream.js +229 -0
- package/dist/transformers/index.js +110 -0
- package/dist/types/anthropic.types.js +2 -0
- package/dist/types/canonical.schema.js +160 -0
- package/dist/types/canonical.types.js +8 -0
- package/dist/types/gemini.types.js +2 -0
- package/dist/types/ollama.types.js +4 -0
- package/dist/types/openai.types.js +2 -0
- package/dist/utils/canonical-error.js +77 -0
- package/dist/utils/errors.js +95 -0
- package/dist/utils/ids.js +17 -0
- package/dist/utils/logger.js +34 -0
- package/dist/utils/model-map.js +39 -0
- package/dist/utils/sse.js +147 -0
- package/dist/utils/stream-reassemble.js +104 -0
- package/dist/utils/token-estimate.js +36 -0
- package/dist/utils/tool-args.js +26 -0
- package/package.json +55 -7
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.geminiAdapter = void 0;
|
|
4
|
+
exports.toCanonicalRequest = toCanonicalRequest;
|
|
5
|
+
exports.fromCanonicalRequest = fromCanonicalRequest;
|
|
6
|
+
exports.toCanonicalResponse = toCanonicalResponse;
|
|
7
|
+
exports.mapFinishReason = mapFinishReason;
|
|
8
|
+
exports.fromCanonicalResponse = fromCanonicalResponse;
|
|
9
|
+
exports.toCanonicalModelList = toCanonicalModelList;
|
|
10
|
+
exports.fromCanonicalModelList = fromCanonicalModelList;
|
|
11
|
+
exports.mapToGeminiFinishReason = mapToGeminiFinishReason;
|
|
12
|
+
exports.toCanonicalError = toCanonicalError;
|
|
13
|
+
exports.fromCanonicalError = fromCanonicalError;
|
|
14
|
+
const crypto_1 = require("crypto");
|
|
15
|
+
const canonical_error_1 = require("../../utils/canonical-error");
|
|
16
|
+
const errors_1 = require("../../utils/errors");
|
|
17
|
+
const ids_1 = require("../../utils/ids");
|
|
18
|
+
const gemini_stream_1 = require("./gemini.stream");
|
|
19
|
+
const gemini_tools_1 = require("./gemini.tools");
|
|
20
|
+
/**
|
|
21
|
+
* Gemini FinishReason values — duplicated locally so the SDK stays a
|
|
22
|
+
* type-only dependency. These are stable API constants (string enum
|
|
23
|
+
* values), not implementation details.
|
|
24
|
+
*/
|
|
25
|
+
const FinishReason = {
|
|
26
|
+
STOP: "STOP",
|
|
27
|
+
MAX_TOKENS: "MAX_TOKENS",
|
|
28
|
+
SAFETY: "SAFETY",
|
|
29
|
+
RECITATION: "RECITATION",
|
|
30
|
+
BLOCKLIST: "BLOCKLIST",
|
|
31
|
+
PROHIBITED_CONTENT: "PROHIBITED_CONTENT",
|
|
32
|
+
SPII: "SPII",
|
|
33
|
+
IMAGE_SAFETY: "IMAGE_SAFETY",
|
|
34
|
+
};
|
|
35
|
+
exports.geminiAdapter = {
|
|
36
|
+
toCanonicalRequest,
|
|
37
|
+
fromCanonicalRequest,
|
|
38
|
+
toCanonicalResponse,
|
|
39
|
+
fromCanonicalResponse,
|
|
40
|
+
toCanonicalModelList,
|
|
41
|
+
fromCanonicalModelList,
|
|
42
|
+
toCanonicalError,
|
|
43
|
+
fromCanonicalError,
|
|
44
|
+
createStreamDecoder: gemini_stream_1.createStreamDecoder,
|
|
45
|
+
createStreamEncoder: gemini_stream_1.createStreamEncoder,
|
|
46
|
+
};
|
|
47
|
+
// ============ REQUEST: Gemini → Canonical ============
|
|
48
|
+
function toCanonicalRequest(request) {
|
|
49
|
+
const messages = [];
|
|
50
|
+
let systemPrompt;
|
|
51
|
+
const ids = (0, ids_1.createIdGenerator)();
|
|
52
|
+
// pending call ids per function name, for matching functionResponses
|
|
53
|
+
const pendingCalls = new Map();
|
|
54
|
+
// Extract system instruction
|
|
55
|
+
if (request.systemInstruction) {
|
|
56
|
+
systemPrompt = extractTextFromInstruction(request.systemInstruction);
|
|
57
|
+
}
|
|
58
|
+
// Convert contents to messages
|
|
59
|
+
for (const content of request.contents) {
|
|
60
|
+
const parts = content.parts ?? [];
|
|
61
|
+
const otherParts = parts.filter((p) => !(0, gemini_tools_1.isFunctionResponsePart)(p));
|
|
62
|
+
if (content.role === "model") {
|
|
63
|
+
const toolCalls = (0, gemini_tools_1.toCanonicalToolCalls)(otherParts, ids);
|
|
64
|
+
for (const call of toolCalls) {
|
|
65
|
+
(0, gemini_tools_1.recordCallId)(pendingCalls, call.function.name, call.id);
|
|
66
|
+
}
|
|
67
|
+
messages.push({
|
|
68
|
+
role: "assistant",
|
|
69
|
+
content: extractContentParts({
|
|
70
|
+
role: content.role,
|
|
71
|
+
parts: otherParts.filter((p) => !(0, gemini_tools_1.isFunctionCallPart)(p)),
|
|
72
|
+
}),
|
|
73
|
+
...(toolCalls.length > 0 && { toolCalls }),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
messages.push(...(0, gemini_tools_1.toCanonicalToolMessages)(parts, pendingCalls));
|
|
78
|
+
if (otherParts.length > 0) {
|
|
79
|
+
messages.push({
|
|
80
|
+
role: "user",
|
|
81
|
+
content: extractContentParts({
|
|
82
|
+
role: content.role,
|
|
83
|
+
parts: otherParts,
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const result = {
|
|
90
|
+
model: "gemini-pro",
|
|
91
|
+
messages,
|
|
92
|
+
...(systemPrompt && { systemPrompt }),
|
|
93
|
+
};
|
|
94
|
+
// Convert tool definitions
|
|
95
|
+
const tools = (0, gemini_tools_1.toCanonicalTools)(request.tools);
|
|
96
|
+
if (tools) {
|
|
97
|
+
result.tools = tools;
|
|
98
|
+
}
|
|
99
|
+
// Convert tool config
|
|
100
|
+
const toolChoice = (0, gemini_tools_1.toCanonicalToolChoice)(request.toolConfig);
|
|
101
|
+
if (toolChoice !== undefined) {
|
|
102
|
+
result.toolChoice = toolChoice;
|
|
103
|
+
}
|
|
104
|
+
// Convert generation config
|
|
105
|
+
if (request.generationConfig) {
|
|
106
|
+
const gc = request.generationConfig;
|
|
107
|
+
result.config = {
|
|
108
|
+
...(gc.temperature !== undefined && { temperature: gc.temperature }),
|
|
109
|
+
...(gc.maxOutputTokens !== undefined && {
|
|
110
|
+
maxTokens: gc.maxOutputTokens,
|
|
111
|
+
}),
|
|
112
|
+
...(gc.topP !== undefined && { topP: gc.topP }),
|
|
113
|
+
...(gc.topK !== undefined && { topK: gc.topK }),
|
|
114
|
+
...(gc.stopSequences && { stopSequences: gc.stopSequences }),
|
|
115
|
+
...(gc.candidateCount !== undefined && {
|
|
116
|
+
candidateCount: gc.candidateCount,
|
|
117
|
+
}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
function extractTextFromInstruction(instruction) {
|
|
123
|
+
if (!instruction)
|
|
124
|
+
return "";
|
|
125
|
+
if (typeof instruction === "string")
|
|
126
|
+
return instruction;
|
|
127
|
+
if ("text" in instruction && typeof instruction.text === "string") {
|
|
128
|
+
return instruction.text;
|
|
129
|
+
}
|
|
130
|
+
if ("parts" in instruction) {
|
|
131
|
+
return instruction.parts
|
|
132
|
+
.filter((p) => "text" in p)
|
|
133
|
+
.map((p) => p.text)
|
|
134
|
+
.join("");
|
|
135
|
+
}
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
function extractContentParts(content) {
|
|
139
|
+
if (!content.parts || content.parts.length === 0) {
|
|
140
|
+
return "";
|
|
141
|
+
}
|
|
142
|
+
// If all parts are text, join them
|
|
143
|
+
const allText = content.parts.every((p) => "text" in p);
|
|
144
|
+
if (allText) {
|
|
145
|
+
return content.parts
|
|
146
|
+
.filter((p) => "text" in p)
|
|
147
|
+
.map((p) => p.text)
|
|
148
|
+
.join("");
|
|
149
|
+
}
|
|
150
|
+
// Mixed content
|
|
151
|
+
return content.parts.map((part) => {
|
|
152
|
+
if ("text" in part) {
|
|
153
|
+
return { type: "text", text: part.text };
|
|
154
|
+
}
|
|
155
|
+
if ("inlineData" in part && part.inlineData) {
|
|
156
|
+
return {
|
|
157
|
+
type: "image",
|
|
158
|
+
image: {
|
|
159
|
+
data: part.inlineData.data,
|
|
160
|
+
mimeType: part.inlineData
|
|
161
|
+
.mimeType,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return { type: "text", text: "" };
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
// ============ REQUEST: Canonical → Gemini ============
|
|
169
|
+
function fromCanonicalRequest(request) {
|
|
170
|
+
// toolCallId → function name, resolved from earlier assistant messages
|
|
171
|
+
const callNames = new Map();
|
|
172
|
+
const contents = [];
|
|
173
|
+
for (const m of request.messages) {
|
|
174
|
+
if (m.role === "system")
|
|
175
|
+
continue;
|
|
176
|
+
if (m.role === "assistant") {
|
|
177
|
+
const calls = m.toolCalls ?? [];
|
|
178
|
+
let parts = convertToParts(m.content);
|
|
179
|
+
for (const call of calls) {
|
|
180
|
+
callNames.set(call.id, call.function.name);
|
|
181
|
+
}
|
|
182
|
+
if (calls.length > 0) {
|
|
183
|
+
parts = parts.filter((p) => !("text" in p && p.text === ""));
|
|
184
|
+
parts.push(...calls.map(gemini_tools_1.functionCallPart));
|
|
185
|
+
}
|
|
186
|
+
contents.push({ role: "model", parts });
|
|
187
|
+
}
|
|
188
|
+
else if (m.role === "tool") {
|
|
189
|
+
const name = m.toolCallId
|
|
190
|
+
? callNames.get(m.toolCallId)
|
|
191
|
+
: undefined;
|
|
192
|
+
if (!name) {
|
|
193
|
+
throw new errors_1.ValidationError(`Tool result "${m.toolCallId ?? ""}" has no matching assistant tool call`);
|
|
194
|
+
}
|
|
195
|
+
(0, gemini_tools_1.appendFunctionResponsePart)(contents, (0, gemini_tools_1.functionResponsePart)(name, m.content));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
contents.push({
|
|
199
|
+
role: mapRoleToGemini(m.role),
|
|
200
|
+
parts: convertToParts(m.content),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const result = {
|
|
205
|
+
contents,
|
|
206
|
+
};
|
|
207
|
+
// Tool definitions; parameters filtered to Google's documented
|
|
208
|
+
// schema subset (spec §3.4)
|
|
209
|
+
const tools = (0, gemini_tools_1.toGeminiTools)(request.tools);
|
|
210
|
+
if (tools) {
|
|
211
|
+
result.tools = tools;
|
|
212
|
+
}
|
|
213
|
+
// Tool choice → functionCallingConfig
|
|
214
|
+
const toolConfig = (0, gemini_tools_1.toGeminiToolConfig)(request.toolChoice);
|
|
215
|
+
if (toolConfig) {
|
|
216
|
+
result.toolConfig = toolConfig;
|
|
217
|
+
}
|
|
218
|
+
// Add system instruction
|
|
219
|
+
const systemMsg = request.messages.find((m) => m.role === "system");
|
|
220
|
+
if (systemMsg || request.systemPrompt) {
|
|
221
|
+
const systemText = request.systemPrompt ||
|
|
222
|
+
(typeof systemMsg?.content === "string" ? systemMsg.content : "");
|
|
223
|
+
if (systemText) {
|
|
224
|
+
result.systemInstruction = {
|
|
225
|
+
role: "user",
|
|
226
|
+
parts: [{ text: systemText }],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Add generation config
|
|
231
|
+
if (request.config) {
|
|
232
|
+
const c = request.config;
|
|
233
|
+
result.generationConfig = {
|
|
234
|
+
...(c.temperature !== undefined && { temperature: c.temperature }),
|
|
235
|
+
...(c.maxTokens !== undefined && { maxOutputTokens: c.maxTokens }),
|
|
236
|
+
...(c.topP !== undefined && { topP: c.topP }),
|
|
237
|
+
...(c.topK !== undefined && { topK: c.topK }),
|
|
238
|
+
...(c.stopSequences && { stopSequences: c.stopSequences }),
|
|
239
|
+
...(c.candidateCount !== undefined && {
|
|
240
|
+
candidateCount: c.candidateCount,
|
|
241
|
+
}),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
function mapRoleToGemini(role) {
|
|
247
|
+
return role === "assistant" ? "model" : "user";
|
|
248
|
+
}
|
|
249
|
+
function convertToParts(content) {
|
|
250
|
+
if (typeof content === "string") {
|
|
251
|
+
return [{ text: content }];
|
|
252
|
+
}
|
|
253
|
+
return content.map((part) => {
|
|
254
|
+
if (part.type === "text") {
|
|
255
|
+
return { text: part.text || "" };
|
|
256
|
+
}
|
|
257
|
+
if (part.type === "image" && part.image) {
|
|
258
|
+
return {
|
|
259
|
+
inlineData: {
|
|
260
|
+
mimeType: part.image.mimeType,
|
|
261
|
+
data: part.image.data,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
return { text: "" };
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
// ============ RESPONSE: Gemini → Canonical ============
|
|
269
|
+
function toCanonicalResponse(response, model, ids = (0, ids_1.createIdGenerator)()) {
|
|
270
|
+
const candidate = response.candidates?.[0];
|
|
271
|
+
const content = candidate?.content;
|
|
272
|
+
const parts = content?.parts ?? [];
|
|
273
|
+
const text = parts
|
|
274
|
+
.filter((p) => "text" in p)
|
|
275
|
+
.map((p) => p.text)
|
|
276
|
+
.join("");
|
|
277
|
+
const toolCalls = (0, gemini_tools_1.toCanonicalToolCalls)(parts, ids);
|
|
278
|
+
return {
|
|
279
|
+
id: `gemini-${(0, crypto_1.randomUUID)()}`,
|
|
280
|
+
model,
|
|
281
|
+
message: {
|
|
282
|
+
role: "assistant",
|
|
283
|
+
content: text,
|
|
284
|
+
...(toolCalls.length > 0 && { toolCalls }),
|
|
285
|
+
},
|
|
286
|
+
finishReason: toolCalls.length > 0
|
|
287
|
+
? "tool_call"
|
|
288
|
+
: mapFinishReason(candidate?.finishReason),
|
|
289
|
+
usage: response.usageMetadata
|
|
290
|
+
? {
|
|
291
|
+
inputTokens: response.usageMetadata.promptTokenCount ?? 0,
|
|
292
|
+
outputTokens: response.usageMetadata.candidatesTokenCount ?? 0,
|
|
293
|
+
totalTokens: response.usageMetadata.totalTokenCount ?? 0,
|
|
294
|
+
}
|
|
295
|
+
: undefined,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function mapFinishReason(reason) {
|
|
299
|
+
switch (reason) {
|
|
300
|
+
case FinishReason.STOP:
|
|
301
|
+
case "STOP":
|
|
302
|
+
return "stop";
|
|
303
|
+
case FinishReason.MAX_TOKENS:
|
|
304
|
+
case "MAX_TOKENS":
|
|
305
|
+
return "length";
|
|
306
|
+
case FinishReason.SAFETY:
|
|
307
|
+
case "SAFETY":
|
|
308
|
+
case FinishReason.RECITATION:
|
|
309
|
+
case "RECITATION":
|
|
310
|
+
case FinishReason.BLOCKLIST:
|
|
311
|
+
case "BLOCKLIST":
|
|
312
|
+
case FinishReason.PROHIBITED_CONTENT:
|
|
313
|
+
case "PROHIBITED_CONTENT":
|
|
314
|
+
case FinishReason.SPII:
|
|
315
|
+
case "SPII":
|
|
316
|
+
case FinishReason.IMAGE_SAFETY:
|
|
317
|
+
case "IMAGE_SAFETY":
|
|
318
|
+
return "content_filter";
|
|
319
|
+
default:
|
|
320
|
+
return "stop";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// ============ RESPONSE: Canonical → Gemini ============
|
|
324
|
+
function fromCanonicalResponse(response) {
|
|
325
|
+
const content = typeof response.message.content === "string"
|
|
326
|
+
? response.message.content
|
|
327
|
+
: "";
|
|
328
|
+
const toolCalls = response.message.toolCalls ?? [];
|
|
329
|
+
const parts = toolCalls.length > 0 && content === ""
|
|
330
|
+
? toolCalls.map(gemini_tools_1.functionCallPart)
|
|
331
|
+
: [{ text: content }, ...toolCalls.map(gemini_tools_1.functionCallPart)];
|
|
332
|
+
return {
|
|
333
|
+
candidates: [
|
|
334
|
+
{
|
|
335
|
+
content: {
|
|
336
|
+
role: "model",
|
|
337
|
+
parts,
|
|
338
|
+
},
|
|
339
|
+
finishReason: mapToGeminiFinishReason(response.finishReason),
|
|
340
|
+
index: 0,
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
usageMetadata: response.usage
|
|
344
|
+
? {
|
|
345
|
+
promptTokenCount: response.usage.inputTokens,
|
|
346
|
+
candidatesTokenCount: response.usage.outputTokens,
|
|
347
|
+
totalTokenCount: response.usage.totalTokens,
|
|
348
|
+
}
|
|
349
|
+
: undefined,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function toCanonicalModelList(response) {
|
|
353
|
+
const res = response;
|
|
354
|
+
return {
|
|
355
|
+
models: (res.models ?? [])
|
|
356
|
+
.filter((m) => Boolean(m.name))
|
|
357
|
+
.map((m) => ({
|
|
358
|
+
// "models/gemini-2.0-flash" → "gemini-2.0-flash"
|
|
359
|
+
id: m.name.replace(/^models\//, ""),
|
|
360
|
+
displayName: m.displayName,
|
|
361
|
+
})),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function fromCanonicalModelList(list) {
|
|
365
|
+
return {
|
|
366
|
+
models: list.models.map((m) => ({
|
|
367
|
+
name: `models/${m.id}`,
|
|
368
|
+
displayName: m.displayName,
|
|
369
|
+
})),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function mapToGeminiFinishReason(reason) {
|
|
373
|
+
switch (reason) {
|
|
374
|
+
case "stop":
|
|
375
|
+
return FinishReason.STOP;
|
|
376
|
+
case "length":
|
|
377
|
+
return FinishReason.MAX_TOKENS;
|
|
378
|
+
case "content_filter":
|
|
379
|
+
return FinishReason.SAFETY;
|
|
380
|
+
default:
|
|
381
|
+
return FinishReason.STOP;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// ============ ERRORS ============
|
|
385
|
+
const GEMINI_ERROR_STATUS = {
|
|
386
|
+
invalid_request: "INVALID_ARGUMENT",
|
|
387
|
+
authentication: "UNAUTHENTICATED",
|
|
388
|
+
permission: "PERMISSION_DENIED",
|
|
389
|
+
not_found: "NOT_FOUND",
|
|
390
|
+
request_too_large: "RESOURCE_EXHAUSTED",
|
|
391
|
+
rate_limit: "RESOURCE_EXHAUSTED",
|
|
392
|
+
unsupported: "UNIMPLEMENTED",
|
|
393
|
+
upstream: "INTERNAL",
|
|
394
|
+
unavailable: "UNAVAILABLE",
|
|
395
|
+
timeout: "DEADLINE_EXCEEDED",
|
|
396
|
+
internal: "INTERNAL",
|
|
397
|
+
};
|
|
398
|
+
function toCanonicalError(status, body) {
|
|
399
|
+
const err = body?.error;
|
|
400
|
+
const message = typeof err?.message === "string" && err.message.length > 0
|
|
401
|
+
? err.message
|
|
402
|
+
: `Upstream gemini returned ${status}`;
|
|
403
|
+
const details = err?.details;
|
|
404
|
+
const keyInvalid = Array.isArray(details) &&
|
|
405
|
+
details.some((d) => typeof d === "object" &&
|
|
406
|
+
d !== null &&
|
|
407
|
+
d.reason === "API_KEY_INVALID");
|
|
408
|
+
return (0, canonical_error_1.makeCanonicalError)(keyInvalid ? "authentication" : (0, canonical_error_1.kindFromStatus)(status), message);
|
|
409
|
+
}
|
|
410
|
+
function fromCanonicalError(err) {
|
|
411
|
+
return {
|
|
412
|
+
error: {
|
|
413
|
+
code: err.status,
|
|
414
|
+
message: err.message,
|
|
415
|
+
status: GEMINI_ERROR_STATUS[err.kind],
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Filter a JSON Schema down to the subset the Gemini API documents for
|
|
4
|
+
* function-declaration `parameters` (and responseSchema), spec §3.4.
|
|
5
|
+
* Source: https://ai.google.dev/api/generate-content#Schema
|
|
6
|
+
* (read 2026-10-27) — the documented fields minus `default`, which the
|
|
7
|
+
* doc itself marks "accepted and ignored" (owner decision 2026-10-27).
|
|
8
|
+
* Returns a new object; the input is never mutated.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.toGeminiSchema = toGeminiSchema;
|
|
12
|
+
const ALLOWED_KEYS = new Set([
|
|
13
|
+
"type",
|
|
14
|
+
"format",
|
|
15
|
+
"title",
|
|
16
|
+
"description",
|
|
17
|
+
"nullable",
|
|
18
|
+
"enum",
|
|
19
|
+
"maxItems",
|
|
20
|
+
"minItems",
|
|
21
|
+
"properties",
|
|
22
|
+
"required",
|
|
23
|
+
"minProperties",
|
|
24
|
+
"maxProperties",
|
|
25
|
+
"minLength",
|
|
26
|
+
"maxLength",
|
|
27
|
+
"pattern",
|
|
28
|
+
"example",
|
|
29
|
+
"anyOf",
|
|
30
|
+
"propertyOrdering",
|
|
31
|
+
"items",
|
|
32
|
+
"minimum",
|
|
33
|
+
"maximum",
|
|
34
|
+
]);
|
|
35
|
+
function isPlainObject(value) {
|
|
36
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
function toGeminiSchema(schema) {
|
|
39
|
+
const out = {};
|
|
40
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
41
|
+
if (!ALLOWED_KEYS.has(key))
|
|
42
|
+
continue;
|
|
43
|
+
if (key === "properties" && isPlainObject(value)) {
|
|
44
|
+
const properties = {};
|
|
45
|
+
for (const [name, sub] of Object.entries(value)) {
|
|
46
|
+
properties[name] = isPlainObject(sub) ? toGeminiSchema(sub) : sub;
|
|
47
|
+
}
|
|
48
|
+
out[key] = properties;
|
|
49
|
+
}
|
|
50
|
+
else if (key === "items" && isPlainObject(value)) {
|
|
51
|
+
out[key] = toGeminiSchema(value);
|
|
52
|
+
}
|
|
53
|
+
else if (key === "anyOf" && Array.isArray(value)) {
|
|
54
|
+
out[key] = value.map((sub) => isPlainObject(sub) ? toGeminiSchema(sub) : sub);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
out[key] = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|