@f5-sales-demo/pi-ai 19.51.2
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/CHANGELOG.md +1997 -0
- package/README.md +1160 -0
- package/package.json +135 -0
- package/src/api-registry.ts +95 -0
- package/src/auth-storage.ts +2694 -0
- package/src/cli.ts +493 -0
- package/src/index.ts +42 -0
- package/src/model-cache.ts +97 -0
- package/src/model-manager.ts +349 -0
- package/src/model-thinking.ts +561 -0
- package/src/models.json +49439 -0
- package/src/models.json.d.ts +9 -0
- package/src/models.ts +56 -0
- package/src/prompts/turn-aborted-guidance.md +4 -0
- package/src/provider-details.ts +81 -0
- package/src/provider-models/descriptors.ts +285 -0
- package/src/provider-models/google.ts +90 -0
- package/src/provider-models/index.ts +4 -0
- package/src/provider-models/openai-compat.ts +2074 -0
- package/src/provider-models/special.ts +106 -0
- package/src/providers/amazon-bedrock.ts +706 -0
- package/src/providers/anthropic.ts +1682 -0
- package/src/providers/azure-openai-responses.ts +391 -0
- package/src/providers/cursor/gen/agent_pb.ts +15274 -0
- package/src/providers/cursor/proto/agent.proto +3526 -0
- package/src/providers/cursor/proto/buf.gen.yaml +6 -0
- package/src/providers/cursor/proto/buf.yaml +17 -0
- package/src/providers/cursor.ts +2218 -0
- package/src/providers/github-copilot-headers.ts +140 -0
- package/src/providers/gitlab-duo.ts +381 -0
- package/src/providers/google-gemini-cli.ts +1133 -0
- package/src/providers/google-shared.ts +354 -0
- package/src/providers/google-vertex.ts +436 -0
- package/src/providers/google.ts +381 -0
- package/src/providers/kimi.ts +151 -0
- package/src/providers/openai-codex/constants.ts +43 -0
- package/src/providers/openai-codex/request-transformer.ts +158 -0
- package/src/providers/openai-codex/response-handler.ts +81 -0
- package/src/providers/openai-codex-responses.ts +2345 -0
- package/src/providers/openai-completions-compat.ts +159 -0
- package/src/providers/openai-completions.ts +1290 -0
- package/src/providers/openai-responses-shared.ts +452 -0
- package/src/providers/openai-responses.ts +519 -0
- package/src/providers/register-builtins.ts +329 -0
- package/src/providers/synthetic.ts +154 -0
- package/src/providers/transform-messages.ts +234 -0
- package/src/rate-limit-utils.ts +84 -0
- package/src/stream.ts +728 -0
- package/src/types.ts +546 -0
- package/src/usage/claude.ts +337 -0
- package/src/usage/gemini.ts +248 -0
- package/src/usage/github-copilot.ts +421 -0
- package/src/usage/google-antigravity.ts +200 -0
- package/src/usage/kimi.ts +286 -0
- package/src/usage/minimax-code.ts +31 -0
- package/src/usage/openai-codex.ts +387 -0
- package/src/usage/zai.ts +247 -0
- package/src/usage.ts +130 -0
- package/src/utils/abort.ts +36 -0
- package/src/utils/anthropic-auth.ts +293 -0
- package/src/utils/discovery/antigravity.ts +261 -0
- package/src/utils/discovery/codex.ts +371 -0
- package/src/utils/discovery/cursor.ts +306 -0
- package/src/utils/discovery/gemini.ts +248 -0
- package/src/utils/discovery/index.ts +5 -0
- package/src/utils/discovery/openai-compatible.ts +224 -0
- package/src/utils/event-stream.ts +209 -0
- package/src/utils/http-inspector.ts +165 -0
- package/src/utils/idle-iterator.ts +176 -0
- package/src/utils/json-parse.ts +28 -0
- package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
- package/src/utils/oauth/anthropic.ts +134 -0
- package/src/utils/oauth/api-key-validation.ts +92 -0
- package/src/utils/oauth/callback-server.ts +276 -0
- package/src/utils/oauth/cerebras.ts +59 -0
- package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
- package/src/utils/oauth/cursor.ts +157 -0
- package/src/utils/oauth/github-copilot.ts +358 -0
- package/src/utils/oauth/gitlab-duo.ts +123 -0
- package/src/utils/oauth/google-antigravity.ts +275 -0
- package/src/utils/oauth/google-gemini-cli.ts +334 -0
- package/src/utils/oauth/huggingface.ts +62 -0
- package/src/utils/oauth/index.ts +512 -0
- package/src/utils/oauth/kagi.ts +47 -0
- package/src/utils/oauth/kilo.ts +87 -0
- package/src/utils/oauth/kimi.ts +251 -0
- package/src/utils/oauth/litellm.ts +81 -0
- package/src/utils/oauth/lm-studio.ts +40 -0
- package/src/utils/oauth/minimax-code.ts +78 -0
- package/src/utils/oauth/moonshot.ts +59 -0
- package/src/utils/oauth/nanogpt.ts +51 -0
- package/src/utils/oauth/nvidia.ts +70 -0
- package/src/utils/oauth/oauth.html +199 -0
- package/src/utils/oauth/ollama.ts +47 -0
- package/src/utils/oauth/openai-codex.ts +190 -0
- package/src/utils/oauth/opencode.ts +49 -0
- package/src/utils/oauth/parallel.ts +46 -0
- package/src/utils/oauth/perplexity.ts +200 -0
- package/src/utils/oauth/pkce.ts +18 -0
- package/src/utils/oauth/qianfan.ts +58 -0
- package/src/utils/oauth/qwen-portal.ts +60 -0
- package/src/utils/oauth/synthetic.ts +60 -0
- package/src/utils/oauth/tavily.ts +46 -0
- package/src/utils/oauth/together.ts +59 -0
- package/src/utils/oauth/types.ts +89 -0
- package/src/utils/oauth/venice.ts +59 -0
- package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
- package/src/utils/oauth/vllm.ts +40 -0
- package/src/utils/oauth/xiaomi.ts +88 -0
- package/src/utils/oauth/zai.ts +60 -0
- package/src/utils/oauth/zenmux.ts +51 -0
- package/src/utils/overflow.ts +134 -0
- package/src/utils/retry-after.ts +110 -0
- package/src/utils/retry.ts +93 -0
- package/src/utils/schema/CONSTRAINTS.md +160 -0
- package/src/utils/schema/adapt.ts +20 -0
- package/src/utils/schema/compatibility.ts +397 -0
- package/src/utils/schema/dereference.ts +93 -0
- package/src/utils/schema/equality.ts +93 -0
- package/src/utils/schema/fields.ts +147 -0
- package/src/utils/schema/index.ts +9 -0
- package/src/utils/schema/normalize-cca.ts +479 -0
- package/src/utils/schema/sanitize-google.ts +212 -0
- package/src/utils/schema/strict-mode.ts +385 -0
- package/src/utils/schema/types.ts +5 -0
- package/src/utils/tool-choice.ts +81 -0
- package/src/utils/validation.ts +664 -0
- package/src/utils.ts +147 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import { $env } from "@f5xc-salesdemos/pi-utils";
|
|
2
|
+
import {
|
|
3
|
+
type GenerateContentConfig,
|
|
4
|
+
type GenerateContentParameters,
|
|
5
|
+
GoogleGenAI,
|
|
6
|
+
type ThinkingConfig,
|
|
7
|
+
ThinkingLevel,
|
|
8
|
+
} from "@google/genai";
|
|
9
|
+
import { calculateCost } from "../models";
|
|
10
|
+
import type {
|
|
11
|
+
Api,
|
|
12
|
+
AssistantMessage,
|
|
13
|
+
Context,
|
|
14
|
+
Model,
|
|
15
|
+
StreamFunction,
|
|
16
|
+
StreamOptions,
|
|
17
|
+
TextContent,
|
|
18
|
+
ThinkingContent,
|
|
19
|
+
ToolCall,
|
|
20
|
+
} from "../types";
|
|
21
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
22
|
+
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
|
23
|
+
import type { GoogleThinkingLevel } from "./google-gemini-cli";
|
|
24
|
+
import {
|
|
25
|
+
convertMessages,
|
|
26
|
+
convertTools,
|
|
27
|
+
isThinkingPart,
|
|
28
|
+
mapStopReason,
|
|
29
|
+
mapToolChoice,
|
|
30
|
+
retainThoughtSignature,
|
|
31
|
+
} from "./google-shared";
|
|
32
|
+
|
|
33
|
+
export interface GoogleVertexOptions extends StreamOptions {
|
|
34
|
+
toolChoice?: "auto" | "none" | "any";
|
|
35
|
+
thinking?: {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
budgetTokens?: number; // -1 for dynamic, 0 to disable
|
|
38
|
+
level?: GoogleThinkingLevel;
|
|
39
|
+
};
|
|
40
|
+
project?: string;
|
|
41
|
+
location?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface GoogleVertexSamplingConfig extends GenerateContentConfig {
|
|
45
|
+
topP?: number;
|
|
46
|
+
topK?: number;
|
|
47
|
+
minP?: number;
|
|
48
|
+
presencePenalty?: number;
|
|
49
|
+
repetitionPenalty?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const API_VERSION = "v1";
|
|
53
|
+
|
|
54
|
+
const THINKING_LEVEL_MAP: Record<GoogleThinkingLevel, ThinkingLevel> = {
|
|
55
|
+
THINKING_LEVEL_UNSPECIFIED: ThinkingLevel.THINKING_LEVEL_UNSPECIFIED,
|
|
56
|
+
MINIMAL: ThinkingLevel.MINIMAL,
|
|
57
|
+
LOW: ThinkingLevel.LOW,
|
|
58
|
+
MEDIUM: ThinkingLevel.MEDIUM,
|
|
59
|
+
HIGH: ThinkingLevel.HIGH,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Counter for generating unique tool call IDs
|
|
63
|
+
let toolCallCounter = 0;
|
|
64
|
+
|
|
65
|
+
export const streamGoogleVertex: StreamFunction<"google-vertex"> = (
|
|
66
|
+
model: Model<"google-vertex">,
|
|
67
|
+
context: Context,
|
|
68
|
+
options?: GoogleVertexOptions,
|
|
69
|
+
): AssistantMessageEventStream => {
|
|
70
|
+
const stream = new AssistantMessageEventStream();
|
|
71
|
+
|
|
72
|
+
(async () => {
|
|
73
|
+
const startTime = Date.now();
|
|
74
|
+
let firstTokenTime: number | undefined;
|
|
75
|
+
|
|
76
|
+
const output: AssistantMessage = {
|
|
77
|
+
role: "assistant",
|
|
78
|
+
content: [],
|
|
79
|
+
api: "google-vertex" as Api,
|
|
80
|
+
provider: model.provider,
|
|
81
|
+
model: model.id,
|
|
82
|
+
usage: {
|
|
83
|
+
input: 0,
|
|
84
|
+
output: 0,
|
|
85
|
+
cacheRead: 0,
|
|
86
|
+
cacheWrite: 0,
|
|
87
|
+
totalTokens: 0,
|
|
88
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
89
|
+
},
|
|
90
|
+
stopReason: "stop",
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
};
|
|
93
|
+
let rawRequestDump: RawHttpRequestDump | undefined;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const apiKey = resolveApiKey(options);
|
|
97
|
+
const project = apiKey ? undefined : resolveProject(options);
|
|
98
|
+
const location = apiKey ? undefined : resolveLocation(options);
|
|
99
|
+
const client = apiKey ? createClientWithApiKey(model, apiKey) : createClient(model, project!, location!);
|
|
100
|
+
const params = buildParams(model, context, options);
|
|
101
|
+
options?.onPayload?.(params);
|
|
102
|
+
rawRequestDump = {
|
|
103
|
+
provider: model.provider,
|
|
104
|
+
api: output.api,
|
|
105
|
+
model: model.id,
|
|
106
|
+
method: "POST",
|
|
107
|
+
url: apiKey
|
|
108
|
+
? `https://aiplatform.googleapis.com/${API_VERSION}/publishers/google/models/${model.id}:streamGenerateContent`
|
|
109
|
+
: `https://${location}-aiplatform.googleapis.com/${API_VERSION}/projects/${project}/locations/${location}/publishers/google/models/${model.id}:streamGenerateContent`,
|
|
110
|
+
body: params,
|
|
111
|
+
};
|
|
112
|
+
const googleStream = await client.models.generateContentStream(params);
|
|
113
|
+
|
|
114
|
+
stream.push({ type: "start", partial: output });
|
|
115
|
+
let currentBlock: TextContent | ThinkingContent | null = null;
|
|
116
|
+
const blocks = output.content;
|
|
117
|
+
const blockIndex = () => blocks.length - 1;
|
|
118
|
+
for await (const chunk of googleStream) {
|
|
119
|
+
const candidate = chunk.candidates?.[0];
|
|
120
|
+
if (candidate?.content?.parts) {
|
|
121
|
+
for (const part of candidate.content.parts) {
|
|
122
|
+
if (part.text !== undefined) {
|
|
123
|
+
if (!firstTokenTime) firstTokenTime = Date.now();
|
|
124
|
+
const isThinking = isThinkingPart(part);
|
|
125
|
+
if (
|
|
126
|
+
!currentBlock ||
|
|
127
|
+
(isThinking && currentBlock.type !== "thinking") ||
|
|
128
|
+
(!isThinking && currentBlock.type !== "text")
|
|
129
|
+
) {
|
|
130
|
+
if (currentBlock) {
|
|
131
|
+
if (currentBlock.type === "text") {
|
|
132
|
+
stream.push({
|
|
133
|
+
type: "text_end",
|
|
134
|
+
contentIndex: blocks.length - 1,
|
|
135
|
+
content: currentBlock.text,
|
|
136
|
+
partial: output,
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
stream.push({
|
|
140
|
+
type: "thinking_end",
|
|
141
|
+
contentIndex: blockIndex(),
|
|
142
|
+
content: currentBlock.thinking,
|
|
143
|
+
partial: output,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (isThinking) {
|
|
148
|
+
currentBlock = { type: "thinking", thinking: "", thinkingSignature: undefined };
|
|
149
|
+
output.content.push(currentBlock);
|
|
150
|
+
stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
|
|
151
|
+
} else {
|
|
152
|
+
currentBlock = { type: "text", text: "" };
|
|
153
|
+
output.content.push(currentBlock);
|
|
154
|
+
stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (currentBlock.type === "thinking") {
|
|
158
|
+
currentBlock.thinking += part.text;
|
|
159
|
+
currentBlock.thinkingSignature = retainThoughtSignature(
|
|
160
|
+
currentBlock.thinkingSignature,
|
|
161
|
+
part.thoughtSignature,
|
|
162
|
+
);
|
|
163
|
+
stream.push({
|
|
164
|
+
type: "thinking_delta",
|
|
165
|
+
contentIndex: blockIndex(),
|
|
166
|
+
delta: part.text,
|
|
167
|
+
partial: output,
|
|
168
|
+
});
|
|
169
|
+
} else {
|
|
170
|
+
currentBlock.text += part.text;
|
|
171
|
+
currentBlock.textSignature = retainThoughtSignature(
|
|
172
|
+
currentBlock.textSignature,
|
|
173
|
+
part.thoughtSignature,
|
|
174
|
+
);
|
|
175
|
+
stream.push({
|
|
176
|
+
type: "text_delta",
|
|
177
|
+
contentIndex: blockIndex(),
|
|
178
|
+
delta: part.text,
|
|
179
|
+
partial: output,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (part.functionCall) {
|
|
185
|
+
if (currentBlock) {
|
|
186
|
+
if (currentBlock.type === "text") {
|
|
187
|
+
stream.push({
|
|
188
|
+
type: "text_end",
|
|
189
|
+
contentIndex: blockIndex(),
|
|
190
|
+
content: currentBlock.text,
|
|
191
|
+
partial: output,
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
stream.push({
|
|
195
|
+
type: "thinking_end",
|
|
196
|
+
contentIndex: blockIndex(),
|
|
197
|
+
content: currentBlock.thinking,
|
|
198
|
+
partial: output,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
currentBlock = null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const providedId = part.functionCall.id;
|
|
205
|
+
const needsNewId =
|
|
206
|
+
!providedId || output.content.some(b => b.type === "toolCall" && b.id === providedId);
|
|
207
|
+
const toolCallId = needsNewId
|
|
208
|
+
? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}`
|
|
209
|
+
: providedId;
|
|
210
|
+
|
|
211
|
+
const toolCall: ToolCall = {
|
|
212
|
+
type: "toolCall",
|
|
213
|
+
id: toolCallId,
|
|
214
|
+
name: part.functionCall.name || "",
|
|
215
|
+
arguments: part.functionCall.args as Record<string, any>,
|
|
216
|
+
...(part.thoughtSignature && { thoughtSignature: part.thoughtSignature }),
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
output.content.push(toolCall);
|
|
220
|
+
stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
|
|
221
|
+
stream.push({
|
|
222
|
+
type: "toolcall_delta",
|
|
223
|
+
contentIndex: blockIndex(),
|
|
224
|
+
delta: JSON.stringify(toolCall.arguments),
|
|
225
|
+
partial: output,
|
|
226
|
+
});
|
|
227
|
+
stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (candidate?.finishReason) {
|
|
233
|
+
output.stopReason = mapStopReason(candidate.finishReason);
|
|
234
|
+
if (output.content.some(b => b.type === "toolCall")) {
|
|
235
|
+
output.stopReason = "toolUse";
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (chunk.usageMetadata) {
|
|
240
|
+
// promptTokenCount includes cachedContentTokenCount when cached content is used.
|
|
241
|
+
// Subtract to get non-cached input, matching the OpenAI convention where
|
|
242
|
+
// input = uncached prompt tokens and cacheRead = cached tokens so that
|
|
243
|
+
// input + cacheRead = total prompt tokens (no double-counting).
|
|
244
|
+
// Ref: https://ai.google.dev/api/generate-content#v1beta.GenerateContentResponse.UsageMetadata
|
|
245
|
+
const cachedTokens = chunk.usageMetadata.cachedContentTokenCount || 0;
|
|
246
|
+
output.usage = {
|
|
247
|
+
input: (chunk.usageMetadata.promptTokenCount || 0) - cachedTokens,
|
|
248
|
+
output:
|
|
249
|
+
(chunk.usageMetadata.candidatesTokenCount || 0) + (chunk.usageMetadata.thoughtsTokenCount || 0),
|
|
250
|
+
cacheRead: cachedTokens,
|
|
251
|
+
cacheWrite: 0,
|
|
252
|
+
totalTokens: chunk.usageMetadata.totalTokenCount || 0,
|
|
253
|
+
cost: {
|
|
254
|
+
input: 0,
|
|
255
|
+
output: 0,
|
|
256
|
+
cacheRead: 0,
|
|
257
|
+
cacheWrite: 0,
|
|
258
|
+
total: 0,
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
calculateCost(model, output.usage);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (currentBlock) {
|
|
266
|
+
if (currentBlock.type === "text") {
|
|
267
|
+
stream.push({
|
|
268
|
+
type: "text_end",
|
|
269
|
+
contentIndex: blockIndex(),
|
|
270
|
+
content: currentBlock.text,
|
|
271
|
+
partial: output,
|
|
272
|
+
});
|
|
273
|
+
} else {
|
|
274
|
+
stream.push({
|
|
275
|
+
type: "thinking_end",
|
|
276
|
+
contentIndex: blockIndex(),
|
|
277
|
+
content: currentBlock.thinking,
|
|
278
|
+
partial: output,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (options?.signal?.aborted) {
|
|
284
|
+
throw new Error("Request was aborted");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
288
|
+
throw new Error("An unknown error occurred");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
output.duration = Date.now() - startTime;
|
|
292
|
+
if (firstTokenTime) output.ttft = firstTokenTime - startTime;
|
|
293
|
+
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
294
|
+
stream.end();
|
|
295
|
+
} catch (error) {
|
|
296
|
+
// Remove internal index property used during streaming
|
|
297
|
+
for (const block of output.content) {
|
|
298
|
+
if ("index" in block) {
|
|
299
|
+
delete (block as { index?: number }).index;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
303
|
+
output.errorMessage = await finalizeErrorMessage(error, rawRequestDump);
|
|
304
|
+
output.duration = Date.now() - startTime;
|
|
305
|
+
if (firstTokenTime) output.ttft = firstTokenTime - startTime;
|
|
306
|
+
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
307
|
+
stream.end();
|
|
308
|
+
}
|
|
309
|
+
})();
|
|
310
|
+
|
|
311
|
+
return stream;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
function buildHttpOptions(model: Model<"google-vertex">): { headers?: Record<string, string> } | undefined {
|
|
315
|
+
if (!model.headers) {
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
return { headers: { ...model.headers } };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function createClient(model: Model<"google-vertex">, project: string, location: string): GoogleGenAI {
|
|
322
|
+
return new GoogleGenAI({
|
|
323
|
+
vertexai: true,
|
|
324
|
+
project,
|
|
325
|
+
location,
|
|
326
|
+
apiVersion: API_VERSION,
|
|
327
|
+
httpOptions: buildHttpOptions(model),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function createClientWithApiKey(model: Model<"google-vertex">, apiKey: string): GoogleGenAI {
|
|
332
|
+
return new GoogleGenAI({
|
|
333
|
+
vertexai: true,
|
|
334
|
+
apiKey,
|
|
335
|
+
apiVersion: API_VERSION,
|
|
336
|
+
httpOptions: buildHttpOptions(model),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function resolveApiKey(options?: GoogleVertexOptions): string | undefined {
|
|
341
|
+
// options.apiKey may contain sentinel values like "<authenticated>" or "N/A"
|
|
342
|
+
// leaked from the agent loop — only use it if it looks like a real API key.
|
|
343
|
+
const optKey = options?.apiKey;
|
|
344
|
+
const realKey = optKey && !optKey.startsWith("<") && optKey !== "N/A" ? optKey : undefined;
|
|
345
|
+
return realKey || $env.GOOGLE_CLOUD_API_KEY;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function resolveProject(options?: GoogleVertexOptions): string {
|
|
349
|
+
const project = options?.project || $env.GOOGLE_CLOUD_PROJECT || $env.GCLOUD_PROJECT;
|
|
350
|
+
if (!project) {
|
|
351
|
+
throw new Error(
|
|
352
|
+
"Vertex AI requires a project ID. Set GOOGLE_CLOUD_PROJECT/GCLOUD_PROJECT or pass project in options.",
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
return project;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function resolveLocation(options?: GoogleVertexOptions): string {
|
|
359
|
+
const location = options?.location || $env.GOOGLE_CLOUD_LOCATION;
|
|
360
|
+
if (!location) {
|
|
361
|
+
throw new Error("Vertex AI requires a location. Set GOOGLE_CLOUD_LOCATION or pass location in options.");
|
|
362
|
+
}
|
|
363
|
+
return location;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function buildParams(
|
|
367
|
+
model: Model<"google-vertex">,
|
|
368
|
+
context: Context,
|
|
369
|
+
options: GoogleVertexOptions = {},
|
|
370
|
+
): GenerateContentParameters {
|
|
371
|
+
const contents = convertMessages(model, context);
|
|
372
|
+
|
|
373
|
+
const generationConfig: GoogleVertexSamplingConfig = {};
|
|
374
|
+
if (options.temperature !== undefined) {
|
|
375
|
+
generationConfig.temperature = options.temperature;
|
|
376
|
+
}
|
|
377
|
+
if (options.maxTokens !== undefined) {
|
|
378
|
+
generationConfig.maxOutputTokens = options.maxTokens;
|
|
379
|
+
}
|
|
380
|
+
if (options.topP !== undefined) {
|
|
381
|
+
generationConfig.topP = options.topP;
|
|
382
|
+
}
|
|
383
|
+
if (options.topK !== undefined) {
|
|
384
|
+
generationConfig.topK = options.topK;
|
|
385
|
+
}
|
|
386
|
+
if (options.minP !== undefined) {
|
|
387
|
+
generationConfig.minP = options.minP;
|
|
388
|
+
}
|
|
389
|
+
if (options.presencePenalty !== undefined) {
|
|
390
|
+
generationConfig.presencePenalty = options.presencePenalty;
|
|
391
|
+
}
|
|
392
|
+
if (options.repetitionPenalty !== undefined) {
|
|
393
|
+
generationConfig.repetitionPenalty = options.repetitionPenalty;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const config: GenerateContentConfig = {
|
|
397
|
+
...(Object.keys(generationConfig).length > 0 && generationConfig),
|
|
398
|
+
...(context.systemPrompt && { systemInstruction: context.systemPrompt.toWellFormed() }),
|
|
399
|
+
...(context.tools && context.tools.length > 0 && { tools: convertTools(context.tools, model) }),
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
if (context.tools && context.tools.length > 0 && options.toolChoice) {
|
|
403
|
+
config.toolConfig = {
|
|
404
|
+
functionCallingConfig: {
|
|
405
|
+
mode: mapToolChoice(options.toolChoice),
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
} else {
|
|
409
|
+
config.toolConfig = undefined;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (options.thinking?.enabled && model.reasoning) {
|
|
413
|
+
const cfg: ThinkingConfig = { includeThoughts: true };
|
|
414
|
+
if (options.thinking.level !== undefined) {
|
|
415
|
+
cfg.thinkingLevel = THINKING_LEVEL_MAP[options.thinking.level];
|
|
416
|
+
} else if (options.thinking.budgetTokens !== undefined) {
|
|
417
|
+
cfg.thinkingBudget = options.thinking.budgetTokens;
|
|
418
|
+
}
|
|
419
|
+
config.thinkingConfig = cfg;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (options.signal) {
|
|
423
|
+
if (options.signal.aborted) {
|
|
424
|
+
throw new Error("Request aborted");
|
|
425
|
+
}
|
|
426
|
+
config.abortSignal = options.signal;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const params: GenerateContentParameters = {
|
|
430
|
+
model: model.id,
|
|
431
|
+
contents,
|
|
432
|
+
config,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
return params;
|
|
436
|
+
}
|