@prestyj/ai 4.3.201 → 4.3.207
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/index.cjs +503 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +503 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -78,6 +78,7 @@ var ProviderError = class extends EZCoderAIError {
|
|
|
78
78
|
var PROVIDER_DISPLAY = {
|
|
79
79
|
openai: "OpenAI",
|
|
80
80
|
anthropic: "Anthropic",
|
|
81
|
+
gemini: "Gemini",
|
|
81
82
|
glm: "Z.AI (GLM)",
|
|
82
83
|
moonshot: "Moonshot",
|
|
83
84
|
deepseek: "DeepSeek",
|
|
@@ -770,20 +771,6 @@ function normalizeOpenAIStopReason(reason) {
|
|
|
770
771
|
function isJsonObject(value) {
|
|
771
772
|
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
772
773
|
}
|
|
773
|
-
function uniqueToolsByName(tools) {
|
|
774
|
-
const seen = /* @__PURE__ */ new Set();
|
|
775
|
-
const unique = [];
|
|
776
|
-
for (const tool of tools) {
|
|
777
|
-
if (!tool.name) {
|
|
778
|
-
unique.push(tool);
|
|
779
|
-
continue;
|
|
780
|
-
}
|
|
781
|
-
if (seen.has(tool.name)) continue;
|
|
782
|
-
seen.add(tool.name);
|
|
783
|
-
unique.push(tool);
|
|
784
|
-
}
|
|
785
|
-
return unique;
|
|
786
|
-
}
|
|
787
774
|
function createClient(options) {
|
|
788
775
|
const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
|
|
789
776
|
return new import_sdk.default({
|
|
@@ -852,18 +839,18 @@ async function* runStream(options) {
|
|
|
852
839
|
if (name) reservedServerNames.add(name);
|
|
853
840
|
}
|
|
854
841
|
const clientTools = options.tools?.length ? toAnthropicTools(
|
|
855
|
-
|
|
842
|
+
options.tools.filter((t) => !reservedServerNames.has(t.name)),
|
|
856
843
|
{
|
|
857
844
|
...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
|
|
858
845
|
...supportsFirstPartyToolExtras ? { enableFineGrainedToolStreaming: true } : {}
|
|
859
846
|
}
|
|
860
847
|
) : [];
|
|
861
848
|
return {
|
|
862
|
-
tools:
|
|
849
|
+
tools: [
|
|
863
850
|
...clientTools,
|
|
864
851
|
...options.serverTools ?? [],
|
|
865
852
|
...options.webSearch ? [{ type: "web_search_20250305", name: "web_search" }] : []
|
|
866
|
-
]
|
|
853
|
+
]
|
|
867
854
|
};
|
|
868
855
|
})() : {},
|
|
869
856
|
...options.toolChoice && options.tools?.length ? { tool_choice: toAnthropicToolChoice(options.toolChoice) } : {},
|
|
@@ -1989,6 +1976,502 @@ function parseCodexErrorBody(text) {
|
|
|
1989
1976
|
}
|
|
1990
1977
|
}
|
|
1991
1978
|
|
|
1979
|
+
// src/providers/gemini.ts
|
|
1980
|
+
var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
|
|
1981
|
+
var CODE_ASSIST_API_VERSION = "v1internal";
|
|
1982
|
+
var GEMINI_CLI_USER_AGENT = "google-gemini-cli";
|
|
1983
|
+
var GEMINI_CLI_API_CLIENT = "gemini-cli/0.0.0";
|
|
1984
|
+
var CODE_ASSIST_NON_STREAMING_RETRIES = 3;
|
|
1985
|
+
var CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS = 1e3;
|
|
1986
|
+
var SYNTHETIC_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
|
|
1987
|
+
var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
|
|
1988
|
+
"gemini-3-pro-preview",
|
|
1989
|
+
"gemini-3.1-pro-preview",
|
|
1990
|
+
"gemini-3.1-pro-preview-customtools",
|
|
1991
|
+
"gemini-3-flash-preview",
|
|
1992
|
+
"gemini-3.1-flash-lite-preview",
|
|
1993
|
+
"gemini-2.5-pro",
|
|
1994
|
+
"gemini-2.5-flash",
|
|
1995
|
+
"gemini-2.5-flash-lite",
|
|
1996
|
+
"gemma-4-31b-it",
|
|
1997
|
+
"gemma-4-26b-a4b-it"
|
|
1998
|
+
]);
|
|
1999
|
+
function isJsonObject4(value) {
|
|
2000
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
2001
|
+
}
|
|
2002
|
+
function getEnvironment() {
|
|
2003
|
+
return globalThis.process?.env;
|
|
2004
|
+
}
|
|
2005
|
+
function getGoogleProject(options) {
|
|
2006
|
+
const env = getEnvironment();
|
|
2007
|
+
return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
|
|
2008
|
+
}
|
|
2009
|
+
function getCodeAssistEndpoint(method) {
|
|
2010
|
+
const env = getEnvironment();
|
|
2011
|
+
const endpoint = env?.CODE_ASSIST_ENDPOINT ?? DEFAULT_CODE_ASSIST_BASE_URL;
|
|
2012
|
+
const version = env?.CODE_ASSIST_API_VERSION || CODE_ASSIST_API_VERSION;
|
|
2013
|
+
return new URL(`${endpoint}/${version}:${method}`);
|
|
2014
|
+
}
|
|
2015
|
+
function formatUnsupportedModelMessage(model) {
|
|
2016
|
+
return `Gemini OAuth is configured to use the Gemini Code Assist subscription endpoint only. That endpoint does not currently expose model "${model}".`;
|
|
2017
|
+
}
|
|
2018
|
+
function formatErrorMessage(status, body, model) {
|
|
2019
|
+
if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
|
|
2020
|
+
return `Gemini API error (404): ${body}
|
|
2021
|
+
|
|
2022
|
+
${formatUnsupportedModelMessage(model)}`;
|
|
2023
|
+
}
|
|
2024
|
+
return `Gemini API error (${status}): ${body}`;
|
|
2025
|
+
}
|
|
2026
|
+
function toSystemAndContents(messages) {
|
|
2027
|
+
let systemText = "";
|
|
2028
|
+
const contents = [];
|
|
2029
|
+
const toolNamesById = /* @__PURE__ */ new Map();
|
|
2030
|
+
for (const msg of messages) {
|
|
2031
|
+
if (msg.role === "system") {
|
|
2032
|
+
systemText = systemText ? `${systemText}
|
|
2033
|
+
|
|
2034
|
+
${msg.content}` : msg.content;
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
if (msg.role === "user") {
|
|
2038
|
+
contents.push({
|
|
2039
|
+
role: "user",
|
|
2040
|
+
parts: typeof msg.content === "string" ? [{ text: msg.content }] : msg.content.map((part) => {
|
|
2041
|
+
if (part.type === "text") return { text: part.text };
|
|
2042
|
+
return { inlineData: { mimeType: part.mediaType, data: part.data } };
|
|
2043
|
+
})
|
|
2044
|
+
});
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
if (msg.role === "assistant") {
|
|
2048
|
+
const parts = [];
|
|
2049
|
+
const source = msg.content;
|
|
2050
|
+
if (typeof source === "string") {
|
|
2051
|
+
if (source) parts.push({ text: source });
|
|
2052
|
+
} else {
|
|
2053
|
+
for (const part of source) {
|
|
2054
|
+
if (part.type === "text" && part.text) {
|
|
2055
|
+
parts.push({ text: part.text });
|
|
2056
|
+
} else if (part.type === "thinking" && part.text) {
|
|
2057
|
+
parts.push({ text: part.text });
|
|
2058
|
+
} else if (part.type === "tool_call") {
|
|
2059
|
+
toolNamesById.set(part.id, part.name);
|
|
2060
|
+
parts.push({
|
|
2061
|
+
functionCall: { id: part.id, name: part.name, args: part.args },
|
|
2062
|
+
thoughtSignature: SYNTHETIC_THOUGHT_SIGNATURE
|
|
2063
|
+
});
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
if (parts.length > 0) contents.push({ role: "model", parts });
|
|
2068
|
+
continue;
|
|
2069
|
+
}
|
|
2070
|
+
if (msg.role === "tool") {
|
|
2071
|
+
const parts = [];
|
|
2072
|
+
for (const result of msg.content) {
|
|
2073
|
+
const name = toolNamesById.get(result.toolCallId) ?? result.toolCallId;
|
|
2074
|
+
const content = typeof result.content === "string" ? result.content : stringifyToolContent(result.content);
|
|
2075
|
+
parts.push({
|
|
2076
|
+
functionResponse: {
|
|
2077
|
+
id: result.toolCallId,
|
|
2078
|
+
name,
|
|
2079
|
+
response: {
|
|
2080
|
+
content,
|
|
2081
|
+
...result.isError ? { isError: true } : {}
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
if (parts.length > 0) contents.push({ role: "user", parts });
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
return {
|
|
2090
|
+
...systemText ? { systemInstruction: { parts: [{ text: systemText }] } } : {},
|
|
2091
|
+
contents
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
function stringifyToolContent(content) {
|
|
2095
|
+
return content.map((part) => part.type === "text" ? part.text : `[image ${part.mediaType}]`).join("\n");
|
|
2096
|
+
}
|
|
2097
|
+
function toGeminiTools(tools) {
|
|
2098
|
+
if (!tools?.length) return void 0;
|
|
2099
|
+
return [
|
|
2100
|
+
{
|
|
2101
|
+
functionDeclarations: tools.map((tool) => ({
|
|
2102
|
+
name: tool.name,
|
|
2103
|
+
description: tool.description,
|
|
2104
|
+
parameters: sanitizeSchema(tool.rawInputSchema ?? zodToJsonSchema(tool.parameters))
|
|
2105
|
+
}))
|
|
2106
|
+
}
|
|
2107
|
+
];
|
|
2108
|
+
}
|
|
2109
|
+
function sanitizeSchema(schema) {
|
|
2110
|
+
const clone = JSON.parse(JSON.stringify(schema));
|
|
2111
|
+
stripUnsupportedSchemaFields(clone);
|
|
2112
|
+
return clone;
|
|
2113
|
+
}
|
|
2114
|
+
function stripUnsupportedSchemaFields(value) {
|
|
2115
|
+
if (!isJsonObject4(value)) {
|
|
2116
|
+
if (Array.isArray(value)) {
|
|
2117
|
+
for (const item of value) stripUnsupportedSchemaFields(item);
|
|
2118
|
+
}
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
delete value.$schema;
|
|
2122
|
+
delete value.additionalProperties;
|
|
2123
|
+
for (const item of Object.values(value)) {
|
|
2124
|
+
if (isJsonObject4(item) || Array.isArray(item)) {
|
|
2125
|
+
stripUnsupportedSchemaFields(item);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
function toGeminiToolConfig(choice, tools) {
|
|
2130
|
+
if (!choice || !tools?.length) return void 0;
|
|
2131
|
+
if (choice === "auto") return { functionCallingConfig: { mode: "AUTO" } };
|
|
2132
|
+
if (choice === "none") return { functionCallingConfig: { mode: "NONE" } };
|
|
2133
|
+
if (choice === "required") return { functionCallingConfig: { mode: "ANY" } };
|
|
2134
|
+
return { functionCallingConfig: { mode: "ANY", allowedFunctionNames: [choice.name] } };
|
|
2135
|
+
}
|
|
2136
|
+
function isGemini3Model(model) {
|
|
2137
|
+
return /^gemini-3(?:\.|-|$)/.test(model);
|
|
2138
|
+
}
|
|
2139
|
+
function toGemini3ThinkingLevel(level) {
|
|
2140
|
+
switch (level) {
|
|
2141
|
+
case "low":
|
|
2142
|
+
return "LOW";
|
|
2143
|
+
case "medium":
|
|
2144
|
+
return "MEDIUM";
|
|
2145
|
+
case "high":
|
|
2146
|
+
case "xhigh":
|
|
2147
|
+
return "HIGH";
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
function toThinkingBudget(level) {
|
|
2151
|
+
switch (level) {
|
|
2152
|
+
case "low":
|
|
2153
|
+
return 1024;
|
|
2154
|
+
case "medium":
|
|
2155
|
+
return 8192;
|
|
2156
|
+
case "high":
|
|
2157
|
+
case "xhigh":
|
|
2158
|
+
return 8192;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
function toThinkingConfig(model, level) {
|
|
2162
|
+
if (!level) return void 0;
|
|
2163
|
+
if (isGemini3Model(model)) {
|
|
2164
|
+
return {
|
|
2165
|
+
includeThoughts: true,
|
|
2166
|
+
thinkingLevel: toGemini3ThinkingLevel(level)
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
return {
|
|
2170
|
+
includeThoughts: true,
|
|
2171
|
+
thinkingBudget: toThinkingBudget(level)
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
function buildGenerateRequest(options) {
|
|
2175
|
+
const downgradedMessages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
2176
|
+
const { systemInstruction, contents } = toSystemAndContents(downgradedMessages);
|
|
2177
|
+
const tools = toGeminiTools(options.tools);
|
|
2178
|
+
const toolConfig = toGeminiToolConfig(options.toolChoice, options.tools);
|
|
2179
|
+
const thinkingConfig = toThinkingConfig(options.model, options.thinking);
|
|
2180
|
+
const generationConfig = {
|
|
2181
|
+
...options.maxTokens ? { maxOutputTokens: options.maxTokens } : {},
|
|
2182
|
+
...options.temperature != null && !options.thinking ? { temperature: options.temperature } : {},
|
|
2183
|
+
...options.topP != null ? { topP: options.topP } : {},
|
|
2184
|
+
...options.stop ? { stopSequences: options.stop } : {},
|
|
2185
|
+
...thinkingConfig ? { thinkingConfig } : {}
|
|
2186
|
+
};
|
|
2187
|
+
return {
|
|
2188
|
+
contents,
|
|
2189
|
+
...systemInstruction ? { systemInstruction } : {},
|
|
2190
|
+
...tools ? { tools } : {},
|
|
2191
|
+
...toolConfig ? { toolConfig } : {},
|
|
2192
|
+
...Object.keys(generationConfig).length > 0 ? { generationConfig } : {},
|
|
2193
|
+
...options.promptCacheKey ? { session_id: options.promptCacheKey } : {}
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
function buildCodeAssistRequest(options, request, projectId) {
|
|
2197
|
+
return {
|
|
2198
|
+
model: options.model,
|
|
2199
|
+
...projectId ? { project: projectId } : {},
|
|
2200
|
+
user_prompt_id: crypto.randomUUID(),
|
|
2201
|
+
request
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
function buildRequestPlan(options, method) {
|
|
2205
|
+
if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
|
|
2206
|
+
throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
|
|
2207
|
+
}
|
|
2208
|
+
const projectId = getGoogleProject(options);
|
|
2209
|
+
const request = buildGenerateRequest(options);
|
|
2210
|
+
return {
|
|
2211
|
+
url: getCodeAssistEndpoint(method),
|
|
2212
|
+
headers: {
|
|
2213
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
2214
|
+
"Content-Type": "application/json",
|
|
2215
|
+
"User-Agent": GEMINI_CLI_USER_AGENT,
|
|
2216
|
+
"X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
|
|
2217
|
+
},
|
|
2218
|
+
body: buildCodeAssistRequest(options, request, projectId)
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
function normalizeGeminiStopReason(reason) {
|
|
2222
|
+
switch (reason) {
|
|
2223
|
+
case "MAX_TOKENS":
|
|
2224
|
+
return "max_tokens";
|
|
2225
|
+
case "STOP":
|
|
2226
|
+
return "stop_sequence";
|
|
2227
|
+
case "SAFETY":
|
|
2228
|
+
case "RECITATION":
|
|
2229
|
+
case "BLOCKLIST":
|
|
2230
|
+
case "PROHIBITED_CONTENT":
|
|
2231
|
+
case "SPII":
|
|
2232
|
+
return "refusal";
|
|
2233
|
+
default:
|
|
2234
|
+
return "end_turn";
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
function parseSseEvents(buffer) {
|
|
2238
|
+
const events = [];
|
|
2239
|
+
let cursor = 0;
|
|
2240
|
+
while (true) {
|
|
2241
|
+
const next = buffer.indexOf("\n\n", cursor);
|
|
2242
|
+
if (next === -1) break;
|
|
2243
|
+
const raw = buffer.slice(cursor, next);
|
|
2244
|
+
cursor = next + 2;
|
|
2245
|
+
let eventName;
|
|
2246
|
+
const dataLines = [];
|
|
2247
|
+
for (const line of raw.split("\n")) {
|
|
2248
|
+
if (line.startsWith("event:")) {
|
|
2249
|
+
eventName = line.slice("event:".length).trim();
|
|
2250
|
+
} else if (line.startsWith("data:")) {
|
|
2251
|
+
dataLines.push(line.slice("data:".length).trimStart());
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
if (dataLines.length > 0) {
|
|
2255
|
+
events.push({ event: eventName, data: dataLines.join("\n") });
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
return { events, remaining: buffer.slice(cursor) };
|
|
2259
|
+
}
|
|
2260
|
+
async function* streamSse(response) {
|
|
2261
|
+
if (!response.body) return;
|
|
2262
|
+
const reader = response.body.getReader();
|
|
2263
|
+
const decoder = new TextDecoder();
|
|
2264
|
+
let buffer = "";
|
|
2265
|
+
try {
|
|
2266
|
+
while (true) {
|
|
2267
|
+
const { done, value } = await reader.read();
|
|
2268
|
+
if (done) break;
|
|
2269
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
2270
|
+
const parsed2 = parseSseEvents(buffer);
|
|
2271
|
+
buffer = parsed2.remaining;
|
|
2272
|
+
for (const event of parsed2.events) {
|
|
2273
|
+
if (event.data === "[DONE]") continue;
|
|
2274
|
+
yield JSON.parse(event.data);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
buffer += decoder.decode().replace(/\r\n/g, "\n");
|
|
2278
|
+
const parsed = parseSseEvents(buffer + "\n\n");
|
|
2279
|
+
for (const event of parsed.events) {
|
|
2280
|
+
if (event.data === "[DONE]") continue;
|
|
2281
|
+
yield JSON.parse(event.data);
|
|
2282
|
+
}
|
|
2283
|
+
} finally {
|
|
2284
|
+
reader.releaseLock();
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
function candidatesFromResponse(response) {
|
|
2288
|
+
return response.response?.candidates ?? response.candidates;
|
|
2289
|
+
}
|
|
2290
|
+
function usageFromResponse(response) {
|
|
2291
|
+
return response.response?.usageMetadata ?? response.usageMetadata;
|
|
2292
|
+
}
|
|
2293
|
+
function partsFromResponse(response) {
|
|
2294
|
+
return candidatesFromResponse(response)?.[0]?.content?.parts ?? [];
|
|
2295
|
+
}
|
|
2296
|
+
function finishReasonFromResponse(response) {
|
|
2297
|
+
return candidatesFromResponse(response)?.[0]?.finishReason;
|
|
2298
|
+
}
|
|
2299
|
+
function readTextPart(part) {
|
|
2300
|
+
return "text" in part ? { text: part.text, thought: part.thought === true } : void 0;
|
|
2301
|
+
}
|
|
2302
|
+
function readFunctionCallPart(part) {
|
|
2303
|
+
if (!("functionCall" in part)) return void 0;
|
|
2304
|
+
return {
|
|
2305
|
+
...part.functionCall.id ? { id: part.functionCall.id } : {},
|
|
2306
|
+
name: part.functionCall.name,
|
|
2307
|
+
args: isJsonObject4(part.functionCall.args) ? part.functionCall.args : {}
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
function makeToolCallId(index, providerId) {
|
|
2311
|
+
return providerId ?? `gemini_call_${index}_${crypto.randomUUID().replace(/-/g, "")}`;
|
|
2312
|
+
}
|
|
2313
|
+
function shouldRetryCodeAssistStatus(status) {
|
|
2314
|
+
return status === 429 || status === 499 || status >= 500 && status <= 599;
|
|
2315
|
+
}
|
|
2316
|
+
function isAbortError(err) {
|
|
2317
|
+
return err instanceof Error && err.name === "AbortError";
|
|
2318
|
+
}
|
|
2319
|
+
async function sleep(ms, signal) {
|
|
2320
|
+
if (ms <= 0) return;
|
|
2321
|
+
await new Promise((resolve, reject) => {
|
|
2322
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
2323
|
+
const timer = setTimeout(() => {
|
|
2324
|
+
cleanup();
|
|
2325
|
+
resolve();
|
|
2326
|
+
}, ms);
|
|
2327
|
+
const onAbort = () => {
|
|
2328
|
+
clearTimeout(timer);
|
|
2329
|
+
cleanup();
|
|
2330
|
+
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
2331
|
+
};
|
|
2332
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2333
|
+
if (signal?.aborted) onAbort();
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
async function fetchCodeAssist(plan, options) {
|
|
2337
|
+
try {
|
|
2338
|
+
const response = await fetch(plan.url, {
|
|
2339
|
+
method: "POST",
|
|
2340
|
+
headers: plan.headers,
|
|
2341
|
+
body: JSON.stringify(plan.body),
|
|
2342
|
+
signal: options.signal
|
|
2343
|
+
});
|
|
2344
|
+
if (!response.ok) {
|
|
2345
|
+
const text = await response.text().catch(() => "");
|
|
2346
|
+
throw new ProviderError("gemini", formatErrorMessage(response.status, text, options.model), {
|
|
2347
|
+
statusCode: response.status
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
return response;
|
|
2351
|
+
} catch (err) {
|
|
2352
|
+
throw toError3(err);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
async function fetchCodeAssistWithRetry(plan, options) {
|
|
2356
|
+
let lastError;
|
|
2357
|
+
for (let attempt = 0; attempt <= CODE_ASSIST_NON_STREAMING_RETRIES; attempt++) {
|
|
2358
|
+
try {
|
|
2359
|
+
return await fetchCodeAssist(plan, options);
|
|
2360
|
+
} catch (err) {
|
|
2361
|
+
const error = toError3(err);
|
|
2362
|
+
const statusCode = error instanceof ProviderError ? error.statusCode : void 0;
|
|
2363
|
+
if (options.signal?.aborted || isAbortError(error) || attempt === CODE_ASSIST_NON_STREAMING_RETRIES || statusCode != null && !shouldRetryCodeAssistStatus(statusCode)) {
|
|
2364
|
+
throw error;
|
|
2365
|
+
}
|
|
2366
|
+
lastError = error;
|
|
2367
|
+
}
|
|
2368
|
+
try {
|
|
2369
|
+
await sleep(CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS, options.signal);
|
|
2370
|
+
} catch (err) {
|
|
2371
|
+
throw toError3(err);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
|
|
2375
|
+
}
|
|
2376
|
+
function streamGemini(options) {
|
|
2377
|
+
return new StreamResult(runStream4(options));
|
|
2378
|
+
}
|
|
2379
|
+
async function* runStream4(options) {
|
|
2380
|
+
const useStreaming = options.streaming !== false;
|
|
2381
|
+
const method = useStreaming ? "streamGenerateContent" : "generateContent";
|
|
2382
|
+
const plan = buildRequestPlan(options, method);
|
|
2383
|
+
if (useStreaming) plan.url.searchParams.set("alt", "sse");
|
|
2384
|
+
const response = useStreaming ? await fetchCodeAssist(plan, options) : await fetchCodeAssistWithRetry(plan, options);
|
|
2385
|
+
const contentParts = [];
|
|
2386
|
+
const pendingToolCalls = [];
|
|
2387
|
+
let textAccum = "";
|
|
2388
|
+
let thinkingAccum = "";
|
|
2389
|
+
let stopReason = "end_turn";
|
|
2390
|
+
let inputTokens = 0;
|
|
2391
|
+
let outputTokens = 0;
|
|
2392
|
+
let cacheRead = 0;
|
|
2393
|
+
let toolIndex = 0;
|
|
2394
|
+
const handleResponse = function* (chunk) {
|
|
2395
|
+
const usage = usageFromResponse(chunk);
|
|
2396
|
+
if (usage) {
|
|
2397
|
+
inputTokens = usage.promptTokenCount ?? inputTokens;
|
|
2398
|
+
outputTokens = usage.candidatesTokenCount ?? outputTokens;
|
|
2399
|
+
cacheRead = usage.cachedContentTokenCount ?? cacheRead;
|
|
2400
|
+
}
|
|
2401
|
+
const reason = finishReasonFromResponse(chunk);
|
|
2402
|
+
if (reason) stopReason = normalizeGeminiStopReason(reason);
|
|
2403
|
+
for (const part of partsFromResponse(chunk)) {
|
|
2404
|
+
const textPart = readTextPart(part);
|
|
2405
|
+
if (textPart) {
|
|
2406
|
+
if (textPart.thought) {
|
|
2407
|
+
thinkingAccum += textPart.text;
|
|
2408
|
+
yield { type: "thinking_delta", text: textPart.text };
|
|
2409
|
+
} else {
|
|
2410
|
+
textAccum += textPart.text;
|
|
2411
|
+
yield { type: "text_delta", text: textPart.text };
|
|
2412
|
+
}
|
|
2413
|
+
continue;
|
|
2414
|
+
}
|
|
2415
|
+
const functionCall = readFunctionCallPart(part);
|
|
2416
|
+
if (functionCall) {
|
|
2417
|
+
const id = makeToolCallId(toolIndex++, functionCall.id);
|
|
2418
|
+
const argsJson = JSON.stringify(functionCall.args);
|
|
2419
|
+
pendingToolCalls.push({
|
|
2420
|
+
type: "tool_call",
|
|
2421
|
+
id,
|
|
2422
|
+
name: functionCall.name,
|
|
2423
|
+
args: functionCall.args
|
|
2424
|
+
});
|
|
2425
|
+
yield { type: "toolcall_delta", id, name: functionCall.name, argsJson };
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
try {
|
|
2430
|
+
if (useStreaming) {
|
|
2431
|
+
for await (const chunk of streamSse(response)) {
|
|
2432
|
+
yield* handleResponse(chunk);
|
|
2433
|
+
}
|
|
2434
|
+
} else {
|
|
2435
|
+
const chunk = await response.json();
|
|
2436
|
+
yield* handleResponse(chunk);
|
|
2437
|
+
}
|
|
2438
|
+
} catch (err) {
|
|
2439
|
+
throw toError3(err);
|
|
2440
|
+
}
|
|
2441
|
+
if (thinkingAccum) contentParts.push({ type: "thinking", text: thinkingAccum });
|
|
2442
|
+
if (textAccum) contentParts.push({ type: "text", text: textAccum });
|
|
2443
|
+
for (const toolCall of pendingToolCalls) {
|
|
2444
|
+
contentParts.push(toolCall);
|
|
2445
|
+
yield {
|
|
2446
|
+
type: "toolcall_done",
|
|
2447
|
+
id: toolCall.id,
|
|
2448
|
+
name: toolCall.name,
|
|
2449
|
+
args: toolCall.args
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
if (pendingToolCalls.length > 0) stopReason = "tool_use";
|
|
2453
|
+
const adjustedInputTokens = Math.max(0, inputTokens - cacheRead);
|
|
2454
|
+
const streamResponse = {
|
|
2455
|
+
message: {
|
|
2456
|
+
role: "assistant",
|
|
2457
|
+
content: contentParts.length > 0 ? contentParts : textAccum
|
|
2458
|
+
},
|
|
2459
|
+
stopReason,
|
|
2460
|
+
usage: {
|
|
2461
|
+
inputTokens: adjustedInputTokens,
|
|
2462
|
+
outputTokens,
|
|
2463
|
+
...cacheRead > 0 ? { cacheRead } : {}
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2466
|
+
yield { type: "done", stopReason };
|
|
2467
|
+
return streamResponse;
|
|
2468
|
+
}
|
|
2469
|
+
function toError3(err) {
|
|
2470
|
+
if (err instanceof ProviderError) return err;
|
|
2471
|
+
if (err instanceof Error) return new ProviderError("gemini", err.message, { cause: err });
|
|
2472
|
+
return new ProviderError("gemini", String(err));
|
|
2473
|
+
}
|
|
2474
|
+
|
|
1992
2475
|
// src/provider-registry.ts
|
|
1993
2476
|
var ProviderRegistryImpl = class {
|
|
1994
2477
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -2045,6 +2528,9 @@ providerRegistry.register("openai", {
|
|
|
2045
2528
|
return streamOpenAI(options);
|
|
2046
2529
|
}
|
|
2047
2530
|
});
|
|
2531
|
+
providerRegistry.register("gemini", {
|
|
2532
|
+
stream: (options) => streamGemini(options)
|
|
2533
|
+
});
|
|
2048
2534
|
providerRegistry.register("glm", {
|
|
2049
2535
|
stream: (options) => streamOpenAI({
|
|
2050
2536
|
...options,
|