@kenkaiiii/gg-ai 4.3.202 → 4.3.204

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.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 {
@@ -163,6 +163,8 @@ interface StreamOptions {
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 {
@@ -163,6 +163,8 @@ interface StreamOptions {
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 GGAIError {
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",
@@ -1926,6 +1927,449 @@ function parseCodexErrorBody(text) {
1926
1927
  }
1927
1928
  }
1928
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 SYNTHETIC_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
1936
+ var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
1937
+ "gemini-3-pro-preview",
1938
+ "gemini-3.1-pro-preview",
1939
+ "gemini-3.1-pro-preview-customtools",
1940
+ "gemini-3-flash-preview",
1941
+ "gemini-3.1-flash-lite-preview",
1942
+ "gemini-2.5-pro",
1943
+ "gemini-2.5-flash",
1944
+ "gemini-2.5-flash-lite",
1945
+ "gemma-4-31b-it",
1946
+ "gemma-4-26b-a4b-it"
1947
+ ]);
1948
+ function isJsonObject4(value) {
1949
+ return value != null && typeof value === "object" && !Array.isArray(value);
1950
+ }
1951
+ function getEnvironment() {
1952
+ return globalThis.process?.env;
1953
+ }
1954
+ function getGoogleProject(options) {
1955
+ const env = getEnvironment();
1956
+ return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
1957
+ }
1958
+ function getCodeAssistEndpoint(method) {
1959
+ return new URL(`${DEFAULT_CODE_ASSIST_BASE_URL}/${CODE_ASSIST_API_VERSION}:${method}`);
1960
+ }
1961
+ function formatUnsupportedModelMessage(model) {
1962
+ return `Gemini OAuth is configured to use the Gemini Code Assist subscription endpoint only. That endpoint does not currently expose model "${model}".`;
1963
+ }
1964
+ function formatErrorMessage(status, body, model) {
1965
+ if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
1966
+ return `Gemini API error (404): ${body}
1967
+
1968
+ ${formatUnsupportedModelMessage(model)}`;
1969
+ }
1970
+ return `Gemini API error (${status}): ${body}`;
1971
+ }
1972
+ function toSystemAndContents(messages) {
1973
+ let systemText = "";
1974
+ const contents = [];
1975
+ const toolNamesById = /* @__PURE__ */ new Map();
1976
+ for (const msg of messages) {
1977
+ if (msg.role === "system") {
1978
+ systemText = systemText ? `${systemText}
1979
+
1980
+ ${msg.content}` : msg.content;
1981
+ continue;
1982
+ }
1983
+ if (msg.role === "user") {
1984
+ contents.push({
1985
+ role: "user",
1986
+ parts: typeof msg.content === "string" ? [{ text: msg.content }] : msg.content.map((part) => {
1987
+ if (part.type === "text") return { text: part.text };
1988
+ return { inlineData: { mimeType: part.mediaType, data: part.data } };
1989
+ })
1990
+ });
1991
+ continue;
1992
+ }
1993
+ if (msg.role === "assistant") {
1994
+ const parts = [];
1995
+ const source = msg.content;
1996
+ if (typeof source === "string") {
1997
+ if (source) parts.push({ text: source });
1998
+ } else {
1999
+ for (const part of source) {
2000
+ if (part.type === "text" && part.text) {
2001
+ parts.push({ text: part.text });
2002
+ } else if (part.type === "thinking" && part.text) {
2003
+ parts.push({ text: part.text });
2004
+ } else if (part.type === "tool_call") {
2005
+ toolNamesById.set(part.id, part.name);
2006
+ parts.push({
2007
+ functionCall: { id: part.id, name: part.name, args: part.args },
2008
+ thoughtSignature: SYNTHETIC_THOUGHT_SIGNATURE
2009
+ });
2010
+ }
2011
+ }
2012
+ }
2013
+ if (parts.length > 0) contents.push({ role: "model", parts });
2014
+ continue;
2015
+ }
2016
+ if (msg.role === "tool") {
2017
+ const parts = [];
2018
+ for (const result of msg.content) {
2019
+ const name = toolNamesById.get(result.toolCallId) ?? result.toolCallId;
2020
+ const content = typeof result.content === "string" ? result.content : stringifyToolContent(result.content);
2021
+ parts.push({
2022
+ functionResponse: {
2023
+ id: result.toolCallId,
2024
+ name,
2025
+ response: {
2026
+ content,
2027
+ ...result.isError ? { isError: true } : {}
2028
+ }
2029
+ }
2030
+ });
2031
+ }
2032
+ if (parts.length > 0) contents.push({ role: "user", parts });
2033
+ }
2034
+ }
2035
+ return {
2036
+ ...systemText ? { systemInstruction: { parts: [{ text: systemText }] } } : {},
2037
+ contents
2038
+ };
2039
+ }
2040
+ function stringifyToolContent(content) {
2041
+ return content.map((part) => part.type === "text" ? part.text : `[image ${part.mediaType}]`).join("\n");
2042
+ }
2043
+ function toGeminiTools(tools) {
2044
+ if (!tools?.length) return void 0;
2045
+ return [
2046
+ {
2047
+ functionDeclarations: tools.map((tool) => ({
2048
+ name: tool.name,
2049
+ description: tool.description,
2050
+ parameters: sanitizeSchema(tool.rawInputSchema ?? zodToJsonSchema(tool.parameters))
2051
+ }))
2052
+ }
2053
+ ];
2054
+ }
2055
+ function sanitizeSchema(schema) {
2056
+ const clone = JSON.parse(JSON.stringify(schema));
2057
+ stripUnsupportedSchemaFields(clone);
2058
+ return clone;
2059
+ }
2060
+ function stripUnsupportedSchemaFields(value) {
2061
+ if (!isJsonObject4(value)) {
2062
+ if (Array.isArray(value)) {
2063
+ for (const item of value) stripUnsupportedSchemaFields(item);
2064
+ }
2065
+ return;
2066
+ }
2067
+ delete value.$schema;
2068
+ delete value.additionalProperties;
2069
+ for (const item of Object.values(value)) {
2070
+ if (isJsonObject4(item) || Array.isArray(item)) {
2071
+ stripUnsupportedSchemaFields(item);
2072
+ }
2073
+ }
2074
+ }
2075
+ function toGeminiToolConfig(choice, tools) {
2076
+ if (!choice || !tools?.length) return void 0;
2077
+ if (choice === "auto") return { functionCallingConfig: { mode: "AUTO" } };
2078
+ if (choice === "none") return { functionCallingConfig: { mode: "NONE" } };
2079
+ if (choice === "required") return { functionCallingConfig: { mode: "ANY" } };
2080
+ return { functionCallingConfig: { mode: "ANY", allowedFunctionNames: [choice.name] } };
2081
+ }
2082
+ function isGemini3Model(model) {
2083
+ return /^gemini-3(?:\.|-|$)/.test(model);
2084
+ }
2085
+ function toGemini3ThinkingLevel(level) {
2086
+ switch (level) {
2087
+ case "low":
2088
+ return "LOW";
2089
+ case "medium":
2090
+ return "MEDIUM";
2091
+ case "high":
2092
+ case "xhigh":
2093
+ return "HIGH";
2094
+ }
2095
+ }
2096
+ function toThinkingBudget(level) {
2097
+ switch (level) {
2098
+ case "low":
2099
+ return 1024;
2100
+ case "medium":
2101
+ return 8192;
2102
+ case "high":
2103
+ case "xhigh":
2104
+ return 8192;
2105
+ }
2106
+ }
2107
+ function toThinkingConfig(model, level) {
2108
+ if (!level) return void 0;
2109
+ if (isGemini3Model(model)) {
2110
+ return {
2111
+ includeThoughts: true,
2112
+ thinkingLevel: toGemini3ThinkingLevel(level)
2113
+ };
2114
+ }
2115
+ return {
2116
+ includeThoughts: true,
2117
+ thinkingBudget: toThinkingBudget(level)
2118
+ };
2119
+ }
2120
+ function buildGenerateRequest(options) {
2121
+ const downgradedMessages = downgradeUnsupportedImages(options.messages, options.supportsImages);
2122
+ const { systemInstruction, contents } = toSystemAndContents(downgradedMessages);
2123
+ const tools = toGeminiTools(options.tools);
2124
+ const toolConfig = toGeminiToolConfig(options.toolChoice, options.tools);
2125
+ const thinkingConfig = toThinkingConfig(options.model, options.thinking);
2126
+ const generationConfig = {
2127
+ ...options.maxTokens ? { maxOutputTokens: options.maxTokens } : {},
2128
+ ...options.temperature != null && !options.thinking ? { temperature: options.temperature } : {},
2129
+ ...options.topP != null ? { topP: options.topP } : {},
2130
+ ...options.stop ? { stopSequences: options.stop } : {},
2131
+ ...thinkingConfig ? { thinkingConfig } : {}
2132
+ };
2133
+ return {
2134
+ contents,
2135
+ ...systemInstruction ? { systemInstruction } : {},
2136
+ ...tools ? { tools } : {},
2137
+ ...toolConfig ? { toolConfig } : {},
2138
+ ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {}
2139
+ };
2140
+ }
2141
+ function buildCodeAssistRequest(options, request, projectId) {
2142
+ return {
2143
+ model: options.model,
2144
+ ...projectId ? { project: projectId } : {},
2145
+ user_prompt_id: crypto.randomUUID(),
2146
+ request
2147
+ };
2148
+ }
2149
+ function buildRequestPlan(options, method) {
2150
+ if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
2151
+ throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
2152
+ }
2153
+ const projectId = getGoogleProject(options);
2154
+ const request = buildGenerateRequest(options);
2155
+ return {
2156
+ url: getCodeAssistEndpoint(method),
2157
+ headers: {
2158
+ Authorization: `Bearer ${options.apiKey}`,
2159
+ "Content-Type": "application/json",
2160
+ "User-Agent": GEMINI_CLI_USER_AGENT,
2161
+ "X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
2162
+ },
2163
+ body: buildCodeAssistRequest(options, request, projectId)
2164
+ };
2165
+ }
2166
+ function normalizeGeminiStopReason(reason) {
2167
+ switch (reason) {
2168
+ case "MAX_TOKENS":
2169
+ return "max_tokens";
2170
+ case "STOP":
2171
+ return "stop_sequence";
2172
+ case "SAFETY":
2173
+ case "RECITATION":
2174
+ case "BLOCKLIST":
2175
+ case "PROHIBITED_CONTENT":
2176
+ case "SPII":
2177
+ return "refusal";
2178
+ default:
2179
+ return "end_turn";
2180
+ }
2181
+ }
2182
+ function parseSseEvents(buffer) {
2183
+ const events = [];
2184
+ let cursor = 0;
2185
+ while (true) {
2186
+ const next = buffer.indexOf("\n\n", cursor);
2187
+ if (next === -1) break;
2188
+ const raw = buffer.slice(cursor, next);
2189
+ cursor = next + 2;
2190
+ let eventName;
2191
+ const dataLines = [];
2192
+ for (const line of raw.split("\n")) {
2193
+ if (line.startsWith("event:")) {
2194
+ eventName = line.slice("event:".length).trim();
2195
+ } else if (line.startsWith("data:")) {
2196
+ dataLines.push(line.slice("data:".length).trimStart());
2197
+ }
2198
+ }
2199
+ if (dataLines.length > 0) {
2200
+ events.push({ event: eventName, data: dataLines.join("\n") });
2201
+ }
2202
+ }
2203
+ return { events, remaining: buffer.slice(cursor) };
2204
+ }
2205
+ async function* streamSse(response) {
2206
+ if (!response.body) return;
2207
+ const reader = response.body.getReader();
2208
+ const decoder = new TextDecoder();
2209
+ let buffer = "";
2210
+ try {
2211
+ while (true) {
2212
+ const { done, value } = await reader.read();
2213
+ if (done) break;
2214
+ buffer += decoder.decode(value, { stream: true }).replace(/\r\n/g, "\n");
2215
+ const parsed2 = parseSseEvents(buffer);
2216
+ buffer = parsed2.remaining;
2217
+ for (const event of parsed2.events) {
2218
+ if (event.data === "[DONE]") continue;
2219
+ yield JSON.parse(event.data);
2220
+ }
2221
+ }
2222
+ buffer += decoder.decode().replace(/\r\n/g, "\n");
2223
+ const parsed = parseSseEvents(buffer + "\n\n");
2224
+ for (const event of parsed.events) {
2225
+ if (event.data === "[DONE]") continue;
2226
+ yield JSON.parse(event.data);
2227
+ }
2228
+ } finally {
2229
+ reader.releaseLock();
2230
+ }
2231
+ }
2232
+ function candidatesFromResponse(response) {
2233
+ return response.response?.candidates ?? response.candidates;
2234
+ }
2235
+ function usageFromResponse(response) {
2236
+ return response.response?.usageMetadata ?? response.usageMetadata;
2237
+ }
2238
+ function partsFromResponse(response) {
2239
+ return candidatesFromResponse(response)?.[0]?.content?.parts ?? [];
2240
+ }
2241
+ function finishReasonFromResponse(response) {
2242
+ return candidatesFromResponse(response)?.[0]?.finishReason;
2243
+ }
2244
+ function readTextPart(part) {
2245
+ return "text" in part ? { text: part.text, thought: part.thought === true } : void 0;
2246
+ }
2247
+ function readFunctionCallPart(part) {
2248
+ if (!("functionCall" in part)) return void 0;
2249
+ return {
2250
+ ...part.functionCall.id ? { id: part.functionCall.id } : {},
2251
+ name: part.functionCall.name,
2252
+ args: isJsonObject4(part.functionCall.args) ? part.functionCall.args : {}
2253
+ };
2254
+ }
2255
+ function makeToolCallId(index, providerId) {
2256
+ return providerId ?? `gemini_call_${index}_${crypto.randomUUID().replace(/-/g, "")}`;
2257
+ }
2258
+ function streamGemini(options) {
2259
+ return new StreamResult(runStream4(options));
2260
+ }
2261
+ async function* runStream4(options) {
2262
+ const useStreaming = options.streaming !== false;
2263
+ const method = useStreaming ? "streamGenerateContent" : "generateContent";
2264
+ const plan = buildRequestPlan(options, method);
2265
+ if (useStreaming) plan.url.searchParams.set("alt", "sse");
2266
+ let response;
2267
+ try {
2268
+ response = await fetch(plan.url, {
2269
+ method: "POST",
2270
+ headers: plan.headers,
2271
+ body: JSON.stringify(plan.body),
2272
+ signal: options.signal
2273
+ });
2274
+ } catch (err) {
2275
+ throw toError3(err);
2276
+ }
2277
+ if (!response.ok) {
2278
+ const text = await response.text().catch(() => "");
2279
+ throw new ProviderError("gemini", formatErrorMessage(response.status, text, options.model), {
2280
+ statusCode: response.status
2281
+ });
2282
+ }
2283
+ const contentParts = [];
2284
+ const pendingToolCalls = [];
2285
+ let textAccum = "";
2286
+ let thinkingAccum = "";
2287
+ let stopReason = "end_turn";
2288
+ let inputTokens = 0;
2289
+ let outputTokens = 0;
2290
+ let cacheRead = 0;
2291
+ let toolIndex = 0;
2292
+ const handleResponse = function* (chunk) {
2293
+ const usage = usageFromResponse(chunk);
2294
+ if (usage) {
2295
+ inputTokens = usage.promptTokenCount ?? inputTokens;
2296
+ outputTokens = usage.candidatesTokenCount ?? outputTokens;
2297
+ cacheRead = usage.cachedContentTokenCount ?? cacheRead;
2298
+ }
2299
+ const reason = finishReasonFromResponse(chunk);
2300
+ if (reason) stopReason = normalizeGeminiStopReason(reason);
2301
+ for (const part of partsFromResponse(chunk)) {
2302
+ const textPart = readTextPart(part);
2303
+ if (textPart) {
2304
+ if (textPart.thought) {
2305
+ thinkingAccum += textPart.text;
2306
+ yield { type: "thinking_delta", text: textPart.text };
2307
+ } else {
2308
+ textAccum += textPart.text;
2309
+ yield { type: "text_delta", text: textPart.text };
2310
+ }
2311
+ continue;
2312
+ }
2313
+ const functionCall = readFunctionCallPart(part);
2314
+ if (functionCall) {
2315
+ const id = makeToolCallId(toolIndex++, functionCall.id);
2316
+ const argsJson = JSON.stringify(functionCall.args);
2317
+ pendingToolCalls.push({
2318
+ type: "tool_call",
2319
+ id,
2320
+ name: functionCall.name,
2321
+ args: functionCall.args
2322
+ });
2323
+ yield { type: "toolcall_delta", id, name: functionCall.name, argsJson };
2324
+ }
2325
+ }
2326
+ };
2327
+ try {
2328
+ if (useStreaming) {
2329
+ for await (const chunk of streamSse(response)) {
2330
+ yield* handleResponse(chunk);
2331
+ }
2332
+ } else {
2333
+ const chunk = await response.json();
2334
+ yield* handleResponse(chunk);
2335
+ }
2336
+ } catch (err) {
2337
+ throw toError3(err);
2338
+ }
2339
+ if (thinkingAccum) contentParts.push({ type: "thinking", text: thinkingAccum });
2340
+ if (textAccum) contentParts.push({ type: "text", text: textAccum });
2341
+ for (const toolCall of pendingToolCalls) {
2342
+ contentParts.push(toolCall);
2343
+ yield {
2344
+ type: "toolcall_done",
2345
+ id: toolCall.id,
2346
+ name: toolCall.name,
2347
+ args: toolCall.args
2348
+ };
2349
+ }
2350
+ if (pendingToolCalls.length > 0) stopReason = "tool_use";
2351
+ const adjustedInputTokens = Math.max(0, inputTokens - cacheRead);
2352
+ const streamResponse = {
2353
+ message: {
2354
+ role: "assistant",
2355
+ content: contentParts.length > 0 ? contentParts : textAccum
2356
+ },
2357
+ stopReason,
2358
+ usage: {
2359
+ inputTokens: adjustedInputTokens,
2360
+ outputTokens,
2361
+ ...cacheRead > 0 ? { cacheRead } : {}
2362
+ }
2363
+ };
2364
+ yield { type: "done", stopReason };
2365
+ return streamResponse;
2366
+ }
2367
+ function toError3(err) {
2368
+ if (err instanceof ProviderError) return err;
2369
+ if (err instanceof Error) return new ProviderError("gemini", err.message, { cause: err });
2370
+ return new ProviderError("gemini", String(err));
2371
+ }
2372
+
1929
2373
  // src/provider-registry.ts
1930
2374
  var ProviderRegistryImpl = class {
1931
2375
  providers = /* @__PURE__ */ new Map();
@@ -1982,6 +2426,9 @@ providerRegistry.register("openai", {
1982
2426
  return streamOpenAI(options);
1983
2427
  }
1984
2428
  });
2429
+ providerRegistry.register("gemini", {
2430
+ stream: (options) => streamGemini(options)
2431
+ });
1985
2432
  providerRegistry.register("glm", {
1986
2433
  stream: (options) => streamOpenAI({
1987
2434
  ...options,