@ank1015/providers 0.0.1 → 0.0.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/LICENSE +21 -0
- package/README.md +93 -383
- package/dist/agent/conversation.d.ts +97 -0
- package/dist/agent/conversation.d.ts.map +1 -0
- package/dist/agent/conversation.js +328 -0
- package/dist/agent/conversation.js.map +1 -0
- package/dist/agent/runner.d.ts +37 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +169 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/tools/calculate.d.ts +15 -0
- package/dist/agent/tools/calculate.d.ts.map +1 -0
- package/dist/agent/tools/calculate.js +23 -0
- package/dist/agent/tools/calculate.js.map +1 -0
- package/dist/agent/tools/get-current-time.d.ts +15 -0
- package/dist/agent/tools/get-current-time.d.ts.map +1 -0
- package/dist/agent/tools/get-current-time.js +38 -0
- package/dist/agent/tools/get-current-time.js.map +1 -0
- package/dist/agent/tools/index.d.ts +3 -0
- package/dist/agent/tools/index.d.ts.map +1 -0
- package/dist/agent/tools/index.js +3 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/types.d.ts +53 -31
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js +1 -2
- package/dist/agent/utils.d.ts +14 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +59 -0
- package/dist/agent/utils.js.map +1 -0
- package/dist/index.d.ts +16 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/llm.d.ts +15 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +92 -0
- package/dist/llm.js.map +1 -0
- package/dist/models.d.ts +8 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +25 -112
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +72 -227
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +30 -32
- package/dist/models.js.map +1 -1
- package/dist/providers/google/complete.d.ts +3 -0
- package/dist/providers/google/complete.d.ts.map +1 -0
- package/dist/providers/google/complete.js +53 -0
- package/dist/providers/google/complete.js.map +1 -0
- package/dist/providers/google/index.d.ts +6 -0
- package/dist/providers/google/index.d.ts.map +1 -0
- package/dist/providers/google/index.js +6 -0
- package/dist/providers/google/index.js.map +1 -0
- package/dist/providers/google/stream.d.ts +3 -0
- package/dist/providers/google/stream.d.ts.map +1 -0
- package/dist/providers/{google.js → google/stream.js} +67 -231
- package/dist/providers/google/stream.js.map +1 -0
- package/dist/providers/google/types.d.ts +8 -0
- package/dist/providers/google/types.d.ts.map +1 -0
- package/dist/providers/google/types.js +2 -0
- package/dist/providers/google/types.js.map +1 -0
- package/dist/providers/google/utils.d.ts +30 -0
- package/dist/providers/google/utils.d.ts.map +1 -0
- package/dist/providers/google/utils.js +354 -0
- package/dist/providers/google/utils.js.map +1 -0
- package/dist/providers/openai/complete.d.ts +3 -0
- package/dist/providers/openai/complete.d.ts.map +1 -0
- package/dist/providers/openai/complete.js +57 -0
- package/dist/providers/openai/complete.js.map +1 -0
- package/dist/providers/openai/index.d.ts +4 -0
- package/dist/providers/openai/index.d.ts.map +1 -0
- package/dist/providers/openai/index.js +4 -0
- package/dist/providers/openai/index.js.map +1 -0
- package/dist/providers/openai/stream.d.ts +3 -0
- package/dist/providers/openai/stream.d.ts.map +1 -0
- package/dist/providers/{openai.js → openai/stream.js} +74 -152
- package/dist/providers/openai/stream.js.map +1 -0
- package/dist/providers/openai/types.d.ts +8 -0
- package/dist/providers/openai/types.d.ts.map +1 -0
- package/dist/providers/openai/types.js +2 -0
- package/dist/providers/openai/types.js.map +1 -0
- package/dist/providers/openai/utils.d.ts +13 -0
- package/dist/providers/openai/utils.d.ts.map +1 -0
- package/dist/providers/openai/utils.js +285 -0
- package/dist/providers/openai/utils.js.map +1 -0
- package/dist/types.d.ts +95 -87
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -9
- package/dist/types.js.map +1 -1
- package/dist/utils/event-stream.d.ts +2 -2
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +2 -7
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/json-parse.js +3 -6
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/overflow.d.ts +51 -0
- package/dist/utils/overflow.d.ts.map +1 -0
- package/dist/utils/overflow.js +106 -0
- package/dist/utils/overflow.js.map +1 -0
- package/dist/utils/sanitize-unicode.js +1 -4
- package/dist/utils/sanitize-unicode.js.map +1 -1
- package/dist/utils/uuid.d.ts +6 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +9 -0
- package/dist/utils/uuid.js.map +1 -0
- package/dist/utils/validation.d.ts +10 -3
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +20 -12
- package/dist/utils/validation.js.map +1 -1
- package/package.json +47 -8
- package/biome.json +0 -43
- package/dist/agent/agent-loop.d.ts +0 -5
- package/dist/agent/agent-loop.d.ts.map +0 -1
- package/dist/agent/agent-loop.js +0 -219
- package/dist/agent/agent-loop.js.map +0 -1
- package/dist/providers/convert.d.ts +0 -6
- package/dist/providers/convert.d.ts.map +0 -1
- package/dist/providers/convert.js +0 -207
- package/dist/providers/convert.js.map +0 -1
- package/dist/providers/google.d.ts +0 -26
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -17
- package/dist/providers/openai.d.ts.map +0 -1
- package/dist/providers/openai.js.map +0 -1
- package/dist/stream.d.ts +0 -4
- package/dist/stream.d.ts.map +0 -1
- package/dist/stream.js +0 -40
- package/dist/stream.js.map +0 -1
- package/dist/test-google-agent-loop.d.ts +0 -2
- package/dist/test-google-agent-loop.d.ts.map +0 -1
- package/dist/test-google-agent-loop.js +0 -186
- package/dist/test-google-agent-loop.js.map +0 -1
- package/dist/test-google.d.ts +0 -2
- package/dist/test-google.d.ts.map +0 -1
- package/dist/test-google.js +0 -41
- package/dist/test-google.js.map +0 -1
- package/src/agent/agent-loop.ts +0 -275
- package/src/agent/types.ts +0 -80
- package/src/index.ts +0 -72
- package/src/models.generated.ts +0 -314
- package/src/models.ts +0 -45
- package/src/providers/convert.ts +0 -222
- package/src/providers/google.ts +0 -496
- package/src/providers/openai.ts +0 -437
- package/src/stream.ts +0 -60
- package/src/types.ts +0 -198
- package/src/utils/event-stream.ts +0 -60
- package/src/utils/json-parse.ts +0 -28
- package/src/utils/sanitize-unicode.ts +0 -25
- package/src/utils/validation.ts +0 -69
- package/test/core/agent-loop.test.ts +0 -958
- package/test/core/stream.test.ts +0 -409
- package/test/data/red-circle.png +0 -0
- package/test/data/superintelligentwill.pdf +0 -0
- package/test/edge-cases/general.test.ts +0 -565
- package/test/integration/e2e.test.ts +0 -530
- package/test/models/cost.test.ts +0 -499
- package/test/models/registry.test.ts +0 -298
- package/test/providers/convert.test.ts +0 -846
- package/test/providers/google-schema.test.ts +0 -666
- package/test/providers/google-stream.test.ts +0 -369
- package/test/providers/openai-stream.test.ts +0 -251
- package/test/utils/event-stream.test.ts +0 -289
- package/test/utils/json-parse.test.ts +0 -344
- package/test/utils/sanitize-unicode.test.ts +0 -329
- package/test/utils/validation.test.ts +0 -614
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -9
package/src/providers/google.ts
DELETED
|
@@ -1,496 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type Content,
|
|
3
|
-
FinishReason,
|
|
4
|
-
FunctionCallingConfigMode,
|
|
5
|
-
type GenerateContentConfig,
|
|
6
|
-
type GenerateContentParameters,
|
|
7
|
-
GenerateContentResponse,
|
|
8
|
-
GoogleGenAI,
|
|
9
|
-
type Part,
|
|
10
|
-
ThinkingLevel,
|
|
11
|
-
} from "@google/genai";
|
|
12
|
-
|
|
13
|
-
import { sanitizeSurrogates } from "../utils/sanitize-unicode";
|
|
14
|
-
import { Model, StreamFunction, Context, Tool, Api, AssistantMessage, AssistantTextContent, AssistantThinkingContent, AssistantToolCall, StopReason } from "../types";
|
|
15
|
-
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
16
|
-
import { buildGoogleMessages } from "./convert";
|
|
17
|
-
import { calculateCost } from "../models";
|
|
18
|
-
import { validateToolArguments } from "../utils/validation";
|
|
19
|
-
|
|
20
|
-
export interface GoogleProviderOptions {
|
|
21
|
-
apiKey?: string;
|
|
22
|
-
signal?: AbortSignal;
|
|
23
|
-
temperature?: number;
|
|
24
|
-
maxOutputTokens?: number;
|
|
25
|
-
responseMimeType?: string;
|
|
26
|
-
thinkingConfig? : {
|
|
27
|
-
thinkingLevel: ThinkingLevel
|
|
28
|
-
};
|
|
29
|
-
imageConfig? : {
|
|
30
|
-
aspectRatio?: "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "9:16" | "16:9" | "21:9";
|
|
31
|
-
imageSize?: '1K' | '2K' | '4K';
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Counter for generating unique tool call IDs
|
|
36
|
-
let toolCallCounter = 0;
|
|
37
|
-
|
|
38
|
-
export const streamGoogle: StreamFunction<'google'> = (
|
|
39
|
-
model: Model<'google'>,
|
|
40
|
-
context: Context,
|
|
41
|
-
options: GoogleProviderOptions
|
|
42
|
-
) => {
|
|
43
|
-
const stream = new AssistantMessageEventStream();
|
|
44
|
-
|
|
45
|
-
(async () => {
|
|
46
|
-
|
|
47
|
-
const output: AssistantMessage = {
|
|
48
|
-
role: "assistant",
|
|
49
|
-
content: [],
|
|
50
|
-
api: "google-generative-ai" as Api,
|
|
51
|
-
model: model.id,
|
|
52
|
-
usage: {
|
|
53
|
-
input: 0,
|
|
54
|
-
output: 0,
|
|
55
|
-
cacheRead: 0,
|
|
56
|
-
cacheWrite: 0,
|
|
57
|
-
totalTokens: 0,
|
|
58
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
59
|
-
},
|
|
60
|
-
stopReason: "stop",
|
|
61
|
-
timestamp: Date.now(),
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
let finalResponse: GenerateContentResponse = {
|
|
65
|
-
text: '',
|
|
66
|
-
data: '',
|
|
67
|
-
functionCalls: [],
|
|
68
|
-
executableCode: '',
|
|
69
|
-
codeExecutionResult: ''
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const client = createClient(model, options?.apiKey);
|
|
74
|
-
const params = buildParams(model, context, options);
|
|
75
|
-
const googleStream = await client.models.generateContentStream(params);
|
|
76
|
-
|
|
77
|
-
stream.push({ type: "start", partial: output });
|
|
78
|
-
let currentBlock: AssistantTextContent | AssistantThinkingContent | null = null;
|
|
79
|
-
const blocks = output.content;
|
|
80
|
-
const blockIndex = () => blocks.length - 1;
|
|
81
|
-
const messageInputs: Content[] = [];
|
|
82
|
-
const accumulatedParts: Part[] = [];
|
|
83
|
-
|
|
84
|
-
for await (const chunk of googleStream) {
|
|
85
|
-
finalResponse = chunk
|
|
86
|
-
const candidate = chunk.candidates?.[0];
|
|
87
|
-
if (candidate?.content?.parts) {
|
|
88
|
-
// Accumulate parts, merging consecutive parts of the same type
|
|
89
|
-
for (const part of candidate.content.parts) {
|
|
90
|
-
const lastPart = accumulatedParts[accumulatedParts.length - 1];
|
|
91
|
-
|
|
92
|
-
// Check if we can merge with the last part
|
|
93
|
-
const canMerge = lastPart &&
|
|
94
|
-
part.text !== undefined &&
|
|
95
|
-
lastPart.text !== undefined &&
|
|
96
|
-
part.thought === lastPart.thought; // Both thinking or both regular text
|
|
97
|
-
|
|
98
|
-
if (canMerge) {
|
|
99
|
-
// Merge the text into the last part
|
|
100
|
-
if(part.text){
|
|
101
|
-
lastPart.text += part.text;
|
|
102
|
-
}
|
|
103
|
-
// Copy over thoughtSignature if present
|
|
104
|
-
if (part.thoughtSignature) {
|
|
105
|
-
lastPart.thoughtSignature = part.thoughtSignature;
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
// Add as a new part
|
|
109
|
-
accumulatedParts.push({ ...part });
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
for (const part of candidate.content.parts) {
|
|
114
|
-
if (part.text !== undefined) {
|
|
115
|
-
const isThinking = part.thought === true;
|
|
116
|
-
if (
|
|
117
|
-
!currentBlock ||
|
|
118
|
-
(isThinking && currentBlock.type !== "thinking") ||
|
|
119
|
-
(!isThinking && currentBlock.type !== "text")
|
|
120
|
-
) {
|
|
121
|
-
if (currentBlock) {
|
|
122
|
-
if (currentBlock.type === "text") {
|
|
123
|
-
stream.push({
|
|
124
|
-
type: "text_end",
|
|
125
|
-
contentIndex: blocks.length - 1,
|
|
126
|
-
content: currentBlock.text,
|
|
127
|
-
partial: output,
|
|
128
|
-
});
|
|
129
|
-
} else {
|
|
130
|
-
stream.push({
|
|
131
|
-
type: "thinking_end",
|
|
132
|
-
contentIndex: blockIndex(),
|
|
133
|
-
content: currentBlock.thinking,
|
|
134
|
-
partial: output,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (isThinking) {
|
|
139
|
-
currentBlock = { type: "thinking", thinking: "" };
|
|
140
|
-
output.content.push(currentBlock);
|
|
141
|
-
stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
|
|
142
|
-
} else {
|
|
143
|
-
currentBlock = { type: "text", text: "" };
|
|
144
|
-
output.content.push(currentBlock);
|
|
145
|
-
stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (currentBlock.type === "thinking") {
|
|
149
|
-
currentBlock.thinking += part.text;
|
|
150
|
-
stream.push({
|
|
151
|
-
type: "thinking_delta",
|
|
152
|
-
contentIndex: blockIndex(),
|
|
153
|
-
delta: part.text,
|
|
154
|
-
partial: output,
|
|
155
|
-
});
|
|
156
|
-
} else {
|
|
157
|
-
currentBlock.text += part.text;
|
|
158
|
-
stream.push({
|
|
159
|
-
type: "text_delta",
|
|
160
|
-
contentIndex: blockIndex(),
|
|
161
|
-
delta: part.text,
|
|
162
|
-
partial: output,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (part.functionCall) {
|
|
168
|
-
if (currentBlock) {
|
|
169
|
-
if (currentBlock.type === "text") {
|
|
170
|
-
stream.push({
|
|
171
|
-
type: "text_end",
|
|
172
|
-
contentIndex: blockIndex(),
|
|
173
|
-
content: currentBlock.text,
|
|
174
|
-
partial: output,
|
|
175
|
-
});
|
|
176
|
-
} else {
|
|
177
|
-
stream.push({
|
|
178
|
-
type: "thinking_end",
|
|
179
|
-
contentIndex: blockIndex(),
|
|
180
|
-
content: currentBlock.thinking,
|
|
181
|
-
partial: output,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
currentBlock = null;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Generate unique ID if not provided or if it's a duplicate
|
|
188
|
-
const providedId = part.functionCall.id;
|
|
189
|
-
const needsNewId =
|
|
190
|
-
!providedId || output.content.some((b) => b.type === "toolCall" && b.id === providedId);
|
|
191
|
-
const toolCallId = needsNewId
|
|
192
|
-
? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}`
|
|
193
|
-
: providedId;
|
|
194
|
-
|
|
195
|
-
const toolCall: AssistantToolCall = {
|
|
196
|
-
type: "toolCall",
|
|
197
|
-
id: toolCallId,
|
|
198
|
-
name: part.functionCall.name || "",
|
|
199
|
-
arguments: part.functionCall.args as Record<string, any>,
|
|
200
|
-
...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// Validate tool arguments if tool definition is available
|
|
205
|
-
if (context.tools) {
|
|
206
|
-
const tool = context.tools.find((t) => t.name === toolCall.name);
|
|
207
|
-
if (tool) {
|
|
208
|
-
toolCall.arguments = validateToolArguments(tool, toolCall)as Record<string, any>;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
output.content.push(toolCall);
|
|
213
|
-
stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
|
|
214
|
-
stream.push({
|
|
215
|
-
type: "toolcall_delta",
|
|
216
|
-
contentIndex: blockIndex(),
|
|
217
|
-
delta: JSON.stringify(toolCall.arguments),
|
|
218
|
-
partial: output,
|
|
219
|
-
});
|
|
220
|
-
stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (candidate?.finishReason) {
|
|
226
|
-
output.stopReason = mapStopReason(candidate.finishReason);
|
|
227
|
-
if (output.content.some((b) => b.type === "toolCall")) {
|
|
228
|
-
output.stopReason = "toolUse";
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (chunk.usageMetadata) {
|
|
233
|
-
output.usage = {
|
|
234
|
-
input: chunk.usageMetadata.promptTokenCount || 0,
|
|
235
|
-
output:
|
|
236
|
-
(chunk.usageMetadata.candidatesTokenCount || 0) + (chunk.usageMetadata.thoughtsTokenCount || 0),
|
|
237
|
-
cacheRead: chunk.usageMetadata.cachedContentTokenCount || 0,
|
|
238
|
-
cacheWrite: 0,
|
|
239
|
-
totalTokens: chunk.usageMetadata.totalTokenCount || 0,
|
|
240
|
-
cost: {
|
|
241
|
-
input: 0,
|
|
242
|
-
output: 0,
|
|
243
|
-
cacheRead: 0,
|
|
244
|
-
cacheWrite: 0,
|
|
245
|
-
total: 0,
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
calculateCost(model, output.usage);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (currentBlock) {
|
|
253
|
-
if (currentBlock.type === "text") {
|
|
254
|
-
stream.push({
|
|
255
|
-
type: "text_end",
|
|
256
|
-
contentIndex: blockIndex(),
|
|
257
|
-
content: currentBlock.text,
|
|
258
|
-
partial: output,
|
|
259
|
-
});
|
|
260
|
-
} else {
|
|
261
|
-
stream.push({
|
|
262
|
-
type: "thinking_end",
|
|
263
|
-
contentIndex: blockIndex(),
|
|
264
|
-
content: currentBlock.thinking,
|
|
265
|
-
partial: output,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (options?.signal?.aborted) {
|
|
271
|
-
throw new Error("Request was aborted");
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
275
|
-
throw new Error("An unkown error ocurred");
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Build the complete Content from accumulated parts
|
|
279
|
-
if (accumulatedParts.length > 0) {
|
|
280
|
-
messageInputs.push({
|
|
281
|
-
role: 'model',
|
|
282
|
-
parts: accumulatedParts
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
finalResponse.candidates = [];
|
|
286
|
-
for(let i=0; i < messageInputs.length; i++){
|
|
287
|
-
finalResponse.candidates?.push({
|
|
288
|
-
content: messageInputs[i]
|
|
289
|
-
})
|
|
290
|
-
}
|
|
291
|
-
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
292
|
-
stream.end({
|
|
293
|
-
_provider: 'google',
|
|
294
|
-
role: 'assistant',
|
|
295
|
-
message: finalResponse
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
} catch(error){
|
|
299
|
-
for (const block of output.content) delete (block as any).index;
|
|
300
|
-
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
301
|
-
output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
|
|
302
|
-
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
303
|
-
|
|
304
|
-
stream.end({
|
|
305
|
-
_provider: 'google',
|
|
306
|
-
role: 'assistant',
|
|
307
|
-
message: finalResponse
|
|
308
|
-
})
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
})()
|
|
312
|
-
|
|
313
|
-
return stream;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function createClient(model: Model<"google">, apiKey?: string): GoogleGenAI {
|
|
317
|
-
if (!apiKey) {
|
|
318
|
-
if (!process.env.GEMINI_API_KEY) {
|
|
319
|
-
throw new Error(
|
|
320
|
-
"Gemini API key is required. Set GEMINI_API_KEY environment variable or pass it as an argument.",
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
apiKey = process.env.GEMINI_API_KEY;
|
|
324
|
-
}
|
|
325
|
-
return new GoogleGenAI({
|
|
326
|
-
apiKey,
|
|
327
|
-
httpOptions: model.headers ? { headers: model.headers } : undefined,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function buildParams(model: Model<"google">, context: Context, options?: GoogleProviderOptions){
|
|
332
|
-
|
|
333
|
-
const contents = buildGoogleMessages(model, context);
|
|
334
|
-
|
|
335
|
-
const config: GenerateContentConfig = {}
|
|
336
|
-
|
|
337
|
-
if(options?.signal){
|
|
338
|
-
config.abortSignal = options.signal;
|
|
339
|
-
}
|
|
340
|
-
if(context.systemPrompt){
|
|
341
|
-
config.systemInstruction = sanitizeSurrogates(context.systemPrompt);
|
|
342
|
-
}
|
|
343
|
-
if(options?.temperature){
|
|
344
|
-
config.temperature = options.temperature;
|
|
345
|
-
}
|
|
346
|
-
if(options?.maxOutputTokens){
|
|
347
|
-
config.maxOutputTokens = options.maxOutputTokens;
|
|
348
|
-
}
|
|
349
|
-
if(options?.responseMimeType){
|
|
350
|
-
config.responseMimeType = options.responseMimeType;
|
|
351
|
-
}
|
|
352
|
-
if(context.tools){
|
|
353
|
-
config.tools = convertTools(context.tools)
|
|
354
|
-
}
|
|
355
|
-
if(options?.thinkingConfig){
|
|
356
|
-
config.thinkingConfig = {
|
|
357
|
-
includeThoughts: true,
|
|
358
|
-
thinkingLevel: options.thinkingConfig.thinkingLevel
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
if(options?.imageConfig){
|
|
362
|
-
config.imageConfig = {
|
|
363
|
-
imageSize: options.imageConfig.imageSize,
|
|
364
|
-
aspectRatio: options.imageConfig.aspectRatio
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const params: GenerateContentParameters = {
|
|
369
|
-
model: model.id,
|
|
370
|
-
contents,
|
|
371
|
-
config
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
return params;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Transforms a JSON Schema to Google's supported subset.
|
|
379
|
-
* Main transformations:
|
|
380
|
-
* - Converts { "const": "value" } to { "enum": ["value"] }
|
|
381
|
-
* - Converts { "anyOf": [{ "const": "a" }, { "const": "b" }] } to { "enum": ["a", "b"] }
|
|
382
|
-
* - Recursively processes nested objects and arrays
|
|
383
|
-
*/
|
|
384
|
-
export function transformSchemaForGoogle(schema: any): any {
|
|
385
|
-
if (!schema || typeof schema !== 'object') {
|
|
386
|
-
return schema;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Handle arrays
|
|
390
|
-
if (Array.isArray(schema)) {
|
|
391
|
-
return schema.map(transformSchemaForGoogle);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const transformed: any = {};
|
|
395
|
-
|
|
396
|
-
// Handle const keyword - convert to enum
|
|
397
|
-
if ('const' in schema) {
|
|
398
|
-
transformed.enum = [schema.const];
|
|
399
|
-
// Copy over other properties except const
|
|
400
|
-
for (const key in schema) {
|
|
401
|
-
if (key !== 'const') {
|
|
402
|
-
transformed[key] = schema[key];
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
return transformed;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Handle anyOf with const values - convert to enum
|
|
409
|
-
if ('anyOf' in schema && Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {
|
|
410
|
-
const allConst = schema.anyOf.every((item: any) => item && typeof item === 'object' && 'const' in item);
|
|
411
|
-
if (allConst) {
|
|
412
|
-
// Extract all const values into a single enum
|
|
413
|
-
transformed.enum = schema.anyOf.map((item: any) => item.const);
|
|
414
|
-
// Copy over other properties from the parent schema
|
|
415
|
-
for (const key in schema) {
|
|
416
|
-
if (key !== 'anyOf') {
|
|
417
|
-
transformed[key] = schema[key];
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
// Copy type and other properties from the first anyOf item if not already set
|
|
421
|
-
if (schema.anyOf.length > 0) {
|
|
422
|
-
const firstItem = schema.anyOf[0];
|
|
423
|
-
for (const key in firstItem) {
|
|
424
|
-
if (key !== 'const' && !(key in transformed)) {
|
|
425
|
-
transformed[key] = firstItem[key];
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return transformed;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Recursively process all properties
|
|
434
|
-
for (const key in schema) {
|
|
435
|
-
if (key === 'properties' && typeof schema.properties === 'object') {
|
|
436
|
-
// Recursively transform each property
|
|
437
|
-
transformed.properties = {};
|
|
438
|
-
for (const propKey in schema.properties) {
|
|
439
|
-
transformed.properties[propKey] = transformSchemaForGoogle(schema.properties[propKey]);
|
|
440
|
-
}
|
|
441
|
-
} else if (key === 'items' && schema.items) {
|
|
442
|
-
// Recursively transform array items schema
|
|
443
|
-
transformed.items = transformSchemaForGoogle(schema.items);
|
|
444
|
-
} else if (key === 'anyOf' || key === 'oneOf' || key === 'allOf') {
|
|
445
|
-
// Recursively transform union/intersection schemas
|
|
446
|
-
transformed[key] = Array.isArray(schema[key])
|
|
447
|
-
? schema[key].map(transformSchemaForGoogle)
|
|
448
|
-
: transformSchemaForGoogle(schema[key]);
|
|
449
|
-
} else {
|
|
450
|
-
// Copy other properties as-is
|
|
451
|
-
transformed[key] = schema[key];
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return transformed;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function convertTools(tools: readonly Tool[]): any[] | undefined {
|
|
459
|
-
if (tools.length === 0) return undefined;
|
|
460
|
-
return [
|
|
461
|
-
{
|
|
462
|
-
functionDeclarations: tools.map((tool) => ({
|
|
463
|
-
name: tool.name,
|
|
464
|
-
description: tool.description,
|
|
465
|
-
parameters: transformSchemaForGoogle(tool.parameters),
|
|
466
|
-
})),
|
|
467
|
-
},
|
|
468
|
-
];
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function mapStopReason(reason: FinishReason): StopReason {
|
|
472
|
-
switch (reason) {
|
|
473
|
-
case FinishReason.STOP:
|
|
474
|
-
return "stop";
|
|
475
|
-
case FinishReason.MAX_TOKENS:
|
|
476
|
-
return "length";
|
|
477
|
-
case FinishReason.BLOCKLIST:
|
|
478
|
-
case FinishReason.PROHIBITED_CONTENT:
|
|
479
|
-
case FinishReason.SPII:
|
|
480
|
-
case FinishReason.SAFETY:
|
|
481
|
-
case FinishReason.IMAGE_SAFETY:
|
|
482
|
-
case FinishReason.IMAGE_PROHIBITED_CONTENT:
|
|
483
|
-
case FinishReason.RECITATION:
|
|
484
|
-
case FinishReason.FINISH_REASON_UNSPECIFIED:
|
|
485
|
-
case FinishReason.OTHER:
|
|
486
|
-
case FinishReason.LANGUAGE:
|
|
487
|
-
case FinishReason.MALFORMED_FUNCTION_CALL:
|
|
488
|
-
case FinishReason.UNEXPECTED_TOOL_CALL:
|
|
489
|
-
case FinishReason.NO_IMAGE:
|
|
490
|
-
return "error";
|
|
491
|
-
default: {
|
|
492
|
-
const _exhaustive: never = reason;
|
|
493
|
-
throw new Error(`Unhandled stop reason: ${_exhaustive}`);
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|