@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.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
type Provider = "anthropic" | "xiaomi" | "openai" | "glm" | "moonshot" | "minimax" | "deepseek" | "openrouter" | "palsu";
|
|
3
|
+
type Provider = "anthropic" | "xiaomi" | "openai" | "gemini" | "glm" | "moonshot" | "minimax" | "deepseek" | "openrouter" | "palsu";
|
|
4
4
|
type ThinkingLevel = "low" | "medium" | "high" | "xhigh";
|
|
5
5
|
type CacheRetention = "none" | "short" | "long";
|
|
6
6
|
interface TextContent {
|
|
@@ -157,12 +157,14 @@ interface StreamOptions {
|
|
|
157
157
|
signal?: AbortSignal;
|
|
158
158
|
/** Prompt cache retention preference. Providers map this to their supported values. Default: "short". */
|
|
159
159
|
cacheRetention?: CacheRetention;
|
|
160
|
-
/** Stable per-session cache routing key for providers that support it (OpenAI, Moonshot). */
|
|
160
|
+
/** Stable per-session cache routing key for providers that support it (OpenAI, Moonshot, Gemini Code Assist). */
|
|
161
161
|
promptCacheKey?: string;
|
|
162
162
|
/** OpenAI service tier for latency-sensitive requests. Only sent to first-party OpenAI API calls. */
|
|
163
163
|
serviceTier?: "auto" | "default" | "flex" | "priority";
|
|
164
164
|
/** OpenAI ChatGPT account ID (from OAuth JWT) for codex endpoint */
|
|
165
165
|
accountId?: string;
|
|
166
|
+
/** Google Cloud/Code Assist project ID used by Gemini OAuth transport. */
|
|
167
|
+
projectId?: string;
|
|
166
168
|
/** Enable provider-native web search. Each provider uses its own format:
|
|
167
169
|
* - Anthropic: server tool `web_search_20250305`
|
|
168
170
|
* - Moonshot: `builtin_function` `$web_search`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
type Provider = "anthropic" | "xiaomi" | "openai" | "glm" | "moonshot" | "minimax" | "deepseek" | "openrouter" | "palsu";
|
|
3
|
+
type Provider = "anthropic" | "xiaomi" | "openai" | "gemini" | "glm" | "moonshot" | "minimax" | "deepseek" | "openrouter" | "palsu";
|
|
4
4
|
type ThinkingLevel = "low" | "medium" | "high" | "xhigh";
|
|
5
5
|
type CacheRetention = "none" | "short" | "long";
|
|
6
6
|
interface TextContent {
|
|
@@ -157,12 +157,14 @@ interface StreamOptions {
|
|
|
157
157
|
signal?: AbortSignal;
|
|
158
158
|
/** Prompt cache retention preference. Providers map this to their supported values. Default: "short". */
|
|
159
159
|
cacheRetention?: CacheRetention;
|
|
160
|
-
/** Stable per-session cache routing key for providers that support it (OpenAI, Moonshot). */
|
|
160
|
+
/** Stable per-session cache routing key for providers that support it (OpenAI, Moonshot, Gemini Code Assist). */
|
|
161
161
|
promptCacheKey?: string;
|
|
162
162
|
/** OpenAI service tier for latency-sensitive requests. Only sent to first-party OpenAI API calls. */
|
|
163
163
|
serviceTier?: "auto" | "default" | "flex" | "priority";
|
|
164
164
|
/** OpenAI ChatGPT account ID (from OAuth JWT) for codex endpoint */
|
|
165
165
|
accountId?: string;
|
|
166
|
+
/** Google Cloud/Code Assist project ID used by Gemini OAuth transport. */
|
|
167
|
+
projectId?: string;
|
|
166
168
|
/** Enable provider-native web search. Each provider uses its own format:
|
|
167
169
|
* - Anthropic: server tool `web_search_20250305`
|
|
168
170
|
* - Moonshot: `builtin_function` `$web_search`
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ var ProviderError = class extends EZCoderAIError {
|
|
|
29
29
|
var PROVIDER_DISPLAY = {
|
|
30
30
|
openai: "OpenAI",
|
|
31
31
|
anthropic: "Anthropic",
|
|
32
|
+
gemini: "Gemini",
|
|
32
33
|
glm: "Z.AI (GLM)",
|
|
33
34
|
moonshot: "Moonshot",
|
|
34
35
|
deepseek: "DeepSeek",
|
|
@@ -721,20 +722,6 @@ function normalizeOpenAIStopReason(reason) {
|
|
|
721
722
|
function isJsonObject(value) {
|
|
722
723
|
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
723
724
|
}
|
|
724
|
-
function uniqueToolsByName(tools) {
|
|
725
|
-
const seen = /* @__PURE__ */ new Set();
|
|
726
|
-
const unique = [];
|
|
727
|
-
for (const tool of tools) {
|
|
728
|
-
if (!tool.name) {
|
|
729
|
-
unique.push(tool);
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
732
|
-
if (seen.has(tool.name)) continue;
|
|
733
|
-
seen.add(tool.name);
|
|
734
|
-
unique.push(tool);
|
|
735
|
-
}
|
|
736
|
-
return unique;
|
|
737
|
-
}
|
|
738
725
|
function createClient(options) {
|
|
739
726
|
const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
|
|
740
727
|
return new Anthropic({
|
|
@@ -803,18 +790,18 @@ async function* runStream(options) {
|
|
|
803
790
|
if (name) reservedServerNames.add(name);
|
|
804
791
|
}
|
|
805
792
|
const clientTools = options.tools?.length ? toAnthropicTools(
|
|
806
|
-
|
|
793
|
+
options.tools.filter((t) => !reservedServerNames.has(t.name)),
|
|
807
794
|
{
|
|
808
795
|
...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
|
|
809
796
|
...supportsFirstPartyToolExtras ? { enableFineGrainedToolStreaming: true } : {}
|
|
810
797
|
}
|
|
811
798
|
) : [];
|
|
812
799
|
return {
|
|
813
|
-
tools:
|
|
800
|
+
tools: [
|
|
814
801
|
...clientTools,
|
|
815
802
|
...options.serverTools ?? [],
|
|
816
803
|
...options.webSearch ? [{ type: "web_search_20250305", name: "web_search" }] : []
|
|
817
|
-
]
|
|
804
|
+
]
|
|
818
805
|
};
|
|
819
806
|
})() : {},
|
|
820
807
|
...options.toolChoice && options.tools?.length ? { tool_choice: toAnthropicToolChoice(options.toolChoice) } : {},
|
|
@@ -1940,6 +1927,502 @@ function parseCodexErrorBody(text) {
|
|
|
1940
1927
|
}
|
|
1941
1928
|
}
|
|
1942
1929
|
|
|
1930
|
+
// src/providers/gemini.ts
|
|
1931
|
+
var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
|
|
1932
|
+
var CODE_ASSIST_API_VERSION = "v1internal";
|
|
1933
|
+
var GEMINI_CLI_USER_AGENT = "google-gemini-cli";
|
|
1934
|
+
var GEMINI_CLI_API_CLIENT = "gemini-cli/0.0.0";
|
|
1935
|
+
var CODE_ASSIST_NON_STREAMING_RETRIES = 3;
|
|
1936
|
+
var CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS = 1e3;
|
|
1937
|
+
var SYNTHETIC_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
|
|
1938
|
+
var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
|
|
1939
|
+
"gemini-3-pro-preview",
|
|
1940
|
+
"gemini-3.1-pro-preview",
|
|
1941
|
+
"gemini-3.1-pro-preview-customtools",
|
|
1942
|
+
"gemini-3-flash-preview",
|
|
1943
|
+
"gemini-3.1-flash-lite-preview",
|
|
1944
|
+
"gemini-2.5-pro",
|
|
1945
|
+
"gemini-2.5-flash",
|
|
1946
|
+
"gemini-2.5-flash-lite",
|
|
1947
|
+
"gemma-4-31b-it",
|
|
1948
|
+
"gemma-4-26b-a4b-it"
|
|
1949
|
+
]);
|
|
1950
|
+
function isJsonObject4(value) {
|
|
1951
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
1952
|
+
}
|
|
1953
|
+
function getEnvironment() {
|
|
1954
|
+
return globalThis.process?.env;
|
|
1955
|
+
}
|
|
1956
|
+
function getGoogleProject(options) {
|
|
1957
|
+
const env = getEnvironment();
|
|
1958
|
+
return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
|
|
1959
|
+
}
|
|
1960
|
+
function getCodeAssistEndpoint(method) {
|
|
1961
|
+
const env = getEnvironment();
|
|
1962
|
+
const endpoint = env?.CODE_ASSIST_ENDPOINT ?? DEFAULT_CODE_ASSIST_BASE_URL;
|
|
1963
|
+
const version = env?.CODE_ASSIST_API_VERSION || CODE_ASSIST_API_VERSION;
|
|
1964
|
+
return new URL(`${endpoint}/${version}:${method}`);
|
|
1965
|
+
}
|
|
1966
|
+
function formatUnsupportedModelMessage(model) {
|
|
1967
|
+
return `Gemini OAuth is configured to use the Gemini Code Assist subscription endpoint only. That endpoint does not currently expose model "${model}".`;
|
|
1968
|
+
}
|
|
1969
|
+
function formatErrorMessage(status, body, model) {
|
|
1970
|
+
if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
|
|
1971
|
+
return `Gemini API error (404): ${body}
|
|
1972
|
+
|
|
1973
|
+
${formatUnsupportedModelMessage(model)}`;
|
|
1974
|
+
}
|
|
1975
|
+
return `Gemini API error (${status}): ${body}`;
|
|
1976
|
+
}
|
|
1977
|
+
function toSystemAndContents(messages) {
|
|
1978
|
+
let systemText = "";
|
|
1979
|
+
const contents = [];
|
|
1980
|
+
const toolNamesById = /* @__PURE__ */ new Map();
|
|
1981
|
+
for (const msg of messages) {
|
|
1982
|
+
if (msg.role === "system") {
|
|
1983
|
+
systemText = systemText ? `${systemText}
|
|
1984
|
+
|
|
1985
|
+
${msg.content}` : msg.content;
|
|
1986
|
+
continue;
|
|
1987
|
+
}
|
|
1988
|
+
if (msg.role === "user") {
|
|
1989
|
+
contents.push({
|
|
1990
|
+
role: "user",
|
|
1991
|
+
parts: typeof msg.content === "string" ? [{ text: msg.content }] : msg.content.map((part) => {
|
|
1992
|
+
if (part.type === "text") return { text: part.text };
|
|
1993
|
+
return { inlineData: { mimeType: part.mediaType, data: part.data } };
|
|
1994
|
+
})
|
|
1995
|
+
});
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
if (msg.role === "assistant") {
|
|
1999
|
+
const parts = [];
|
|
2000
|
+
const source = msg.content;
|
|
2001
|
+
if (typeof source === "string") {
|
|
2002
|
+
if (source) parts.push({ text: source });
|
|
2003
|
+
} else {
|
|
2004
|
+
for (const part of source) {
|
|
2005
|
+
if (part.type === "text" && part.text) {
|
|
2006
|
+
parts.push({ text: part.text });
|
|
2007
|
+
} else if (part.type === "thinking" && part.text) {
|
|
2008
|
+
parts.push({ text: part.text });
|
|
2009
|
+
} else if (part.type === "tool_call") {
|
|
2010
|
+
toolNamesById.set(part.id, part.name);
|
|
2011
|
+
parts.push({
|
|
2012
|
+
functionCall: { id: part.id, name: part.name, args: part.args },
|
|
2013
|
+
thoughtSignature: SYNTHETIC_THOUGHT_SIGNATURE
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
if (parts.length > 0) contents.push({ role: "model", parts });
|
|
2019
|
+
continue;
|
|
2020
|
+
}
|
|
2021
|
+
if (msg.role === "tool") {
|
|
2022
|
+
const parts = [];
|
|
2023
|
+
for (const result of msg.content) {
|
|
2024
|
+
const name = toolNamesById.get(result.toolCallId) ?? result.toolCallId;
|
|
2025
|
+
const content = typeof result.content === "string" ? result.content : stringifyToolContent(result.content);
|
|
2026
|
+
parts.push({
|
|
2027
|
+
functionResponse: {
|
|
2028
|
+
id: result.toolCallId,
|
|
2029
|
+
name,
|
|
2030
|
+
response: {
|
|
2031
|
+
content,
|
|
2032
|
+
...result.isError ? { isError: true } : {}
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
if (parts.length > 0) contents.push({ role: "user", parts });
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return {
|
|
2041
|
+
...systemText ? { systemInstruction: { parts: [{ text: systemText }] } } : {},
|
|
2042
|
+
contents
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
function stringifyToolContent(content) {
|
|
2046
|
+
return content.map((part) => part.type === "text" ? part.text : `[image ${part.mediaType}]`).join("\n");
|
|
2047
|
+
}
|
|
2048
|
+
function toGeminiTools(tools) {
|
|
2049
|
+
if (!tools?.length) return void 0;
|
|
2050
|
+
return [
|
|
2051
|
+
{
|
|
2052
|
+
functionDeclarations: tools.map((tool) => ({
|
|
2053
|
+
name: tool.name,
|
|
2054
|
+
description: tool.description,
|
|
2055
|
+
parameters: sanitizeSchema(tool.rawInputSchema ?? zodToJsonSchema(tool.parameters))
|
|
2056
|
+
}))
|
|
2057
|
+
}
|
|
2058
|
+
];
|
|
2059
|
+
}
|
|
2060
|
+
function sanitizeSchema(schema) {
|
|
2061
|
+
const clone = JSON.parse(JSON.stringify(schema));
|
|
2062
|
+
stripUnsupportedSchemaFields(clone);
|
|
2063
|
+
return clone;
|
|
2064
|
+
}
|
|
2065
|
+
function stripUnsupportedSchemaFields(value) {
|
|
2066
|
+
if (!isJsonObject4(value)) {
|
|
2067
|
+
if (Array.isArray(value)) {
|
|
2068
|
+
for (const item of value) stripUnsupportedSchemaFields(item);
|
|
2069
|
+
}
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
delete value.$schema;
|
|
2073
|
+
delete value.additionalProperties;
|
|
2074
|
+
for (const item of Object.values(value)) {
|
|
2075
|
+
if (isJsonObject4(item) || Array.isArray(item)) {
|
|
2076
|
+
stripUnsupportedSchemaFields(item);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
function toGeminiToolConfig(choice, tools) {
|
|
2081
|
+
if (!choice || !tools?.length) return void 0;
|
|
2082
|
+
if (choice === "auto") return { functionCallingConfig: { mode: "AUTO" } };
|
|
2083
|
+
if (choice === "none") return { functionCallingConfig: { mode: "NONE" } };
|
|
2084
|
+
if (choice === "required") return { functionCallingConfig: { mode: "ANY" } };
|
|
2085
|
+
return { functionCallingConfig: { mode: "ANY", allowedFunctionNames: [choice.name] } };
|
|
2086
|
+
}
|
|
2087
|
+
function isGemini3Model(model) {
|
|
2088
|
+
return /^gemini-3(?:\.|-|$)/.test(model);
|
|
2089
|
+
}
|
|
2090
|
+
function toGemini3ThinkingLevel(level) {
|
|
2091
|
+
switch (level) {
|
|
2092
|
+
case "low":
|
|
2093
|
+
return "LOW";
|
|
2094
|
+
case "medium":
|
|
2095
|
+
return "MEDIUM";
|
|
2096
|
+
case "high":
|
|
2097
|
+
case "xhigh":
|
|
2098
|
+
return "HIGH";
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
function toThinkingBudget(level) {
|
|
2102
|
+
switch (level) {
|
|
2103
|
+
case "low":
|
|
2104
|
+
return 1024;
|
|
2105
|
+
case "medium":
|
|
2106
|
+
return 8192;
|
|
2107
|
+
case "high":
|
|
2108
|
+
case "xhigh":
|
|
2109
|
+
return 8192;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
function toThinkingConfig(model, level) {
|
|
2113
|
+
if (!level) return void 0;
|
|
2114
|
+
if (isGemini3Model(model)) {
|
|
2115
|
+
return {
|
|
2116
|
+
includeThoughts: true,
|
|
2117
|
+
thinkingLevel: toGemini3ThinkingLevel(level)
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
return {
|
|
2121
|
+
includeThoughts: true,
|
|
2122
|
+
thinkingBudget: toThinkingBudget(level)
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
function buildGenerateRequest(options) {
|
|
2126
|
+
const downgradedMessages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
2127
|
+
const { systemInstruction, contents } = toSystemAndContents(downgradedMessages);
|
|
2128
|
+
const tools = toGeminiTools(options.tools);
|
|
2129
|
+
const toolConfig = toGeminiToolConfig(options.toolChoice, options.tools);
|
|
2130
|
+
const thinkingConfig = toThinkingConfig(options.model, options.thinking);
|
|
2131
|
+
const generationConfig = {
|
|
2132
|
+
...options.maxTokens ? { maxOutputTokens: options.maxTokens } : {},
|
|
2133
|
+
...options.temperature != null && !options.thinking ? { temperature: options.temperature } : {},
|
|
2134
|
+
...options.topP != null ? { topP: options.topP } : {},
|
|
2135
|
+
...options.stop ? { stopSequences: options.stop } : {},
|
|
2136
|
+
...thinkingConfig ? { thinkingConfig } : {}
|
|
2137
|
+
};
|
|
2138
|
+
return {
|
|
2139
|
+
contents,
|
|
2140
|
+
...systemInstruction ? { systemInstruction } : {},
|
|
2141
|
+
...tools ? { tools } : {},
|
|
2142
|
+
...toolConfig ? { toolConfig } : {},
|
|
2143
|
+
...Object.keys(generationConfig).length > 0 ? { generationConfig } : {},
|
|
2144
|
+
...options.promptCacheKey ? { session_id: options.promptCacheKey } : {}
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
function buildCodeAssistRequest(options, request, projectId) {
|
|
2148
|
+
return {
|
|
2149
|
+
model: options.model,
|
|
2150
|
+
...projectId ? { project: projectId } : {},
|
|
2151
|
+
user_prompt_id: crypto.randomUUID(),
|
|
2152
|
+
request
|
|
2153
|
+
};
|
|
2154
|
+
}
|
|
2155
|
+
function buildRequestPlan(options, method) {
|
|
2156
|
+
if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
|
|
2157
|
+
throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
|
|
2158
|
+
}
|
|
2159
|
+
const projectId = getGoogleProject(options);
|
|
2160
|
+
const request = buildGenerateRequest(options);
|
|
2161
|
+
return {
|
|
2162
|
+
url: getCodeAssistEndpoint(method),
|
|
2163
|
+
headers: {
|
|
2164
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
2165
|
+
"Content-Type": "application/json",
|
|
2166
|
+
"User-Agent": GEMINI_CLI_USER_AGENT,
|
|
2167
|
+
"X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
|
|
2168
|
+
},
|
|
2169
|
+
body: buildCodeAssistRequest(options, request, projectId)
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
function normalizeGeminiStopReason(reason) {
|
|
2173
|
+
switch (reason) {
|
|
2174
|
+
case "MAX_TOKENS":
|
|
2175
|
+
return "max_tokens";
|
|
2176
|
+
case "STOP":
|
|
2177
|
+
return "stop_sequence";
|
|
2178
|
+
case "SAFETY":
|
|
2179
|
+
case "RECITATION":
|
|
2180
|
+
case "BLOCKLIST":
|
|
2181
|
+
case "PROHIBITED_CONTENT":
|
|
2182
|
+
case "SPII":
|
|
2183
|
+
return "refusal";
|
|
2184
|
+
default:
|
|
2185
|
+
return "end_turn";
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
function parseSseEvents(buffer) {
|
|
2189
|
+
const events = [];
|
|
2190
|
+
let cursor = 0;
|
|
2191
|
+
while (true) {
|
|
2192
|
+
const next = buffer.indexOf("\n\n", cursor);
|
|
2193
|
+
if (next === -1) break;
|
|
2194
|
+
const raw = buffer.slice(cursor, next);
|
|
2195
|
+
cursor = next + 2;
|
|
2196
|
+
let eventName;
|
|
2197
|
+
const dataLines = [];
|
|
2198
|
+
for (const line of raw.split("\n")) {
|
|
2199
|
+
if (line.startsWith("event:")) {
|
|
2200
|
+
eventName = line.slice("event:".length).trim();
|
|
2201
|
+
} else if (line.startsWith("data:")) {
|
|
2202
|
+
dataLines.push(line.slice("data:".length).trimStart());
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
if (dataLines.length > 0) {
|
|
2206
|
+
events.push({ event: eventName, data: dataLines.join("\n") });
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
return { events, remaining: buffer.slice(cursor) };
|
|
2210
|
+
}
|
|
2211
|
+
async function* streamSse(response) {
|
|
2212
|
+
if (!response.body) return;
|
|
2213
|
+
const reader = response.body.getReader();
|
|
2214
|
+
const decoder = new TextDecoder();
|
|
2215
|
+
let buffer = "";
|
|
2216
|
+
try {
|
|
2217
|
+
while (true) {
|
|
2218
|
+
const { done, value } = await reader.read();
|
|
2219
|
+
if (done) break;
|
|
2220
|
+
buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
|
|
2221
|
+
const parsed2 = parseSseEvents(buffer);
|
|
2222
|
+
buffer = parsed2.remaining;
|
|
2223
|
+
for (const event of parsed2.events) {
|
|
2224
|
+
if (event.data === "[DONE]") continue;
|
|
2225
|
+
yield JSON.parse(event.data);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
buffer += decoder.decode().replace(/\r\n/g, "\n");
|
|
2229
|
+
const parsed = parseSseEvents(buffer + "\n\n");
|
|
2230
|
+
for (const event of parsed.events) {
|
|
2231
|
+
if (event.data === "[DONE]") continue;
|
|
2232
|
+
yield JSON.parse(event.data);
|
|
2233
|
+
}
|
|
2234
|
+
} finally {
|
|
2235
|
+
reader.releaseLock();
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
function candidatesFromResponse(response) {
|
|
2239
|
+
return response.response?.candidates ?? response.candidates;
|
|
2240
|
+
}
|
|
2241
|
+
function usageFromResponse(response) {
|
|
2242
|
+
return response.response?.usageMetadata ?? response.usageMetadata;
|
|
2243
|
+
}
|
|
2244
|
+
function partsFromResponse(response) {
|
|
2245
|
+
return candidatesFromResponse(response)?.[0]?.content?.parts ?? [];
|
|
2246
|
+
}
|
|
2247
|
+
function finishReasonFromResponse(response) {
|
|
2248
|
+
return candidatesFromResponse(response)?.[0]?.finishReason;
|
|
2249
|
+
}
|
|
2250
|
+
function readTextPart(part) {
|
|
2251
|
+
return "text" in part ? { text: part.text, thought: part.thought === true } : void 0;
|
|
2252
|
+
}
|
|
2253
|
+
function readFunctionCallPart(part) {
|
|
2254
|
+
if (!("functionCall" in part)) return void 0;
|
|
2255
|
+
return {
|
|
2256
|
+
...part.functionCall.id ? { id: part.functionCall.id } : {},
|
|
2257
|
+
name: part.functionCall.name,
|
|
2258
|
+
args: isJsonObject4(part.functionCall.args) ? part.functionCall.args : {}
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
function makeToolCallId(index, providerId) {
|
|
2262
|
+
return providerId ?? `gemini_call_${index}_${crypto.randomUUID().replace(/-/g, "")}`;
|
|
2263
|
+
}
|
|
2264
|
+
function shouldRetryCodeAssistStatus(status) {
|
|
2265
|
+
return status === 429 || status === 499 || status >= 500 && status <= 599;
|
|
2266
|
+
}
|
|
2267
|
+
function isAbortError(err) {
|
|
2268
|
+
return err instanceof Error && err.name === "AbortError";
|
|
2269
|
+
}
|
|
2270
|
+
async function sleep(ms, signal) {
|
|
2271
|
+
if (ms <= 0) return;
|
|
2272
|
+
await new Promise((resolve, reject) => {
|
|
2273
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
2274
|
+
const timer = setTimeout(() => {
|
|
2275
|
+
cleanup();
|
|
2276
|
+
resolve();
|
|
2277
|
+
}, ms);
|
|
2278
|
+
const onAbort = () => {
|
|
2279
|
+
clearTimeout(timer);
|
|
2280
|
+
cleanup();
|
|
2281
|
+
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
2282
|
+
};
|
|
2283
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2284
|
+
if (signal?.aborted) onAbort();
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
async function fetchCodeAssist(plan, options) {
|
|
2288
|
+
try {
|
|
2289
|
+
const response = await fetch(plan.url, {
|
|
2290
|
+
method: "POST",
|
|
2291
|
+
headers: plan.headers,
|
|
2292
|
+
body: JSON.stringify(plan.body),
|
|
2293
|
+
signal: options.signal
|
|
2294
|
+
});
|
|
2295
|
+
if (!response.ok) {
|
|
2296
|
+
const text = await response.text().catch(() => "");
|
|
2297
|
+
throw new ProviderError("gemini", formatErrorMessage(response.status, text, options.model), {
|
|
2298
|
+
statusCode: response.status
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
return response;
|
|
2302
|
+
} catch (err) {
|
|
2303
|
+
throw toError3(err);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
async function fetchCodeAssistWithRetry(plan, options) {
|
|
2307
|
+
let lastError;
|
|
2308
|
+
for (let attempt = 0; attempt <= CODE_ASSIST_NON_STREAMING_RETRIES; attempt++) {
|
|
2309
|
+
try {
|
|
2310
|
+
return await fetchCodeAssist(plan, options);
|
|
2311
|
+
} catch (err) {
|
|
2312
|
+
const error = toError3(err);
|
|
2313
|
+
const statusCode = error instanceof ProviderError ? error.statusCode : void 0;
|
|
2314
|
+
if (options.signal?.aborted || isAbortError(error) || attempt === CODE_ASSIST_NON_STREAMING_RETRIES || statusCode != null && !shouldRetryCodeAssistStatus(statusCode)) {
|
|
2315
|
+
throw error;
|
|
2316
|
+
}
|
|
2317
|
+
lastError = error;
|
|
2318
|
+
}
|
|
2319
|
+
try {
|
|
2320
|
+
await sleep(CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS, options.signal);
|
|
2321
|
+
} catch (err) {
|
|
2322
|
+
throw toError3(err);
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
|
|
2326
|
+
}
|
|
2327
|
+
function streamGemini(options) {
|
|
2328
|
+
return new StreamResult(runStream4(options));
|
|
2329
|
+
}
|
|
2330
|
+
async function* runStream4(options) {
|
|
2331
|
+
const useStreaming = options.streaming !== false;
|
|
2332
|
+
const method = useStreaming ? "streamGenerateContent" : "generateContent";
|
|
2333
|
+
const plan = buildRequestPlan(options, method);
|
|
2334
|
+
if (useStreaming) plan.url.searchParams.set("alt", "sse");
|
|
2335
|
+
const response = useStreaming ? await fetchCodeAssist(plan, options) : await fetchCodeAssistWithRetry(plan, options);
|
|
2336
|
+
const contentParts = [];
|
|
2337
|
+
const pendingToolCalls = [];
|
|
2338
|
+
let textAccum = "";
|
|
2339
|
+
let thinkingAccum = "";
|
|
2340
|
+
let stopReason = "end_turn";
|
|
2341
|
+
let inputTokens = 0;
|
|
2342
|
+
let outputTokens = 0;
|
|
2343
|
+
let cacheRead = 0;
|
|
2344
|
+
let toolIndex = 0;
|
|
2345
|
+
const handleResponse = function* (chunk) {
|
|
2346
|
+
const usage = usageFromResponse(chunk);
|
|
2347
|
+
if (usage) {
|
|
2348
|
+
inputTokens = usage.promptTokenCount ?? inputTokens;
|
|
2349
|
+
outputTokens = usage.candidatesTokenCount ?? outputTokens;
|
|
2350
|
+
cacheRead = usage.cachedContentTokenCount ?? cacheRead;
|
|
2351
|
+
}
|
|
2352
|
+
const reason = finishReasonFromResponse(chunk);
|
|
2353
|
+
if (reason) stopReason = normalizeGeminiStopReason(reason);
|
|
2354
|
+
for (const part of partsFromResponse(chunk)) {
|
|
2355
|
+
const textPart = readTextPart(part);
|
|
2356
|
+
if (textPart) {
|
|
2357
|
+
if (textPart.thought) {
|
|
2358
|
+
thinkingAccum += textPart.text;
|
|
2359
|
+
yield { type: "thinking_delta", text: textPart.text };
|
|
2360
|
+
} else {
|
|
2361
|
+
textAccum += textPart.text;
|
|
2362
|
+
yield { type: "text_delta", text: textPart.text };
|
|
2363
|
+
}
|
|
2364
|
+
continue;
|
|
2365
|
+
}
|
|
2366
|
+
const functionCall = readFunctionCallPart(part);
|
|
2367
|
+
if (functionCall) {
|
|
2368
|
+
const id = makeToolCallId(toolIndex++, functionCall.id);
|
|
2369
|
+
const argsJson = JSON.stringify(functionCall.args);
|
|
2370
|
+
pendingToolCalls.push({
|
|
2371
|
+
type: "tool_call",
|
|
2372
|
+
id,
|
|
2373
|
+
name: functionCall.name,
|
|
2374
|
+
args: functionCall.args
|
|
2375
|
+
});
|
|
2376
|
+
yield { type: "toolcall_delta", id, name: functionCall.name, argsJson };
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
};
|
|
2380
|
+
try {
|
|
2381
|
+
if (useStreaming) {
|
|
2382
|
+
for await (const chunk of streamSse(response)) {
|
|
2383
|
+
yield* handleResponse(chunk);
|
|
2384
|
+
}
|
|
2385
|
+
} else {
|
|
2386
|
+
const chunk = await response.json();
|
|
2387
|
+
yield* handleResponse(chunk);
|
|
2388
|
+
}
|
|
2389
|
+
} catch (err) {
|
|
2390
|
+
throw toError3(err);
|
|
2391
|
+
}
|
|
2392
|
+
if (thinkingAccum) contentParts.push({ type: "thinking", text: thinkingAccum });
|
|
2393
|
+
if (textAccum) contentParts.push({ type: "text", text: textAccum });
|
|
2394
|
+
for (const toolCall of pendingToolCalls) {
|
|
2395
|
+
contentParts.push(toolCall);
|
|
2396
|
+
yield {
|
|
2397
|
+
type: "toolcall_done",
|
|
2398
|
+
id: toolCall.id,
|
|
2399
|
+
name: toolCall.name,
|
|
2400
|
+
args: toolCall.args
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
if (pendingToolCalls.length > 0) stopReason = "tool_use";
|
|
2404
|
+
const adjustedInputTokens = Math.max(0, inputTokens - cacheRead);
|
|
2405
|
+
const streamResponse = {
|
|
2406
|
+
message: {
|
|
2407
|
+
role: "assistant",
|
|
2408
|
+
content: contentParts.length > 0 ? contentParts : textAccum
|
|
2409
|
+
},
|
|
2410
|
+
stopReason,
|
|
2411
|
+
usage: {
|
|
2412
|
+
inputTokens: adjustedInputTokens,
|
|
2413
|
+
outputTokens,
|
|
2414
|
+
...cacheRead > 0 ? { cacheRead } : {}
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
yield { type: "done", stopReason };
|
|
2418
|
+
return streamResponse;
|
|
2419
|
+
}
|
|
2420
|
+
function toError3(err) {
|
|
2421
|
+
if (err instanceof ProviderError) return err;
|
|
2422
|
+
if (err instanceof Error) return new ProviderError("gemini", err.message, { cause: err });
|
|
2423
|
+
return new ProviderError("gemini", String(err));
|
|
2424
|
+
}
|
|
2425
|
+
|
|
1943
2426
|
// src/provider-registry.ts
|
|
1944
2427
|
var ProviderRegistryImpl = class {
|
|
1945
2428
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -1996,6 +2479,9 @@ providerRegistry.register("openai", {
|
|
|
1996
2479
|
return streamOpenAI(options);
|
|
1997
2480
|
}
|
|
1998
2481
|
});
|
|
2482
|
+
providerRegistry.register("gemini", {
|
|
2483
|
+
stream: (options) => streamGemini(options)
|
|
2484
|
+
});
|
|
1999
2485
|
providerRegistry.register("glm", {
|
|
2000
2486
|
stream: (options) => streamOpenAI({
|
|
2001
2487
|
...options,
|