@corbat-tech/coco 2.25.14 → 2.27.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/dist/cli/index.js +1159 -672
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +630 -296
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import { glob } from 'glob';
|
|
|
21
21
|
import Anthropic from '@anthropic-ai/sdk';
|
|
22
22
|
import { jsonrepair } from 'jsonrepair';
|
|
23
23
|
import OpenAI from 'openai';
|
|
24
|
-
import {
|
|
24
|
+
import { GoogleGenAI, FunctionCallingConfigMode } from '@google/genai';
|
|
25
25
|
import 'events';
|
|
26
26
|
import 'minimatch';
|
|
27
27
|
import { simpleGit } from 'simple-git';
|
|
@@ -782,26 +782,58 @@ var init_flow = __esm({
|
|
|
782
782
|
promisify(execFile);
|
|
783
783
|
}
|
|
784
784
|
});
|
|
785
|
-
async function
|
|
785
|
+
async function inspectADC() {
|
|
786
786
|
try {
|
|
787
|
-
const { stdout } = await execAsync2(
|
|
787
|
+
const { stdout } = await execAsync2(PRINT_ACCESS_TOKEN_COMMAND, {
|
|
788
788
|
timeout: 1e4
|
|
789
789
|
});
|
|
790
790
|
const accessToken = stdout.trim();
|
|
791
|
-
if (!accessToken)
|
|
791
|
+
if (!accessToken) {
|
|
792
|
+
return {
|
|
793
|
+
status: "missing",
|
|
794
|
+
token: null,
|
|
795
|
+
message: "gcloud ADC is not configured.",
|
|
796
|
+
suggestion: `Run \`${ADC_LOGIN_COMMAND}\` manually, then retry Coco.`
|
|
797
|
+
};
|
|
798
|
+
}
|
|
792
799
|
const expiresAt = Date.now() + 55 * 60 * 1e3;
|
|
793
800
|
return {
|
|
794
|
-
|
|
795
|
-
|
|
801
|
+
status: "ok",
|
|
802
|
+
token: {
|
|
803
|
+
accessToken,
|
|
804
|
+
expiresAt
|
|
805
|
+
}
|
|
796
806
|
};
|
|
797
807
|
} catch (error) {
|
|
798
808
|
const message = error instanceof Error ? error.message : String(error);
|
|
809
|
+
if (message.includes("scope is required but not consented")) {
|
|
810
|
+
return {
|
|
811
|
+
status: "scope_not_consented",
|
|
812
|
+
token: null,
|
|
813
|
+
message: "gcloud ADC exists, but the required Google scope was not consented for this account.",
|
|
814
|
+
suggestion: `For Vertex AI, rerun \`gcloud auth application-default login\` manually. For Gemini API OAuth, follow Google's OAuth guide with your own OAuth client and run \`gcloud auth application-default login --client-id-file=client_secret.json --scopes='${GEMINI_OAUTH_SCOPES}'\`. Otherwise use a Gemini API key in Coco.`
|
|
815
|
+
};
|
|
816
|
+
}
|
|
799
817
|
if (message.includes("not logged in") || message.includes("no application default credentials")) {
|
|
800
|
-
return
|
|
818
|
+
return {
|
|
819
|
+
status: "missing",
|
|
820
|
+
token: null,
|
|
821
|
+
message: "No application default credentials were found for gcloud.",
|
|
822
|
+
suggestion: `Run \`${ADC_LOGIN_COMMAND}\` manually, then retry Coco.`
|
|
823
|
+
};
|
|
801
824
|
}
|
|
802
|
-
return
|
|
825
|
+
return {
|
|
826
|
+
status: "error",
|
|
827
|
+
token: null,
|
|
828
|
+
message,
|
|
829
|
+
suggestion: `Try \`${PRINT_ACCESS_TOKEN_COMMAND}\` in your terminal to inspect the local ADC state.`
|
|
830
|
+
};
|
|
803
831
|
}
|
|
804
832
|
}
|
|
833
|
+
async function getADCAccessToken() {
|
|
834
|
+
const result = await inspectADC();
|
|
835
|
+
return result.token;
|
|
836
|
+
}
|
|
805
837
|
async function getCachedADCToken() {
|
|
806
838
|
if (cachedToken && cachedToken.expiresAt && Date.now() < cachedToken.expiresAt) {
|
|
807
839
|
return cachedToken;
|
|
@@ -809,10 +841,16 @@ async function getCachedADCToken() {
|
|
|
809
841
|
cachedToken = await getADCAccessToken();
|
|
810
842
|
return cachedToken;
|
|
811
843
|
}
|
|
812
|
-
var execAsync2, cachedToken;
|
|
844
|
+
var execAsync2, PRINT_ACCESS_TOKEN_COMMAND, ADC_LOGIN_COMMAND, GEMINI_OAUTH_SCOPES, cachedToken;
|
|
813
845
|
var init_gcloud = __esm({
|
|
814
846
|
"src/auth/gcloud.ts"() {
|
|
815
847
|
execAsync2 = promisify(exec);
|
|
848
|
+
PRINT_ACCESS_TOKEN_COMMAND = "gcloud auth application-default print-access-token";
|
|
849
|
+
ADC_LOGIN_COMMAND = "gcloud auth application-default login";
|
|
850
|
+
GEMINI_OAUTH_SCOPES = [
|
|
851
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
852
|
+
"https://www.googleapis.com/auth/generative-language.retriever"
|
|
853
|
+
].join(",");
|
|
816
854
|
cachedToken = null;
|
|
817
855
|
}
|
|
818
856
|
});
|
|
@@ -898,6 +936,7 @@ var init_schema = __esm({
|
|
|
898
936
|
"codex",
|
|
899
937
|
"copilot",
|
|
900
938
|
"gemini",
|
|
939
|
+
"vertex",
|
|
901
940
|
"kimi",
|
|
902
941
|
"kimi-code",
|
|
903
942
|
"lmstudio",
|
|
@@ -914,7 +953,9 @@ var init_schema = __esm({
|
|
|
914
953
|
model: z.string().default("claude-sonnet-4-6"),
|
|
915
954
|
maxTokens: z.number().min(1).max(2e5).default(8192),
|
|
916
955
|
temperature: z.number().min(0).max(2).default(0),
|
|
917
|
-
timeout: z.number().min(1e3).default(12e4)
|
|
956
|
+
timeout: z.number().min(1e3).default(12e4),
|
|
957
|
+
project: z.string().optional(),
|
|
958
|
+
location: z.string().optional()
|
|
918
959
|
});
|
|
919
960
|
QualityConfigSchema = z.object({
|
|
920
961
|
minScore: z.number().min(0).max(100).default(85),
|
|
@@ -1366,6 +1407,8 @@ function getApiKey(provider) {
|
|
|
1366
1407
|
return process.env["OPENAI_API_KEY"];
|
|
1367
1408
|
case "gemini":
|
|
1368
1409
|
return process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
|
|
1410
|
+
case "vertex":
|
|
1411
|
+
return void 0;
|
|
1369
1412
|
case "kimi":
|
|
1370
1413
|
return process.env["KIMI_API_KEY"] ?? process.env["MOONSHOT_API_KEY"];
|
|
1371
1414
|
case "kimi-code":
|
|
@@ -1414,6 +1457,8 @@ function getBaseUrl(provider) {
|
|
|
1414
1457
|
return "https://chatgpt.com/backend-api/codex/responses";
|
|
1415
1458
|
case "copilot":
|
|
1416
1459
|
return process.env["COPILOT_BASE_URL"] ?? "https://api.githubcopilot.com";
|
|
1460
|
+
case "vertex":
|
|
1461
|
+
return process.env["VERTEX_BASE_URL"] ?? "https://aiplatform.googleapis.com/v1";
|
|
1417
1462
|
case "groq":
|
|
1418
1463
|
return process.env["GROQ_BASE_URL"] ?? "https://api.groq.com/openai/v1";
|
|
1419
1464
|
case "openrouter":
|
|
@@ -1440,6 +1485,8 @@ function getDefaultModel(provider) {
|
|
|
1440
1485
|
return process.env["OPENAI_MODEL"] ?? "gpt-5.4-codex";
|
|
1441
1486
|
case "gemini":
|
|
1442
1487
|
return process.env["GEMINI_MODEL"] ?? "gemini-3.1-pro-preview";
|
|
1488
|
+
case "vertex":
|
|
1489
|
+
return process.env["VERTEX_MODEL"] ?? "gemini-2.5-pro";
|
|
1443
1490
|
case "kimi":
|
|
1444
1491
|
return process.env["KIMI_MODEL"] ?? "kimi-k2.5";
|
|
1445
1492
|
case "kimi-code":
|
|
@@ -1489,6 +1536,7 @@ var init_env = __esm({
|
|
|
1489
1536
|
"codex",
|
|
1490
1537
|
"copilot",
|
|
1491
1538
|
"gemini",
|
|
1539
|
+
"vertex",
|
|
1492
1540
|
"kimi",
|
|
1493
1541
|
"kimi-code",
|
|
1494
1542
|
"lmstudio",
|
|
@@ -16028,19 +16076,14 @@ var CopilotProvider = class extends OpenAIProvider {
|
|
|
16028
16076
|
|
|
16029
16077
|
// src/providers/gemini.ts
|
|
16030
16078
|
init_errors();
|
|
16031
|
-
init_gcloud();
|
|
16032
16079
|
var DEFAULT_MODEL5 = "gemini-3.1-pro-preview";
|
|
16033
16080
|
var CONTEXT_WINDOWS5 = {
|
|
16034
|
-
// Gemini 3.1 series (latest)
|
|
16035
16081
|
"gemini-3.1-pro-preview": 1e6,
|
|
16036
16082
|
"gemini-3.1-flash-lite-preview": 1e6,
|
|
16037
|
-
// Gemini 3 series
|
|
16038
16083
|
"gemini-3-flash-preview": 1e6,
|
|
16039
|
-
// Gemini 2.5 series (production stable)
|
|
16040
16084
|
"gemini-2.5-pro": 1048576,
|
|
16041
16085
|
"gemini-2.5-flash": 1048576,
|
|
16042
16086
|
"gemini-2.5-flash-lite": 1048576,
|
|
16043
|
-
// Legacy
|
|
16044
16087
|
"gemini-1.5-flash": 1e6,
|
|
16045
16088
|
"gemini-1.5-pro": 2e6
|
|
16046
16089
|
};
|
|
@@ -16049,129 +16092,54 @@ var GeminiProvider = class {
|
|
|
16049
16092
|
name = "Google Gemini";
|
|
16050
16093
|
client = null;
|
|
16051
16094
|
config = {};
|
|
16052
|
-
/**
|
|
16053
|
-
* Initialize the provider
|
|
16054
|
-
*
|
|
16055
|
-
* Authentication priority:
|
|
16056
|
-
* 1. API key passed in config (unless it's the ADC marker)
|
|
16057
|
-
* 2. GEMINI_API_KEY environment variable
|
|
16058
|
-
* 3. GOOGLE_API_KEY environment variable
|
|
16059
|
-
* 4. Google Cloud ADC (gcloud auth application-default login)
|
|
16060
|
-
*/
|
|
16061
16095
|
async initialize(config) {
|
|
16062
16096
|
this.config = config;
|
|
16063
|
-
const
|
|
16064
|
-
let apiKey = !isADCMarker && config.apiKey ? config.apiKey : process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
|
|
16065
|
-
if (!apiKey || isADCMarker) {
|
|
16066
|
-
try {
|
|
16067
|
-
const adcToken = await getCachedADCToken();
|
|
16068
|
-
if (adcToken) {
|
|
16069
|
-
apiKey = adcToken.accessToken;
|
|
16070
|
-
this.config.useADC = true;
|
|
16071
|
-
}
|
|
16072
|
-
} catch {
|
|
16073
|
-
}
|
|
16074
|
-
}
|
|
16097
|
+
const apiKey = config.apiKey ?? process.env["GEMINI_API_KEY"] ?? process.env["GOOGLE_API_KEY"];
|
|
16075
16098
|
if (!apiKey) {
|
|
16076
16099
|
throw new ProviderError(
|
|
16077
|
-
"Gemini API key not provided. Set GEMINI_API_KEY or
|
|
16100
|
+
"Gemini Developer API key not provided. Set GEMINI_API_KEY or GOOGLE_API_KEY.",
|
|
16078
16101
|
{ provider: this.id }
|
|
16079
16102
|
);
|
|
16080
16103
|
}
|
|
16081
|
-
this.client = new
|
|
16104
|
+
this.client = new GoogleGenAI({ apiKey });
|
|
16082
16105
|
}
|
|
16083
|
-
/**
|
|
16084
|
-
* Refresh ADC token if needed and reinitialize client
|
|
16085
|
-
*/
|
|
16086
|
-
async refreshADCIfNeeded() {
|
|
16087
|
-
if (!this.config.useADC) return;
|
|
16088
|
-
try {
|
|
16089
|
-
const adcToken = await getCachedADCToken();
|
|
16090
|
-
if (adcToken) {
|
|
16091
|
-
this.client = new GoogleGenerativeAI(adcToken.accessToken);
|
|
16092
|
-
}
|
|
16093
|
-
} catch {
|
|
16094
|
-
}
|
|
16095
|
-
}
|
|
16096
|
-
/**
|
|
16097
|
-
* Send a chat message
|
|
16098
|
-
*/
|
|
16099
16106
|
async chat(messages, options) {
|
|
16100
16107
|
this.ensureInitialized();
|
|
16101
|
-
await this.refreshADCIfNeeded();
|
|
16102
16108
|
try {
|
|
16103
|
-
const
|
|
16104
|
-
model: options?.model
|
|
16105
|
-
|
|
16106
|
-
|
|
16107
|
-
temperature: options?.temperature ?? this.config.temperature ?? 0,
|
|
16108
|
-
stopSequences: options?.stopSequences
|
|
16109
|
-
},
|
|
16110
|
-
systemInstruction: this.extractSystem(messages, options?.system)
|
|
16109
|
+
const response = await this.client.models.generateContent({
|
|
16110
|
+
model: this.getModel(options?.model),
|
|
16111
|
+
contents: this.convertContents(messages),
|
|
16112
|
+
config: this.buildConfig(messages, options)
|
|
16111
16113
|
});
|
|
16112
|
-
|
|
16113
|
-
const chat = model.startChat({ history });
|
|
16114
|
-
const result = await chat.sendMessage(lastMessage);
|
|
16115
|
-
return this.parseResponse(result);
|
|
16114
|
+
return this.parseResponse(response, options?.model);
|
|
16116
16115
|
} catch (error) {
|
|
16117
16116
|
throw this.handleError(error);
|
|
16118
16117
|
}
|
|
16119
16118
|
}
|
|
16120
|
-
/**
|
|
16121
|
-
* Send a chat message with tool use
|
|
16122
|
-
*/
|
|
16123
16119
|
async chatWithTools(messages, options) {
|
|
16124
16120
|
this.ensureInitialized();
|
|
16125
|
-
await this.refreshADCIfNeeded();
|
|
16126
16121
|
try {
|
|
16127
|
-
const
|
|
16128
|
-
|
|
16129
|
-
|
|
16130
|
-
|
|
16131
|
-
];
|
|
16132
|
-
const model = this.client.getGenerativeModel({
|
|
16133
|
-
model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
|
|
16134
|
-
generationConfig: {
|
|
16135
|
-
maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
16136
|
-
temperature: options?.temperature ?? this.config.temperature ?? 0
|
|
16137
|
-
},
|
|
16138
|
-
systemInstruction: this.extractSystem(messages, options?.system),
|
|
16139
|
-
tools,
|
|
16140
|
-
toolConfig: {
|
|
16141
|
-
functionCallingConfig: {
|
|
16142
|
-
mode: this.convertToolChoice(options.toolChoice)
|
|
16143
|
-
}
|
|
16144
|
-
}
|
|
16122
|
+
const response = await this.client.models.generateContent({
|
|
16123
|
+
model: this.getModel(options.model),
|
|
16124
|
+
contents: this.convertContents(messages),
|
|
16125
|
+
config: this.buildConfig(messages, options, options.tools, options.toolChoice)
|
|
16145
16126
|
});
|
|
16146
|
-
|
|
16147
|
-
const chat = model.startChat({ history });
|
|
16148
|
-
const result = await chat.sendMessage(lastMessage);
|
|
16149
|
-
return this.parseResponseWithTools(result);
|
|
16127
|
+
return this.parseResponseWithTools(response, options.model);
|
|
16150
16128
|
} catch (error) {
|
|
16151
16129
|
throw this.handleError(error);
|
|
16152
16130
|
}
|
|
16153
16131
|
}
|
|
16154
|
-
/**
|
|
16155
|
-
* Stream a chat response
|
|
16156
|
-
*/
|
|
16157
16132
|
async *stream(messages, options) {
|
|
16158
16133
|
this.ensureInitialized();
|
|
16159
|
-
await this.refreshADCIfNeeded();
|
|
16160
16134
|
try {
|
|
16161
|
-
const
|
|
16162
|
-
model: options?.model
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
temperature: options?.temperature ?? this.config.temperature ?? 0
|
|
16166
|
-
},
|
|
16167
|
-
systemInstruction: this.extractSystem(messages, options?.system)
|
|
16135
|
+
const stream = await this.client.models.generateContentStream({
|
|
16136
|
+
model: this.getModel(options?.model),
|
|
16137
|
+
contents: this.convertContents(messages),
|
|
16138
|
+
config: this.buildConfig(messages, options)
|
|
16168
16139
|
});
|
|
16169
|
-
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
let streamStopReason;
|
|
16173
|
-
for await (const chunk of result.stream) {
|
|
16174
|
-
const text = chunk.text();
|
|
16140
|
+
let streamStopReason = "end_turn";
|
|
16141
|
+
for await (const chunk of stream) {
|
|
16142
|
+
const text = chunk.text;
|
|
16175
16143
|
if (text) {
|
|
16176
16144
|
yield { type: "text", text };
|
|
16177
16145
|
}
|
|
@@ -16185,116 +16153,76 @@ var GeminiProvider = class {
|
|
|
16185
16153
|
throw this.handleError(error);
|
|
16186
16154
|
}
|
|
16187
16155
|
}
|
|
16188
|
-
/**
|
|
16189
|
-
* Stream a chat response with tool use
|
|
16190
|
-
*/
|
|
16191
16156
|
async *streamWithTools(messages, options) {
|
|
16192
16157
|
this.ensureInitialized();
|
|
16193
|
-
await this.refreshADCIfNeeded();
|
|
16194
16158
|
try {
|
|
16195
|
-
const
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
];
|
|
16200
|
-
const model = this.client.getGenerativeModel({
|
|
16201
|
-
model: options?.model ?? this.config.model ?? DEFAULT_MODEL5,
|
|
16202
|
-
generationConfig: {
|
|
16203
|
-
maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
16204
|
-
temperature: options?.temperature ?? this.config.temperature ?? 0
|
|
16205
|
-
},
|
|
16206
|
-
systemInstruction: this.extractSystem(messages, options?.system),
|
|
16207
|
-
tools,
|
|
16208
|
-
toolConfig: {
|
|
16209
|
-
functionCallingConfig: {
|
|
16210
|
-
mode: this.convertToolChoice(options.toolChoice)
|
|
16211
|
-
}
|
|
16212
|
-
}
|
|
16159
|
+
const stream = await this.client.models.generateContentStream({
|
|
16160
|
+
model: this.getModel(options.model),
|
|
16161
|
+
contents: this.convertContents(messages),
|
|
16162
|
+
config: this.buildConfig(messages, options, options.tools, options.toolChoice)
|
|
16213
16163
|
});
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
const
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
for await (const chunk of result.stream) {
|
|
16220
|
-
const text = chunk.text();
|
|
16164
|
+
let streamStopReason = "end_turn";
|
|
16165
|
+
let fallbackToolCounter = 0;
|
|
16166
|
+
const emittedToolIds = /* @__PURE__ */ new Set();
|
|
16167
|
+
for await (const chunk of stream) {
|
|
16168
|
+
const text = chunk.text;
|
|
16221
16169
|
if (text) {
|
|
16222
16170
|
yield { type: "text", text };
|
|
16223
16171
|
}
|
|
16172
|
+
const functionCalls = this.extractFunctionCalls(chunk);
|
|
16173
|
+
for (const functionCall of functionCalls) {
|
|
16174
|
+
const toolCallId = functionCall.id ?? `gemini_call_${++fallbackToolCounter}`;
|
|
16175
|
+
if (emittedToolIds.has(toolCallId)) continue;
|
|
16176
|
+
emittedToolIds.add(toolCallId);
|
|
16177
|
+
const toolCall = {
|
|
16178
|
+
id: toolCallId,
|
|
16179
|
+
name: functionCall.name ?? "unknown_function",
|
|
16180
|
+
input: functionCall.args ?? {}
|
|
16181
|
+
};
|
|
16182
|
+
yield {
|
|
16183
|
+
type: "tool_use_start",
|
|
16184
|
+
toolCall: {
|
|
16185
|
+
id: toolCall.id,
|
|
16186
|
+
name: toolCall.name
|
|
16187
|
+
}
|
|
16188
|
+
};
|
|
16189
|
+
yield {
|
|
16190
|
+
type: "tool_use_end",
|
|
16191
|
+
toolCall
|
|
16192
|
+
};
|
|
16193
|
+
}
|
|
16224
16194
|
const finishReason = chunk.candidates?.[0]?.finishReason;
|
|
16225
|
-
if (
|
|
16195
|
+
if (functionCalls.length > 0) {
|
|
16196
|
+
streamStopReason = "tool_use";
|
|
16197
|
+
} else if (finishReason) {
|
|
16226
16198
|
streamStopReason = this.mapFinishReason(finishReason);
|
|
16227
16199
|
}
|
|
16228
|
-
const candidate = chunk.candidates?.[0];
|
|
16229
|
-
if (candidate?.content?.parts) {
|
|
16230
|
-
for (const part of candidate.content.parts) {
|
|
16231
|
-
if ("functionCall" in part && part.functionCall) {
|
|
16232
|
-
const funcCall = part.functionCall;
|
|
16233
|
-
streamToolCallCounter++;
|
|
16234
|
-
const toolCall = {
|
|
16235
|
-
id: `gemini_call_${streamToolCallCounter}`,
|
|
16236
|
-
name: funcCall.name,
|
|
16237
|
-
input: funcCall.args ?? {}
|
|
16238
|
-
};
|
|
16239
|
-
yield {
|
|
16240
|
-
type: "tool_use_start",
|
|
16241
|
-
toolCall: {
|
|
16242
|
-
id: toolCall.id,
|
|
16243
|
-
name: toolCall.name
|
|
16244
|
-
}
|
|
16245
|
-
};
|
|
16246
|
-
yield {
|
|
16247
|
-
type: "tool_use_end",
|
|
16248
|
-
toolCall
|
|
16249
|
-
};
|
|
16250
|
-
}
|
|
16251
|
-
}
|
|
16252
|
-
}
|
|
16253
16200
|
}
|
|
16254
16201
|
yield { type: "done", stopReason: streamStopReason };
|
|
16255
16202
|
} catch (error) {
|
|
16256
16203
|
throw this.handleError(error);
|
|
16257
16204
|
}
|
|
16258
16205
|
}
|
|
16259
|
-
/**
|
|
16260
|
-
* Count tokens (approximate)
|
|
16261
|
-
*
|
|
16262
|
-
* Gemini uses a SentencePiece tokenizer. The average ratio varies:
|
|
16263
|
-
* - English text: ~4 characters per token
|
|
16264
|
-
* - Code: ~3.2 characters per token
|
|
16265
|
-
* - Mixed content: ~3.5 characters per token
|
|
16266
|
-
*
|
|
16267
|
-
* Using 3.5 as the default provides a better estimate for typical
|
|
16268
|
-
* coding agent workloads which mix code and natural language.
|
|
16269
|
-
*/
|
|
16270
16206
|
countTokens(text) {
|
|
16271
16207
|
if (!text) return 0;
|
|
16272
16208
|
return Math.ceil(text.length / 3.5);
|
|
16273
16209
|
}
|
|
16274
|
-
/**
|
|
16275
|
-
* Get context window size
|
|
16276
|
-
*/
|
|
16277
16210
|
getContextWindow() {
|
|
16278
16211
|
const model = this.config.model ?? DEFAULT_MODEL5;
|
|
16279
16212
|
return CONTEXT_WINDOWS5[model] ?? 1e6;
|
|
16280
16213
|
}
|
|
16281
|
-
/**
|
|
16282
|
-
* Check if provider is available
|
|
16283
|
-
*/
|
|
16284
16214
|
async isAvailable() {
|
|
16285
16215
|
if (!this.client) return false;
|
|
16286
16216
|
try {
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
16217
|
+
await this.client.models.generateContent({
|
|
16218
|
+
model: this.getModel(),
|
|
16219
|
+
contents: "hi"
|
|
16220
|
+
});
|
|
16290
16221
|
return true;
|
|
16291
16222
|
} catch {
|
|
16292
16223
|
return false;
|
|
16293
16224
|
}
|
|
16294
16225
|
}
|
|
16295
|
-
/**
|
|
16296
|
-
* Ensure client is initialized
|
|
16297
|
-
*/
|
|
16298
16226
|
ensureInitialized() {
|
|
16299
16227
|
if (!this.client) {
|
|
16300
16228
|
throw new ProviderError("Provider not initialized. Call initialize() first.", {
|
|
@@ -16302,13 +16230,24 @@ var GeminiProvider = class {
|
|
|
16302
16230
|
});
|
|
16303
16231
|
}
|
|
16304
16232
|
}
|
|
16305
|
-
|
|
16306
|
-
|
|
16307
|
-
|
|
16308
|
-
|
|
16309
|
-
|
|
16310
|
-
|
|
16311
|
-
|
|
16233
|
+
getModel(model) {
|
|
16234
|
+
return model ?? this.config.model ?? DEFAULT_MODEL5;
|
|
16235
|
+
}
|
|
16236
|
+
buildConfig(messages, options, tools, toolChoice) {
|
|
16237
|
+
const config = {
|
|
16238
|
+
maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
16239
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0,
|
|
16240
|
+
stopSequences: options?.stopSequences,
|
|
16241
|
+
systemInstruction: this.extractSystem(messages, options?.system)
|
|
16242
|
+
};
|
|
16243
|
+
if (tools && tools.length > 0) {
|
|
16244
|
+
config.tools = [{ functionDeclarations: this.convertTools(tools) }];
|
|
16245
|
+
config.toolConfig = {
|
|
16246
|
+
functionCallingConfig: this.convertToolChoice(toolChoice)
|
|
16247
|
+
};
|
|
16248
|
+
}
|
|
16249
|
+
return config;
|
|
16250
|
+
}
|
|
16312
16251
|
extractSystem(messages, optionsSystem) {
|
|
16313
16252
|
if (optionsSystem !== void 0) return optionsSystem;
|
|
16314
16253
|
const systemMsg = messages.find((m) => m.role === "system");
|
|
@@ -16317,55 +16256,36 @@ var GeminiProvider = class {
|
|
|
16317
16256
|
const text = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
16318
16257
|
return text || void 0;
|
|
16319
16258
|
}
|
|
16320
|
-
|
|
16321
|
-
* Convert messages to Gemini format
|
|
16322
|
-
*/
|
|
16323
|
-
convertMessages(messages) {
|
|
16259
|
+
convertContents(messages) {
|
|
16324
16260
|
const toolNameByUseId = this.buildToolUseNameMap(messages);
|
|
16325
16261
|
const conversation = messages.filter((m) => m.role !== "system");
|
|
16326
|
-
const
|
|
16327
|
-
|
|
16328
|
-
for (let i = 0; i < conversation.length; i++) {
|
|
16329
|
-
const msg = conversation[i];
|
|
16330
|
-
const isLastMessage = i === conversation.length - 1;
|
|
16262
|
+
const contents = [];
|
|
16263
|
+
for (const msg of conversation) {
|
|
16331
16264
|
if (msg.role === "user") {
|
|
16332
16265
|
if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
|
|
16333
|
-
const
|
|
16266
|
+
const parts = [];
|
|
16334
16267
|
for (const block of msg.content) {
|
|
16335
16268
|
if (block.type === "tool_result") {
|
|
16336
16269
|
const toolResult = block;
|
|
16337
|
-
|
|
16270
|
+
parts.push({
|
|
16338
16271
|
functionResponse: {
|
|
16339
|
-
|
|
16340
|
-
// Recover it from prior assistant tool_use blocks when possible.
|
|
16272
|
+
id: toolResult.tool_use_id,
|
|
16341
16273
|
name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
|
|
16342
16274
|
response: { result: toolResult.content }
|
|
16343
16275
|
}
|
|
16344
16276
|
});
|
|
16345
16277
|
}
|
|
16346
16278
|
}
|
|
16347
|
-
|
|
16348
|
-
if (isLastMessage) {
|
|
16349
|
-
lastUserMessage = "";
|
|
16350
|
-
}
|
|
16279
|
+
contents.push({ role: "user", parts });
|
|
16351
16280
|
} else {
|
|
16352
|
-
|
|
16353
|
-
if (isLastMessage) {
|
|
16354
|
-
lastUserMessage = parts;
|
|
16355
|
-
} else {
|
|
16356
|
-
history.push({ role: "user", parts });
|
|
16357
|
-
}
|
|
16281
|
+
contents.push({ role: "user", parts: this.convertContent(msg.content) });
|
|
16358
16282
|
}
|
|
16359
16283
|
} else if (msg.role === "assistant") {
|
|
16360
|
-
|
|
16361
|
-
history.push({ role: "model", parts });
|
|
16284
|
+
contents.push({ role: "model", parts: this.convertContent(msg.content) });
|
|
16362
16285
|
}
|
|
16363
16286
|
}
|
|
16364
|
-
return {
|
|
16287
|
+
return contents.length > 0 ? contents : [{ role: "user", parts: [{ text: "" }] }];
|
|
16365
16288
|
}
|
|
16366
|
-
/**
|
|
16367
|
-
* Build a map from tool_use IDs to function names from assistant history.
|
|
16368
|
-
*/
|
|
16369
16289
|
buildToolUseNameMap(messages) {
|
|
16370
16290
|
const map = /* @__PURE__ */ new Map();
|
|
16371
16291
|
for (const msg of messages) {
|
|
@@ -16378,9 +16298,6 @@ var GeminiProvider = class {
|
|
|
16378
16298
|
}
|
|
16379
16299
|
return map;
|
|
16380
16300
|
}
|
|
16381
|
-
/**
|
|
16382
|
-
* Convert content to Gemini parts
|
|
16383
|
-
*/
|
|
16384
16301
|
convertContent(content) {
|
|
16385
16302
|
if (typeof content === "string") {
|
|
16386
16303
|
return [{ text: content }];
|
|
@@ -16390,27 +16307,26 @@ var GeminiProvider = class {
|
|
|
16390
16307
|
if (block.type === "text") {
|
|
16391
16308
|
parts.push({ text: block.text });
|
|
16392
16309
|
} else if (block.type === "image") {
|
|
16393
|
-
const
|
|
16310
|
+
const image = block;
|
|
16394
16311
|
parts.push({
|
|
16395
16312
|
inlineData: {
|
|
16396
|
-
data:
|
|
16397
|
-
mimeType:
|
|
16313
|
+
data: image.source.data,
|
|
16314
|
+
mimeType: image.source.media_type
|
|
16398
16315
|
}
|
|
16399
16316
|
});
|
|
16400
16317
|
} else if (block.type === "tool_use") {
|
|
16318
|
+
const toolUse = block;
|
|
16401
16319
|
parts.push({
|
|
16402
16320
|
functionCall: {
|
|
16403
|
-
|
|
16404
|
-
|
|
16321
|
+
id: toolUse.id,
|
|
16322
|
+
name: toolUse.name,
|
|
16323
|
+
args: toolUse.input
|
|
16405
16324
|
}
|
|
16406
16325
|
});
|
|
16407
16326
|
}
|
|
16408
16327
|
}
|
|
16409
16328
|
return parts.length > 0 ? parts : [{ text: "" }];
|
|
16410
16329
|
}
|
|
16411
|
-
/**
|
|
16412
|
-
* Convert tools to Gemini format
|
|
16413
|
-
*/
|
|
16414
16330
|
convertTools(tools) {
|
|
16415
16331
|
return tools.map((tool) => ({
|
|
16416
16332
|
name: tool.name,
|
|
@@ -16418,72 +16334,58 @@ var GeminiProvider = class {
|
|
|
16418
16334
|
parameters: tool.input_schema
|
|
16419
16335
|
}));
|
|
16420
16336
|
}
|
|
16421
|
-
/**
|
|
16422
|
-
* Convert tool choice to Gemini format
|
|
16423
|
-
*/
|
|
16424
16337
|
convertToolChoice(choice) {
|
|
16425
|
-
if (!choice || choice === "auto")
|
|
16426
|
-
|
|
16427
|
-
|
|
16338
|
+
if (!choice || choice === "auto") {
|
|
16339
|
+
return { mode: FunctionCallingConfigMode.AUTO };
|
|
16340
|
+
}
|
|
16341
|
+
if (choice === "any") {
|
|
16342
|
+
return { mode: FunctionCallingConfigMode.ANY };
|
|
16343
|
+
}
|
|
16344
|
+
return {
|
|
16345
|
+
mode: FunctionCallingConfigMode.ANY,
|
|
16346
|
+
allowedFunctionNames: [choice.name]
|
|
16347
|
+
};
|
|
16428
16348
|
}
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
const
|
|
16434
|
-
const
|
|
16349
|
+
extractFunctionCalls(response) {
|
|
16350
|
+
if (response.functionCalls && response.functionCalls.length > 0) {
|
|
16351
|
+
return response.functionCalls;
|
|
16352
|
+
}
|
|
16353
|
+
const candidate = response.candidates?.[0];
|
|
16354
|
+
const parts = candidate?.content?.parts ?? [];
|
|
16355
|
+
return parts.filter((part) => !!part.functionCall).map((part) => part.functionCall).filter(Boolean);
|
|
16356
|
+
}
|
|
16357
|
+
parseResponse(response, model) {
|
|
16435
16358
|
const usage = response.usageMetadata;
|
|
16436
16359
|
return {
|
|
16437
16360
|
id: `gemini-${Date.now()}`,
|
|
16438
|
-
content: text,
|
|
16361
|
+
content: response.text ?? "",
|
|
16439
16362
|
stopReason: this.mapFinishReason(response.candidates?.[0]?.finishReason),
|
|
16440
16363
|
usage: {
|
|
16441
16364
|
inputTokens: usage?.promptTokenCount ?? 0,
|
|
16442
16365
|
outputTokens: usage?.candidatesTokenCount ?? 0
|
|
16443
16366
|
},
|
|
16444
|
-
model: this.
|
|
16367
|
+
model: this.getModel(model)
|
|
16445
16368
|
};
|
|
16446
16369
|
}
|
|
16447
|
-
|
|
16448
|
-
* Parse response with tool calls from Gemini
|
|
16449
|
-
*/
|
|
16450
|
-
parseResponseWithTools(result) {
|
|
16451
|
-
const response = result.response;
|
|
16452
|
-
const candidate = response.candidates?.[0];
|
|
16370
|
+
parseResponseWithTools(response, model) {
|
|
16453
16371
|
const usage = response.usageMetadata;
|
|
16454
|
-
|
|
16455
|
-
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
|
|
16459
|
-
if ("text" in part && part.text) {
|
|
16460
|
-
textContent += part.text;
|
|
16461
|
-
}
|
|
16462
|
-
if ("functionCall" in part && part.functionCall) {
|
|
16463
|
-
toolIndex++;
|
|
16464
|
-
toolCalls.push({
|
|
16465
|
-
id: `gemini_call_${toolIndex}`,
|
|
16466
|
-
name: part.functionCall.name,
|
|
16467
|
-
input: part.functionCall.args ?? {}
|
|
16468
|
-
});
|
|
16469
|
-
}
|
|
16470
|
-
}
|
|
16471
|
-
}
|
|
16372
|
+
const toolCalls = this.extractFunctionCalls(response).map((functionCall, index) => ({
|
|
16373
|
+
id: functionCall.id ?? `gemini_call_${index + 1}`,
|
|
16374
|
+
name: functionCall.name ?? "unknown_function",
|
|
16375
|
+
input: functionCall.args ?? {}
|
|
16376
|
+
}));
|
|
16472
16377
|
return {
|
|
16473
16378
|
id: `gemini-${Date.now()}`,
|
|
16474
|
-
content:
|
|
16475
|
-
stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(
|
|
16379
|
+
content: response.text ?? "",
|
|
16380
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(response.candidates?.[0]?.finishReason),
|
|
16476
16381
|
usage: {
|
|
16477
16382
|
inputTokens: usage?.promptTokenCount ?? 0,
|
|
16478
16383
|
outputTokens: usage?.candidatesTokenCount ?? 0
|
|
16479
16384
|
},
|
|
16480
|
-
model: this.
|
|
16385
|
+
model: this.getModel(model),
|
|
16481
16386
|
toolCalls
|
|
16482
16387
|
};
|
|
16483
16388
|
}
|
|
16484
|
-
/**
|
|
16485
|
-
* Map finish reason to our format
|
|
16486
|
-
*/
|
|
16487
16389
|
mapFinishReason(reason) {
|
|
16488
16390
|
switch (reason) {
|
|
16489
16391
|
case "STOP":
|
|
@@ -16498,9 +16400,6 @@ var GeminiProvider = class {
|
|
|
16498
16400
|
return "end_turn";
|
|
16499
16401
|
}
|
|
16500
16402
|
}
|
|
16501
|
-
/**
|
|
16502
|
-
* Handle API errors
|
|
16503
|
-
*/
|
|
16504
16403
|
handleError(error) {
|
|
16505
16404
|
const message = error instanceof Error ? error.message : String(error);
|
|
16506
16405
|
const msg = message.toLowerCase();
|
|
@@ -16519,6 +16418,422 @@ var GeminiProvider = class {
|
|
|
16519
16418
|
}
|
|
16520
16419
|
};
|
|
16521
16420
|
|
|
16421
|
+
// src/providers/vertex.ts
|
|
16422
|
+
init_errors();
|
|
16423
|
+
init_gcloud();
|
|
16424
|
+
var DEFAULT_MODEL6 = "gemini-2.5-pro";
|
|
16425
|
+
var DEFAULT_BASE_URL = "https://aiplatform.googleapis.com/v1";
|
|
16426
|
+
var DEFAULT_LOCATION = "global";
|
|
16427
|
+
var CONTEXT_WINDOWS6 = {
|
|
16428
|
+
"gemini-2.5-pro": 1048576,
|
|
16429
|
+
"gemini-2.5-flash": 1048576,
|
|
16430
|
+
"gemini-2.5-flash-lite": 1048576,
|
|
16431
|
+
"gemini-2.0-flash-001": 1048576,
|
|
16432
|
+
"gemini-2.0-flash-lite-001": 1048576
|
|
16433
|
+
};
|
|
16434
|
+
var VertexProvider = class {
|
|
16435
|
+
id = "vertex";
|
|
16436
|
+
name = "Google Vertex AI Gemini";
|
|
16437
|
+
config = {};
|
|
16438
|
+
project = "";
|
|
16439
|
+
location = DEFAULT_LOCATION;
|
|
16440
|
+
retryConfig = DEFAULT_RETRY_CONFIG;
|
|
16441
|
+
async initialize(config) {
|
|
16442
|
+
this.config = config;
|
|
16443
|
+
this.project = config.project ?? process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"] ?? "";
|
|
16444
|
+
this.location = config.location ?? process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"] ?? DEFAULT_LOCATION;
|
|
16445
|
+
if (!this.project.trim()) {
|
|
16446
|
+
throw new ProviderError(
|
|
16447
|
+
"Vertex AI project not configured. Set provider.project, VERTEX_PROJECT, or GOOGLE_CLOUD_PROJECT.",
|
|
16448
|
+
{ provider: this.id }
|
|
16449
|
+
);
|
|
16450
|
+
}
|
|
16451
|
+
const token = await getCachedADCToken();
|
|
16452
|
+
if (!token) {
|
|
16453
|
+
throw new ProviderError(
|
|
16454
|
+
"Vertex AI ADC is not configured. Run `gcloud auth application-default login` manually, then retry.",
|
|
16455
|
+
{ provider: this.id }
|
|
16456
|
+
);
|
|
16457
|
+
}
|
|
16458
|
+
}
|
|
16459
|
+
async chat(messages, options) {
|
|
16460
|
+
this.ensureInitialized();
|
|
16461
|
+
return withRetry(async () => {
|
|
16462
|
+
const response = await this.generateContent(messages, options);
|
|
16463
|
+
return this.parseResponse(response, options?.model);
|
|
16464
|
+
}, this.retryConfig);
|
|
16465
|
+
}
|
|
16466
|
+
async chatWithTools(messages, options) {
|
|
16467
|
+
this.ensureInitialized();
|
|
16468
|
+
return withRetry(async () => {
|
|
16469
|
+
const response = await this.generateContent(
|
|
16470
|
+
messages,
|
|
16471
|
+
options,
|
|
16472
|
+
options.tools,
|
|
16473
|
+
options.toolChoice
|
|
16474
|
+
);
|
|
16475
|
+
return this.parseResponseWithTools(response, options.model);
|
|
16476
|
+
}, this.retryConfig);
|
|
16477
|
+
}
|
|
16478
|
+
async *stream(messages, options) {
|
|
16479
|
+
this.ensureInitialized();
|
|
16480
|
+
const stream = await this.streamGenerateContent(messages, options);
|
|
16481
|
+
let stopReason = "end_turn";
|
|
16482
|
+
for await (const chunk of stream) {
|
|
16483
|
+
const candidate = chunk.candidates?.[0];
|
|
16484
|
+
const parts = candidate?.content?.parts ?? [];
|
|
16485
|
+
for (const part of parts) {
|
|
16486
|
+
if (part.text) {
|
|
16487
|
+
yield { type: "text", text: part.text };
|
|
16488
|
+
}
|
|
16489
|
+
}
|
|
16490
|
+
stopReason = this.mapFinishReason(candidate?.finishReason);
|
|
16491
|
+
}
|
|
16492
|
+
yield { type: "done", stopReason };
|
|
16493
|
+
}
|
|
16494
|
+
async *streamWithTools(messages, options) {
|
|
16495
|
+
this.ensureInitialized();
|
|
16496
|
+
const stream = await this.streamGenerateContent(
|
|
16497
|
+
messages,
|
|
16498
|
+
options,
|
|
16499
|
+
options.tools,
|
|
16500
|
+
options.toolChoice
|
|
16501
|
+
);
|
|
16502
|
+
let stopReason = "end_turn";
|
|
16503
|
+
let streamToolCallCounter = 0;
|
|
16504
|
+
for await (const chunk of stream) {
|
|
16505
|
+
const candidate = chunk.candidates?.[0];
|
|
16506
|
+
const parts = candidate?.content?.parts ?? [];
|
|
16507
|
+
for (const part of parts) {
|
|
16508
|
+
if (part.text) {
|
|
16509
|
+
yield { type: "text", text: part.text };
|
|
16510
|
+
}
|
|
16511
|
+
if (part.functionCall) {
|
|
16512
|
+
streamToolCallCounter++;
|
|
16513
|
+
yield {
|
|
16514
|
+
type: "tool_use_start",
|
|
16515
|
+
toolCall: {
|
|
16516
|
+
id: `vertex_call_${streamToolCallCounter}`,
|
|
16517
|
+
name: part.functionCall.name,
|
|
16518
|
+
input: part.functionCall.args ?? {}
|
|
16519
|
+
}
|
|
16520
|
+
};
|
|
16521
|
+
yield {
|
|
16522
|
+
type: "tool_use_end",
|
|
16523
|
+
toolCall: {
|
|
16524
|
+
id: `vertex_call_${streamToolCallCounter}`,
|
|
16525
|
+
name: part.functionCall.name,
|
|
16526
|
+
input: part.functionCall.args ?? {}
|
|
16527
|
+
}
|
|
16528
|
+
};
|
|
16529
|
+
}
|
|
16530
|
+
}
|
|
16531
|
+
stopReason = parts.some((part) => part.functionCall) ? "tool_use" : this.mapFinishReason(candidate?.finishReason);
|
|
16532
|
+
}
|
|
16533
|
+
yield { type: "done", stopReason };
|
|
16534
|
+
}
|
|
16535
|
+
countTokens(text) {
|
|
16536
|
+
return Math.ceil(text.length / 4);
|
|
16537
|
+
}
|
|
16538
|
+
getContextWindow() {
|
|
16539
|
+
return CONTEXT_WINDOWS6[this.config.model ?? DEFAULT_MODEL6] ?? 1048576;
|
|
16540
|
+
}
|
|
16541
|
+
async isAvailable() {
|
|
16542
|
+
try {
|
|
16543
|
+
await this.generateContent([{ role: "user", content: "hi" }], { maxTokens: 8 });
|
|
16544
|
+
return true;
|
|
16545
|
+
} catch {
|
|
16546
|
+
return false;
|
|
16547
|
+
}
|
|
16548
|
+
}
|
|
16549
|
+
ensureInitialized() {
|
|
16550
|
+
if (!this.project) {
|
|
16551
|
+
throw new ProviderError("Provider not initialized. Call initialize() first.", {
|
|
16552
|
+
provider: this.id
|
|
16553
|
+
});
|
|
16554
|
+
}
|
|
16555
|
+
}
|
|
16556
|
+
getModel(model) {
|
|
16557
|
+
return model ?? this.config.model ?? DEFAULT_MODEL6;
|
|
16558
|
+
}
|
|
16559
|
+
getResolvedBaseUrl() {
|
|
16560
|
+
if (this.config.baseUrl && this.config.baseUrl.trim()) {
|
|
16561
|
+
return this.config.baseUrl;
|
|
16562
|
+
}
|
|
16563
|
+
if (this.location === DEFAULT_LOCATION) {
|
|
16564
|
+
return DEFAULT_BASE_URL;
|
|
16565
|
+
}
|
|
16566
|
+
return `https://${encodeURIComponent(this.location)}-aiplatform.googleapis.com/v1`;
|
|
16567
|
+
}
|
|
16568
|
+
buildEndpoint(model, stream = false) {
|
|
16569
|
+
const action = stream ? "streamGenerateContent?alt=sse" : "generateContent";
|
|
16570
|
+
return `${this.getResolvedBaseUrl()}/projects/${encodeURIComponent(this.project)}/locations/${encodeURIComponent(this.location)}/publishers/google/models/${encodeURIComponent(this.getModel(model))}:${action}`;
|
|
16571
|
+
}
|
|
16572
|
+
async getHeaders() {
|
|
16573
|
+
const token = await getCachedADCToken();
|
|
16574
|
+
if (!token) {
|
|
16575
|
+
throw new ProviderError(
|
|
16576
|
+
"Vertex AI ADC token is unavailable. Re-authenticate with gcloud and retry.",
|
|
16577
|
+
{ provider: this.id }
|
|
16578
|
+
);
|
|
16579
|
+
}
|
|
16580
|
+
return {
|
|
16581
|
+
"Content-Type": "application/json",
|
|
16582
|
+
Authorization: `Bearer ${token.accessToken}`,
|
|
16583
|
+
"x-goog-user-project": this.project
|
|
16584
|
+
};
|
|
16585
|
+
}
|
|
16586
|
+
extractSystem(messages, optionsSystem) {
|
|
16587
|
+
if (optionsSystem !== void 0) return optionsSystem;
|
|
16588
|
+
const systemMsg = messages.find((m) => m.role === "system");
|
|
16589
|
+
if (!systemMsg) return void 0;
|
|
16590
|
+
if (typeof systemMsg.content === "string") return systemMsg.content;
|
|
16591
|
+
const text = systemMsg.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
16592
|
+
return text || void 0;
|
|
16593
|
+
}
|
|
16594
|
+
buildToolUseNameMap(messages) {
|
|
16595
|
+
const map = /* @__PURE__ */ new Map();
|
|
16596
|
+
for (const msg of messages) {
|
|
16597
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
16598
|
+
for (const block of msg.content) {
|
|
16599
|
+
if (block.type === "tool_use") {
|
|
16600
|
+
map.set(block.id, block.name);
|
|
16601
|
+
}
|
|
16602
|
+
}
|
|
16603
|
+
}
|
|
16604
|
+
return map;
|
|
16605
|
+
}
|
|
16606
|
+
convertContents(messages) {
|
|
16607
|
+
const toolNameByUseId = this.buildToolUseNameMap(messages);
|
|
16608
|
+
const conversation = messages.filter((m) => m.role !== "system");
|
|
16609
|
+
const contents = [];
|
|
16610
|
+
for (let i = 0; i < conversation.length; i++) {
|
|
16611
|
+
const msg = conversation[i];
|
|
16612
|
+
if (msg.role === "user") {
|
|
16613
|
+
if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
|
|
16614
|
+
const functionResponses = [];
|
|
16615
|
+
for (const block of msg.content) {
|
|
16616
|
+
if (block.type === "tool_result") {
|
|
16617
|
+
const toolResult = block;
|
|
16618
|
+
functionResponses.push({
|
|
16619
|
+
functionResponse: {
|
|
16620
|
+
name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
|
|
16621
|
+
response: { result: toolResult.content }
|
|
16622
|
+
}
|
|
16623
|
+
});
|
|
16624
|
+
}
|
|
16625
|
+
}
|
|
16626
|
+
contents.push({ role: "user", parts: functionResponses });
|
|
16627
|
+
} else {
|
|
16628
|
+
contents.push({ role: "user", parts: this.convertContent(msg.content) });
|
|
16629
|
+
}
|
|
16630
|
+
} else if (msg.role === "assistant") {
|
|
16631
|
+
contents.push({ role: "model", parts: this.convertContent(msg.content) });
|
|
16632
|
+
}
|
|
16633
|
+
}
|
|
16634
|
+
return contents.length > 0 ? contents : [{ role: "user", parts: [{ text: "" }] }];
|
|
16635
|
+
}
|
|
16636
|
+
convertContent(content) {
|
|
16637
|
+
if (typeof content === "string") return [{ text: content }];
|
|
16638
|
+
const parts = [];
|
|
16639
|
+
for (const block of content) {
|
|
16640
|
+
if (block.type === "text") {
|
|
16641
|
+
parts.push({ text: block.text });
|
|
16642
|
+
} else if (block.type === "image") {
|
|
16643
|
+
const image = block;
|
|
16644
|
+
parts.push({
|
|
16645
|
+
inlineData: {
|
|
16646
|
+
data: image.source.data,
|
|
16647
|
+
mimeType: image.source.media_type
|
|
16648
|
+
}
|
|
16649
|
+
});
|
|
16650
|
+
} else if (block.type === "tool_use") {
|
|
16651
|
+
const toolUse = block;
|
|
16652
|
+
parts.push({
|
|
16653
|
+
functionCall: {
|
|
16654
|
+
name: toolUse.name,
|
|
16655
|
+
args: toolUse.input
|
|
16656
|
+
}
|
|
16657
|
+
});
|
|
16658
|
+
}
|
|
16659
|
+
}
|
|
16660
|
+
return parts.length > 0 ? parts : [{ text: "" }];
|
|
16661
|
+
}
|
|
16662
|
+
convertTools(tools) {
|
|
16663
|
+
return [
|
|
16664
|
+
{
|
|
16665
|
+
functionDeclarations: tools.map((tool) => ({
|
|
16666
|
+
name: tool.name,
|
|
16667
|
+
description: tool.description,
|
|
16668
|
+
parameters: tool.input_schema
|
|
16669
|
+
}))
|
|
16670
|
+
}
|
|
16671
|
+
];
|
|
16672
|
+
}
|
|
16673
|
+
convertToolChoice(choice) {
|
|
16674
|
+
if (!choice || choice === "auto") {
|
|
16675
|
+
return { functionCallingConfig: { mode: "AUTO" } };
|
|
16676
|
+
}
|
|
16677
|
+
if (choice === "any") {
|
|
16678
|
+
return { functionCallingConfig: { mode: "ANY" } };
|
|
16679
|
+
}
|
|
16680
|
+
return { functionCallingConfig: { mode: "ANY", allowedFunctionNames: [choice.name] } };
|
|
16681
|
+
}
|
|
16682
|
+
buildRequestBody(messages, options, tools, toolChoice) {
|
|
16683
|
+
const body = {
|
|
16684
|
+
contents: this.convertContents(messages),
|
|
16685
|
+
generationConfig: {
|
|
16686
|
+
maxOutputTokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
16687
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0,
|
|
16688
|
+
stopSequences: options?.stopSequences
|
|
16689
|
+
}
|
|
16690
|
+
};
|
|
16691
|
+
const systemInstruction = this.extractSystem(messages, options?.system);
|
|
16692
|
+
if (systemInstruction) {
|
|
16693
|
+
body["systemInstruction"] = {
|
|
16694
|
+
parts: [{ text: systemInstruction }]
|
|
16695
|
+
};
|
|
16696
|
+
}
|
|
16697
|
+
if (tools && tools.length > 0) {
|
|
16698
|
+
body["tools"] = this.convertTools(tools);
|
|
16699
|
+
const convertedChoice = this.convertToolChoice(toolChoice);
|
|
16700
|
+
if (convertedChoice) {
|
|
16701
|
+
body["toolConfig"] = convertedChoice;
|
|
16702
|
+
}
|
|
16703
|
+
}
|
|
16704
|
+
return body;
|
|
16705
|
+
}
|
|
16706
|
+
async generateContent(messages, options, tools, toolChoice) {
|
|
16707
|
+
const response = await fetch(this.buildEndpoint(options?.model), {
|
|
16708
|
+
method: "POST",
|
|
16709
|
+
headers: await this.getHeaders(),
|
|
16710
|
+
body: JSON.stringify(this.buildRequestBody(messages, options, tools, toolChoice)),
|
|
16711
|
+
signal: options?.signal
|
|
16712
|
+
});
|
|
16713
|
+
if (!response.ok) {
|
|
16714
|
+
throw await this.buildHttpError(response);
|
|
16715
|
+
}
|
|
16716
|
+
const data = await response.json();
|
|
16717
|
+
if (data.error?.message) {
|
|
16718
|
+
throw new ProviderError(data.error.message, {
|
|
16719
|
+
provider: this.id,
|
|
16720
|
+
statusCode: data.error.code
|
|
16721
|
+
});
|
|
16722
|
+
}
|
|
16723
|
+
return data;
|
|
16724
|
+
}
|
|
16725
|
+
async *streamGenerateContent(messages, options, tools, toolChoice) {
|
|
16726
|
+
const response = await fetch(this.buildEndpoint(options?.model, true), {
|
|
16727
|
+
method: "POST",
|
|
16728
|
+
headers: await this.getHeaders(),
|
|
16729
|
+
body: JSON.stringify(this.buildRequestBody(messages, options, tools, toolChoice)),
|
|
16730
|
+
signal: options?.signal
|
|
16731
|
+
});
|
|
16732
|
+
if (!response.ok) {
|
|
16733
|
+
throw await this.buildHttpError(response);
|
|
16734
|
+
}
|
|
16735
|
+
if (!response.body) {
|
|
16736
|
+
throw new ProviderError("Vertex AI streaming response body is empty.", {
|
|
16737
|
+
provider: this.id
|
|
16738
|
+
});
|
|
16739
|
+
}
|
|
16740
|
+
const reader = response.body.getReader();
|
|
16741
|
+
const decoder = new TextDecoder();
|
|
16742
|
+
let buffer = "";
|
|
16743
|
+
while (true) {
|
|
16744
|
+
const { value, done } = await reader.read();
|
|
16745
|
+
if (done) break;
|
|
16746
|
+
buffer += decoder.decode(value, { stream: true });
|
|
16747
|
+
while (true) {
|
|
16748
|
+
const eventBoundary = buffer.indexOf("\n\n");
|
|
16749
|
+
if (eventBoundary === -1) break;
|
|
16750
|
+
const rawEvent = buffer.slice(0, eventBoundary);
|
|
16751
|
+
buffer = buffer.slice(eventBoundary + 2);
|
|
16752
|
+
const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
|
|
16753
|
+
for (const line of dataLines) {
|
|
16754
|
+
if (line === "[DONE]") return;
|
|
16755
|
+
yield JSON.parse(line);
|
|
16756
|
+
}
|
|
16757
|
+
}
|
|
16758
|
+
}
|
|
16759
|
+
const trailing = buffer.trim();
|
|
16760
|
+
if (trailing.startsWith("data:")) {
|
|
16761
|
+
const line = trailing.slice(5).trim();
|
|
16762
|
+
if (line && line !== "[DONE]") {
|
|
16763
|
+
yield JSON.parse(line);
|
|
16764
|
+
}
|
|
16765
|
+
}
|
|
16766
|
+
}
|
|
16767
|
+
parseResponse(response, model) {
|
|
16768
|
+
const candidate = response.candidates?.[0];
|
|
16769
|
+
const text = (candidate?.content?.parts ?? []).filter((part) => part.text).map((part) => part.text).join("");
|
|
16770
|
+
return {
|
|
16771
|
+
id: `vertex-${Date.now()}`,
|
|
16772
|
+
content: text,
|
|
16773
|
+
stopReason: this.mapFinishReason(candidate?.finishReason),
|
|
16774
|
+
usage: {
|
|
16775
|
+
inputTokens: response.usageMetadata?.promptTokenCount ?? 0,
|
|
16776
|
+
outputTokens: response.usageMetadata?.candidatesTokenCount ?? 0
|
|
16777
|
+
},
|
|
16778
|
+
model: this.getModel(model)
|
|
16779
|
+
};
|
|
16780
|
+
}
|
|
16781
|
+
parseResponseWithTools(response, model) {
|
|
16782
|
+
const candidate = response.candidates?.[0];
|
|
16783
|
+
const parts = candidate?.content?.parts ?? [];
|
|
16784
|
+
const toolCalls = [];
|
|
16785
|
+
let textContent = "";
|
|
16786
|
+
let toolIndex = 0;
|
|
16787
|
+
for (const part of parts) {
|
|
16788
|
+
if (part.text) {
|
|
16789
|
+
textContent += part.text;
|
|
16790
|
+
}
|
|
16791
|
+
if (part.functionCall) {
|
|
16792
|
+
toolIndex++;
|
|
16793
|
+
toolCalls.push({
|
|
16794
|
+
id: `vertex_call_${toolIndex}`,
|
|
16795
|
+
name: part.functionCall.name,
|
|
16796
|
+
input: part.functionCall.args ?? {}
|
|
16797
|
+
});
|
|
16798
|
+
}
|
|
16799
|
+
}
|
|
16800
|
+
return {
|
|
16801
|
+
id: `vertex-${Date.now()}`,
|
|
16802
|
+
content: textContent,
|
|
16803
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : this.mapFinishReason(candidate?.finishReason),
|
|
16804
|
+
usage: {
|
|
16805
|
+
inputTokens: response.usageMetadata?.promptTokenCount ?? 0,
|
|
16806
|
+
outputTokens: response.usageMetadata?.candidatesTokenCount ?? 0
|
|
16807
|
+
},
|
|
16808
|
+
model: this.getModel(model),
|
|
16809
|
+
toolCalls
|
|
16810
|
+
};
|
|
16811
|
+
}
|
|
16812
|
+
mapFinishReason(reason) {
|
|
16813
|
+
switch (reason) {
|
|
16814
|
+
case "STOP":
|
|
16815
|
+
return "end_turn";
|
|
16816
|
+
case "MAX_TOKENS":
|
|
16817
|
+
return "max_tokens";
|
|
16818
|
+
case "SAFETY":
|
|
16819
|
+
case "RECITATION":
|
|
16820
|
+
case "OTHER":
|
|
16821
|
+
return "stop_sequence";
|
|
16822
|
+
default:
|
|
16823
|
+
return "end_turn";
|
|
16824
|
+
}
|
|
16825
|
+
}
|
|
16826
|
+
async buildHttpError(response) {
|
|
16827
|
+
const body = await response.text();
|
|
16828
|
+
const retryable = response.status === 429 || response.status >= 500;
|
|
16829
|
+
return new ProviderError(`Vertex AI error: ${response.status} - ${body}`, {
|
|
16830
|
+
provider: this.id,
|
|
16831
|
+
statusCode: response.status,
|
|
16832
|
+
retryable
|
|
16833
|
+
});
|
|
16834
|
+
}
|
|
16835
|
+
};
|
|
16836
|
+
|
|
16522
16837
|
// src/providers/circuit-breaker.ts
|
|
16523
16838
|
init_errors();
|
|
16524
16839
|
var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
@@ -16820,6 +17135,11 @@ function normalizeProviderModel(model) {
|
|
|
16820
17135
|
const trimmed = model.trim();
|
|
16821
17136
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
16822
17137
|
}
|
|
17138
|
+
function normalizeOptional(value) {
|
|
17139
|
+
if (typeof value !== "string") return void 0;
|
|
17140
|
+
const trimmed = value.trim();
|
|
17141
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
17142
|
+
}
|
|
16823
17143
|
async function createProvider(type, config = {}) {
|
|
16824
17144
|
let provider;
|
|
16825
17145
|
const mergedConfig = {
|
|
@@ -16828,7 +17148,11 @@ async function createProvider(type, config = {}) {
|
|
|
16828
17148
|
model: normalizeProviderModel(config.model) ?? getDefaultModel(type),
|
|
16829
17149
|
maxTokens: config.maxTokens,
|
|
16830
17150
|
temperature: config.temperature,
|
|
16831
|
-
timeout: config.timeout
|
|
17151
|
+
timeout: config.timeout,
|
|
17152
|
+
project: normalizeOptional(config.project) ?? (type === "vertex" ? normalizeOptional(
|
|
17153
|
+
process.env["VERTEX_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"] ?? process.env["GCLOUD_PROJECT"]
|
|
17154
|
+
) : void 0),
|
|
17155
|
+
location: normalizeOptional(config.location) ?? (type === "vertex" ? normalizeOptional(process.env["VERTEX_LOCATION"] ?? process.env["GOOGLE_CLOUD_LOCATION"]) : void 0)
|
|
16832
17156
|
};
|
|
16833
17157
|
switch (type) {
|
|
16834
17158
|
case "anthropic":
|
|
@@ -16846,6 +17170,9 @@ async function createProvider(type, config = {}) {
|
|
|
16846
17170
|
case "gemini":
|
|
16847
17171
|
provider = new GeminiProvider();
|
|
16848
17172
|
break;
|
|
17173
|
+
case "vertex":
|
|
17174
|
+
provider = new VertexProvider();
|
|
17175
|
+
break;
|
|
16849
17176
|
case "kimi":
|
|
16850
17177
|
provider = createKimiProvider(mergedConfig);
|
|
16851
17178
|
break;
|
|
@@ -24902,7 +25229,7 @@ Examples:
|
|
|
24902
25229
|
description = response.choices[0]?.message?.content ?? "No description generated";
|
|
24903
25230
|
} else if (selectedProvider === "gemini") {
|
|
24904
25231
|
model = "gemini-2.0-flash";
|
|
24905
|
-
const {
|
|
25232
|
+
const { GoogleGenAI: GoogleGenAI2 } = await import('@google/genai');
|
|
24906
25233
|
const apiKey = process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY;
|
|
24907
25234
|
if (!apiKey) {
|
|
24908
25235
|
throw new ToolError(
|
|
@@ -24910,18 +25237,25 @@ Examples:
|
|
|
24910
25237
|
{ tool: "read_image" }
|
|
24911
25238
|
);
|
|
24912
25239
|
}
|
|
24913
|
-
const genAI = new
|
|
24914
|
-
const
|
|
24915
|
-
|
|
24916
|
-
|
|
24917
|
-
|
|
24918
|
-
|
|
24919
|
-
|
|
24920
|
-
|
|
25240
|
+
const genAI = new GoogleGenAI2({ apiKey });
|
|
25241
|
+
const result = await genAI.models.generateContent({
|
|
25242
|
+
model,
|
|
25243
|
+
contents: [
|
|
25244
|
+
{
|
|
25245
|
+
role: "user",
|
|
25246
|
+
parts: [
|
|
25247
|
+
{ text: effectivePrompt },
|
|
25248
|
+
{
|
|
25249
|
+
inlineData: {
|
|
25250
|
+
data: base64,
|
|
25251
|
+
mimeType
|
|
25252
|
+
}
|
|
25253
|
+
}
|
|
25254
|
+
]
|
|
24921
25255
|
}
|
|
24922
|
-
|
|
24923
|
-
|
|
24924
|
-
description = result.
|
|
25256
|
+
]
|
|
25257
|
+
});
|
|
25258
|
+
description = result.text ?? "No description generated";
|
|
24925
25259
|
} else {
|
|
24926
25260
|
throw new ToolError(`Unsupported provider: ${selectedProvider}`, {
|
|
24927
25261
|
tool: "read_image"
|
|
@@ -24933,7 +25267,7 @@ Examples:
|
|
|
24933
25267
|
const pkgMap = {
|
|
24934
25268
|
anthropic: "@anthropic-ai/sdk",
|
|
24935
25269
|
openai: "openai",
|
|
24936
|
-
gemini: "@google/
|
|
25270
|
+
gemini: "@google/genai"
|
|
24937
25271
|
};
|
|
24938
25272
|
const pkg = pkgMap[selectedProvider] ?? selectedProvider;
|
|
24939
25273
|
throw new ToolError(`Provider SDK not installed. Run: pnpm add ${pkg}`, {
|