@kenkaiiii/gg-ai 5.91.0 → 5.91.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NOTICE ADDED
@@ -0,0 +1,52 @@
1
+ GG Coder
2
+ Copyright (C) 2025-2026 KenKai
3
+
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License as published by the Free
6
+ Software Foundation, version 3 only (AGPL-3.0-only). See LICENSE.
7
+
8
+ This program is distributed in the hope that it will be useful, but WITHOUT
9
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
11
+ details.
12
+
13
+ Releases before 4 November 2026 (up to and including v0.102.0 of the desktop
14
+ app and 5.91.0 of the npm packages) were published under the MIT License, and
15
+ copies already received under those terms keep them.
16
+
17
+ --------------------------------------------------------------------------------
18
+ Contributions under the MIT License
19
+
20
+ The WSL-to-Windows-host audio playback in packages/ggcoder/src/core/radio.ts
21
+ (isWsl and tryPlayOnWindowsHost) is derived from contributions by
22
+ creativeprofit22 (pull requests #15 and #16, May 2026), made under these terms:
23
+
24
+ Copyright (c) 2026 creativeprofit22
25
+
26
+ Permission is hereby granted, free of charge, to any person obtaining a copy
27
+ of this software and associated documentation files (the "Software"), to deal
28
+ in the Software without restriction, including without limitation the rights
29
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
30
+ copies of the Software, and to permit persons to whom the Software is
31
+ furnished to do so, subject to the following conditions:
32
+
33
+ The above copyright notice and this permission notice shall be included in
34
+ all copies or substantial portions of the Software.
35
+
36
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
39
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
41
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
42
+ SOFTWARE.
43
+
44
+ --------------------------------------------------------------------------------
45
+ Third-party components
46
+
47
+ Bundled third-party code, fonts and media keep their own licenses, listed in:
48
+ gg-app/src/assets/fonts/THIRD-PARTY.md
49
+ packages/ggcoder/assets/motion/THIRD-PARTY.md
50
+ packages/gg-world/CREDITS.md
51
+ and in the LICENSE / OFL.txt files beside each component. npm and Cargo
52
+ dependencies are installed under their own licenses.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/@kenkaiiii/gg-ai"><img src="https://img.shields.io/npm/v/@kenkaiiii/gg-ai?style=for-the-badge" alt="npm version"></a>
9
- <a href="../../LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="MIT License"></a>
9
+ <a href="../../LICENSE"><img src="https://img.shields.io/badge/License-AGPL--3.0-blue.svg?style=for-the-badge" alt="AGPL-3.0 License"></a>
10
10
  </p>
11
11
 
12
12
  One function. Flat options. Switch providers by changing a string. No adapters, no plugins, no wrapper classes.
@@ -38,21 +38,21 @@ Tool parameters are Zod schemas. Converted to JSON Schema at the provider bounda
38
38
 
39
39
  ## Providers
40
40
 
41
- | Provider | Models | Notes |
42
- |---|---|---|
43
- | `anthropic` | Claude Fable 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5 | Extended thinking, prompt caching, server-side compaction |
44
- | `openai` | GPT-6 Astra, GPT-6.1 Sol, GPT-6 Luna | OAuth (Codex endpoint) or API key |
45
- | `gemini` | Gemini 3.1 Pro (Preview), 3.8 / 3.7 / 3.5 Flash, 3.5 / 3.1 Flash Lite | OAuth (Code Assist); native video input |
46
- | `xai` | Grok 4.7 | OpenAI-compatible, `https://api.x.ai/v1` |
47
- | `moonshot` | Kimi K3, K2.8 Preview, K2.7 Code, K2.7 Code HighSpeed | OpenAI-compatible; native video input |
48
- | `glm` | GLM-5.3, GLM-5.3-Flash | Z.AI coding endpoint, OpenAI-compatible |
49
- | `minimax` | MiniMax M3 | Anthropic-compatible endpoint |
50
- | `xiaomi` | MiMo-V2.6-Pro, MiMo-V2.6-Flash, MiMo-V2.6-Pro-UltraSpeed | OpenAI-compatible |
51
- | `deepseek` | DeepSeek V4 Pro, V4.1 Flash | OpenAI-compatible |
52
- | `sakana` | Fugu, Fugu Max, Fugu Ultra | OpenAI-compatible |
53
- | `openrouter` | Qwen3.8 Max, or any OpenRouter model id | OpenAI-compatible gateway |
54
- | `huggingface` | Kimi K2.7 Code, DeepSeek V4.1 Flash, GPT-OSS 120B | Inference Providers router, OpenAI-compatible |
55
- | `local` | Any model your server exposes | Ollama, LM Studio, llama.cpp, vLLM; `baseUrl` is required |
41
+ | Provider | Models | Notes |
42
+ | ------------- | --------------------------------------------------------------------- | --------------------------------------------------------- |
43
+ | `anthropic` | Claude Fable 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5 | Extended thinking, prompt caching, server-side compaction |
44
+ | `openai` | GPT-6 Astra, GPT-6.1 Sol, GPT-6 Luna | OAuth (Codex endpoint) or API key |
45
+ | `gemini` | Gemini 3.1 Pro (Preview), 3.8 / 3.7 / 3.5 Flash, 3.5 / 3.1 Flash Lite | OAuth (Code Assist); native video input |
46
+ | `xai` | Grok 4.7 | OpenAI-compatible, `https://api.x.ai/v1` |
47
+ | `moonshot` | Kimi K3, K2.8 Preview, K2.7 Code, K2.7 Code HighSpeed | OpenAI-compatible; native video input |
48
+ | `glm` | GLM-5.3, GLM-5.3-Flash | Z.AI coding endpoint, OpenAI-compatible |
49
+ | `minimax` | MiniMax M3 | Anthropic-compatible endpoint |
50
+ | `xiaomi` | MiMo-V2.6-Pro, MiMo-V2.6-Flash, MiMo-V2.6-Pro-UltraSpeed | OpenAI-compatible |
51
+ | `deepseek` | DeepSeek V4 Pro, V4.1 Flash | OpenAI-compatible |
52
+ | `sakana` | Fugu, Fugu Max, Fugu Ultra | OpenAI-compatible |
53
+ | `openrouter` | Qwen3.8 Max, or any OpenRouter model id | OpenAI-compatible gateway |
54
+ | `huggingface` | Kimi K2.7 Code, DeepSeek V4.1 Flash, GPT-OSS 120B | Inference Providers router, OpenAI-compatible |
55
+ | `local` | Any model your server exposes | Ollama, LM Studio, llama.cpp, vLLM; `baseUrl` is required |
56
56
 
57
57
  The model lists are the ones GG Coder ships in its registry (`@kenkaiiii/gg-core`). `stream()` passes `model` straight through, so any id the provider accepts works. Every provider's default endpoint can be overridden with `baseUrl`, and `providerRegistry.register()` adds your own.
58
58
 
@@ -60,45 +60,45 @@ The model lists are the ones GG Coder ships in its registry (`@kenkaiiii/gg-core
60
60
 
61
61
  ## Stream events
62
62
 
63
- | Event | Description |
64
- |---|---|
65
- | `text_delta` | Incremental text output |
66
- | `thinking_delta` | Reasoning output, from any provider that streams it |
67
- | `toolcall_delta` | Streaming tool call arguments |
68
- | `toolcall_done` | Completed tool call with parsed args |
69
- | `server_toolcall` | Server-side tool invocation |
70
- | `server_toolresult` | Server-side tool result |
71
- | `keepalive` | Provider heartbeat: the stream is alive but has no new content yet |
72
- | `done` | Stream finished, includes stop reason |
73
- | `error` | Error occurred |
63
+ | Event | Description |
64
+ | ------------------- | ------------------------------------------------------------------ |
65
+ | `text_delta` | Incremental text output |
66
+ | `thinking_delta` | Reasoning output, from any provider that streams it |
67
+ | `toolcall_delta` | Streaming tool call arguments |
68
+ | `toolcall_done` | Completed tool call with parsed args |
69
+ | `server_toolcall` | Server-side tool invocation |
70
+ | `server_toolresult` | Server-side tool result |
71
+ | `keepalive` | Provider heartbeat: the stream is alive but has no new content yet |
72
+ | `done` | Stream finished, includes stop reason |
73
+ | `error` | Error occurred |
74
74
 
75
75
  ---
76
76
 
77
77
  ## Options
78
78
 
79
- | Option | Type | Description |
80
- |---|---|---|
81
- | `provider` | `Provider` (any id in the table above) | Required |
82
- | `model` | `string` | Required |
83
- | `messages` | `Message[]` | Required |
84
- | `tools` | `Tool[]` | Tool definitions with Zod schemas |
85
- | `toolChoice` | `"auto" \| "none" \| "required" \| { name }` | Tool selection strategy |
86
- | `serverTools` | `ServerToolDefinition[]` | Server-side tool definitions |
87
- | `maxTokens` | `number` | Max output tokens |
88
- | `temperature` | `number` | Sampling temperature |
89
- | `topP` | `number` | Nucleus sampling |
90
- | `stop` | `string[]` | Stop sequences |
91
- | `thinking` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra"` | Reasoning effort; each provider maps it to what the model supports |
92
- | `apiKey` | `string` | Provider API key |
93
- | `baseUrl` | `string` | Custom endpoint |
94
- | `signal` | `AbortSignal` | Cancellation |
95
- | `cacheRetention` | `"none" \| "short" \| "long"` | Prompt cache preference |
96
- | `promptCacheKey` | `string` | Stable cache routing key (OpenAI, Moonshot, Gemini) |
97
- | `webSearch` | `boolean` | Provider-native web search where supported |
98
- | `compaction` | `boolean` | Server-side compaction (Anthropic only) |
99
- | `clearToolUses` | `boolean` | Server-side clearing of old tool results (Anthropic only) |
100
- | `fetch` | `typeof fetch` | Custom fetch, e.g. for React Native |
101
- | `providerOptions` | `Record<string, unknown>` | Provider-specific body fields; see below |
79
+ | Option | Type | Description |
80
+ | ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------------ |
81
+ | `provider` | `Provider` (any id in the table above) | Required |
82
+ | `model` | `string` | Required |
83
+ | `messages` | `Message[]` | Required |
84
+ | `tools` | `Tool[]` | Tool definitions with Zod schemas |
85
+ | `toolChoice` | `"auto" \| "none" \| "required" \| { name }` | Tool selection strategy |
86
+ | `serverTools` | `ServerToolDefinition[]` | Server-side tool definitions |
87
+ | `maxTokens` | `number` | Max output tokens |
88
+ | `temperature` | `number` | Sampling temperature |
89
+ | `topP` | `number` | Nucleus sampling |
90
+ | `stop` | `string[]` | Stop sequences |
91
+ | `thinking` | `"low" \| "medium" \| "high" \| "xhigh" \| "max" \| "ultra"` | Reasoning effort; each provider maps it to what the model supports |
92
+ | `apiKey` | `string` | Provider API key |
93
+ | `baseUrl` | `string` | Custom endpoint |
94
+ | `signal` | `AbortSignal` | Cancellation |
95
+ | `cacheRetention` | `"none" \| "short" \| "long"` | Prompt cache preference |
96
+ | `promptCacheKey` | `string` | Stable cache routing key (OpenAI, Moonshot, Gemini) |
97
+ | `webSearch` | `boolean` | Provider-native web search where supported |
98
+ | `compaction` | `boolean` | Server-side compaction (Anthropic only) |
99
+ | `clearToolUses` | `boolean` | Server-side clearing of old tool results (Anthropic only) |
100
+ | `fetch` | `typeof fetch` | Custom fetch, e.g. for React Native |
101
+ | `providerOptions` | `Record<string, unknown>` | Provider-specific body fields; see below |
102
102
 
103
103
  ### Provider-specific body fields
104
104
 
@@ -152,6 +152,7 @@ const { results } = await rerank({
152
152
  ```
153
153
 
154
154
  **What `embed()` guarantees**
155
+
155
156
  - Vectors come back in input order, even when the provider returns them out of order (gg-ai sorts by `index`).
156
157
  - Long inputs are split at the provider's per-request limit (table below; override with `batchSize`). Batches run one at a time, in order.
157
158
  - The response is checked: exactly one vector per input, all numbers, all the same length, and `dimensions` long when you asked for that. If any check fails it throws `ProviderError`. Nothing is padded or truncated.
@@ -163,18 +164,19 @@ const { results } = await rerank({
163
164
 
164
165
  ### Provider support
165
166
 
166
- | Provider | `embed` | Max inputs per request | `inputType` | `rerank` |
167
- |---|---|---|---|---|
168
- | `openai` | ✓ `/v1/embeddings` | 2048 | ignored | — |
169
- | `openrouter` | ✓ `/api/v1/embeddings` | 64 (gg-ai default; OpenRouter documents no limit) | `input_type` | ✓ `/api/v1/rerank` |
170
- | `gemini` | ✓ native `batchEmbedContents` | 100 | `taskType` | — |
171
- | `local` | ✓ `{baseUrl}/embeddings` (Ollama, LM Studio, llama.cpp, vLLM) | 64 (gg-ai default) | ignored | ✓ `{baseUrl}/rerank` (llama.cpp `--rerank`, vLLM; Ollama and LM Studio have none and return 404) |
172
- | `palsu` | ✓ deterministic fake | all | ignored | ✓ deterministic fake |
173
- | `anthropic`, `xiaomi`, `glm`, `moonshot`, `minimax`, `deepseek`, `sakana`, `xai`, `huggingface` | — | | | — |
167
+ | Provider | `embed` | Max inputs per request | `inputType` | `rerank` |
168
+ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------ |
169
+ | `openai` | ✓ `/v1/embeddings` | 2048 | ignored | — |
170
+ | `openrouter` | ✓ `/api/v1/embeddings` | 64 (gg-ai default; OpenRouter documents no limit) | `input_type` | ✓ `/api/v1/rerank` |
171
+ | `gemini` | ✓ native `batchEmbedContents` | 100 | `taskType` | — |
172
+ | `local` | ✓ `{baseUrl}/embeddings` (Ollama, LM Studio, llama.cpp, vLLM) | 64 (gg-ai default) | ignored | ✓ `{baseUrl}/rerank` (llama.cpp `--rerank`, vLLM; Ollama and LM Studio have none and return 404) |
173
+ | `palsu` | ✓ deterministic fake | all | ignored | ✓ deterministic fake |
174
+ | `anthropic`, `xiaomi`, `glm`, `moonshot`, `minimax`, `deepseek`, `sakana`, `xai`, `huggingface` | — | | | — |
174
175
 
175
176
  Calling `embed()` or `rerank()` on a provider without that capability throws `GGAIError` with `source: "capability"`. Check first with `providerRegistry.supports(name, "embed")`.
176
177
 
177
178
  Notes:
179
+
178
180
  - **Gemini** embeddings use the Gemini API with an AI Studio key, sent as `x-goog-api-key`. The Code Assist sign-in that `stream()` uses for Gemini chat has no embeddings endpoint.
179
181
  - **OpenAI** embeddings need a platform API key. ChatGPT-subscription (Codex) tokens can't call `/embeddings`.
180
182
  - Anthropic has no embeddings API (it points to Voyage AI). The other providers in the last row don't document an embeddings or rerank endpoint on the API gg-ai talks to, so none is registered for them.
@@ -184,14 +186,14 @@ Notes:
184
186
 
185
187
  Failures use the same error model as `stream()`:
186
188
 
187
- | Failure | Error |
188
- |---|---|
189
- | HTTP 401 / 403 | `ProviderError`, `source: "auth"` |
190
- | HTTP 402, or billing text such as `insufficient_quota` | `ProviderError` with "usage limit reached" (`isUsageLimitError()` is true) |
191
- | HTTP 429 | `ProviderError`, `statusCode: 429`, `resetsAt` from `Retry-After` when sent |
192
- | Other HTTP errors | `ProviderError` with `statusCode` and `requestId` |
193
- | Connection failure | `GGAIError`, `source: "network"` |
194
- | `signal` aborted | `AbortError` (`DOMException`); remaining batches are skipped |
189
+ | Failure | Error |
190
+ | ------------------------------------------------------ | --------------------------------------------------------------------------- |
191
+ | HTTP 401 / 403 | `ProviderError`, `source: "auth"` |
192
+ | HTTP 402, or billing text such as `insufficient_quota` | `ProviderError` with "usage limit reached" (`isUsageLimitError()` is true) |
193
+ | HTTP 429 | `ProviderError`, `statusCode: 429`, `resetsAt` from `Retry-After` when sent |
194
+ | Other HTTP errors | `ProviderError` with `statusCode` and `requestId` |
195
+ | Connection failure | `GGAIError`, `source: "network"` |
196
+ | `signal` aborted | `AbortError` (`DOMException`); remaining batches are skipped |
195
197
 
196
198
  Provider error bodies are trimmed to their message, then redacted and truncated, so API keys never end up in an error message.
197
199
 
@@ -205,7 +207,9 @@ A provider registered at runtime can offer embeddings and reranking too. `stream
205
207
  import { providerRegistry, embed, type Provider, type ProviderEntry } from "@kenkaiiii/gg-ai";
206
208
 
207
209
  providerRegistry.register("my-search", {
208
- stream: () => { throw new Error("chat not supported"); },
210
+ stream: () => {
211
+ throw new Error("chat not supported");
212
+ },
209
213
  embedBatchSize: 128,
210
214
  embed: async ({ model, input, dimensions, apiKey, signal, fetch = globalThis.fetch }) => {
211
215
  const res = await fetch("https://example.com/embed", {
@@ -234,4 +238,4 @@ await embed({ provider: "my-search" as Provider, model: "v1", input: ["hello"] }
234
238
 
235
239
  ## License
236
240
 
237
- MIT
241
+ AGPL-3.0-only. See [LICENSE](../../LICENSE).
package/dist/index.cjs CHANGED
@@ -2068,6 +2068,14 @@ function toCodexReasoningEffort(level, model) {
2068
2068
  if (level !== "ultra") return level;
2069
2069
  return model === "gpt-6-astra" || model === "gpt-6.1-sol" ? "xhigh" : "max";
2070
2070
  }
2071
+ var DECLARED_EFFORT_ORDER = ["low", "medium", "high", "xhigh", "max"];
2072
+ function effortForRequest(level, model, declared) {
2073
+ const rungs = DECLARED_EFFORT_ORDER.filter((rung) => declared?.includes(rung));
2074
+ if (rungs.length === 0) return toOpenAIReasoningEffort(level, model);
2075
+ const wanted = level === "ultra" ? DECLARED_EFFORT_ORDER.length - 1 : DECLARED_EFFORT_ORDER.indexOf(level);
2076
+ const atOrBelow = rungs.filter((rung) => DECLARED_EFFORT_ORDER.indexOf(rung) <= wanted);
2077
+ return atOrBelow[atOrBelow.length - 1] ?? rungs[0];
2078
+ }
2071
2079
  function toOpenAIReasoningEffort(level, model) {
2072
2080
  const effort = level === "max" || level === "ultra" ? "xhigh" : level;
2073
2081
  if (model.startsWith("fugu") && (effort === "low" || effort === "medium")) {
@@ -3103,7 +3111,13 @@ async function* runStream2(options) {
3103
3111
  ...effectiveTemp != null && !options.thinking && !hasFixedKimiSampling ? { temperature: effectiveTemp } : {},
3104
3112
  ...options.topP != null && !hasFixedKimiSampling ? { top_p: options.topP } : {},
3105
3113
  ...options.stop ? { stop: options.stop } : {},
3106
- ...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 && !isLocal ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
3114
+ ...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 && !isLocal ? {
3115
+ reasoning_effort: effortForRequest(
3116
+ options.thinking,
3117
+ options.model,
3118
+ options.thinkingLevels
3119
+ )
3120
+ } : {},
3107
3121
  ...options.tools?.length ? {
3108
3122
  tools: toOpenAITools(options.tools, {
3109
3123
  strict: supportsStrictToolSampling(options.provider) && (options.strictTools ?? true)