@kenkaiiii/gg-ai 5.90.0 → 5.91.1
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/LICENSE +661 -21
- package/NOTICE +52 -0
- package/README.md +72 -68
- package/dist/index.cjs +67 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +67 -12
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
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-
|
|
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
|
|
42
|
-
|
|
43
|
-
| `anthropic`
|
|
44
|
-
| `openai`
|
|
45
|
-
| `gemini`
|
|
46
|
-
| `xai`
|
|
47
|
-
| `moonshot`
|
|
48
|
-
| `glm`
|
|
49
|
-
| `minimax`
|
|
50
|
-
| `xiaomi`
|
|
51
|
-
| `deepseek`
|
|
52
|
-
| `sakana`
|
|
53
|
-
| `openrouter`
|
|
54
|
-
| `huggingface` | Kimi K2.7 Code, DeepSeek V4.1 Flash, GPT-OSS 120B
|
|
55
|
-
| `local`
|
|
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
|
|
64
|
-
|
|
65
|
-
| `text_delta`
|
|
66
|
-
| `thinking_delta`
|
|
67
|
-
| `toolcall_delta`
|
|
68
|
-
| `toolcall_done`
|
|
69
|
-
| `server_toolcall`
|
|
70
|
-
| `server_toolresult` | Server-side tool result
|
|
71
|
-
| `keepalive`
|
|
72
|
-
| `done`
|
|
73
|
-
| `error`
|
|
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
|
|
80
|
-
|
|
81
|
-
| `provider`
|
|
82
|
-
| `model`
|
|
83
|
-
| `messages`
|
|
84
|
-
| `tools`
|
|
85
|
-
| `toolChoice`
|
|
86
|
-
| `serverTools`
|
|
87
|
-
| `maxTokens`
|
|
88
|
-
| `temperature`
|
|
89
|
-
| `topP`
|
|
90
|
-
| `stop`
|
|
91
|
-
| `thinking`
|
|
92
|
-
| `apiKey`
|
|
93
|
-
| `baseUrl`
|
|
94
|
-
| `signal`
|
|
95
|
-
| `cacheRetention`
|
|
96
|
-
| `promptCacheKey`
|
|
97
|
-
| `webSearch`
|
|
98
|
-
| `compaction`
|
|
99
|
-
| `clearToolUses`
|
|
100
|
-
| `fetch`
|
|
101
|
-
| `providerOptions` | `Record<string, unknown>`
|
|
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
|
|
167
|
-
|
|
168
|
-
| `openai`
|
|
169
|
-
| `openrouter`
|
|
170
|
-
| `gemini`
|
|
171
|
-
| `local`
|
|
172
|
-
| `palsu`
|
|
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
|
|
188
|
-
|
|
189
|
-
| HTTP 401 / 403
|
|
190
|
-
| HTTP 402, or billing text such as `insufficient_quota` | `ProviderError` with "usage limit reached" (`isUsageLimitError()` is true)
|
|
191
|
-
| HTTP 429
|
|
192
|
-
| Other HTTP errors
|
|
193
|
-
| Connection failure
|
|
194
|
-
| `signal` aborted
|
|
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: () => {
|
|
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
|
-
|
|
241
|
+
AGPL-3.0-only. See [LICENSE](../../LICENSE).
|
package/dist/index.cjs
CHANGED
|
@@ -335,6 +335,8 @@ function formatError(err) {
|
|
|
335
335
|
guidance: fastRejection.guidance
|
|
336
336
|
};
|
|
337
337
|
}
|
|
338
|
+
const miniMax = err.provider === "minimax" ? formatMiniMaxError(err, cleanMessage) : void 0;
|
|
339
|
+
if (miniMax) return miniMax;
|
|
338
340
|
if (isUsageLimitError(err)) {
|
|
339
341
|
const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
|
|
340
342
|
return {
|
|
@@ -441,6 +443,38 @@ function inferSource(err) {
|
|
|
441
443
|
}
|
|
442
444
|
return "ggcoder";
|
|
443
445
|
}
|
|
446
|
+
function formatMiniMaxError(err, cleanMessage) {
|
|
447
|
+
const code = /\((\d{4,5})\)/.exec(err.message)?.[1];
|
|
448
|
+
const base = {
|
|
449
|
+
source: "provider",
|
|
450
|
+
message: cleanMessage,
|
|
451
|
+
provider: err.provider,
|
|
452
|
+
statusCode: err.statusCode,
|
|
453
|
+
...err.requestId ? { requestId: err.requestId } : {}
|
|
454
|
+
};
|
|
455
|
+
if (code === "2049" || code === "1004") {
|
|
456
|
+
return {
|
|
457
|
+
...base,
|
|
458
|
+
headline: "MiniMax didn't accept this API key.",
|
|
459
|
+
guidance: "A MiniMax key only works in the region that issued it (Global: platform.minimax.io, Mainland China: platform.minimaxi.com). Connect MiniMax again with the key so GG Coder can detect its region, and check the key is active."
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (code === "2061") {
|
|
463
|
+
return {
|
|
464
|
+
...base,
|
|
465
|
+
headline: "Your MiniMax plan doesn't include this model.",
|
|
466
|
+
guidance: "Switch to MiniMax M3.1 Flash in the model selector \u2014 it's the model M Plan and Token Plan cover. MiniMax M3 needs a pay-as-you-go key."
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
if (code === "1008") {
|
|
470
|
+
return {
|
|
471
|
+
...base,
|
|
472
|
+
headline: "MiniMax has no balance for this request.",
|
|
473
|
+
guidance: "Check this key's plan is active, or its pay-as-you-go balance, at platform.minimax.io (platform.minimaxi.com for Mainland China keys). Plan keys only cover MiniMax M3.1 Flash. Not a GG Coder issue."
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
return void 0;
|
|
477
|
+
}
|
|
444
478
|
function providerGuidance(provider, message, statusCode) {
|
|
445
479
|
const name = provider ? providerDisplayName(provider) : "the provider";
|
|
446
480
|
const status = provider ? PROVIDER_STATUS_URL[provider] : void 0;
|
|
@@ -1818,6 +1852,13 @@ function isAdaptiveThinkingModel(model) {
|
|
|
1818
1852
|
return /opus-5|opus-4[-.]8|opus-4[-.]7|opus-4[-.]6|sonnet-5|haiku-5|fable-5|mythos-5/.test(model);
|
|
1819
1853
|
}
|
|
1820
1854
|
function toAnthropicThinking(level, maxTokens, model) {
|
|
1855
|
+
if (model.startsWith("MiniMax-M3.1")) {
|
|
1856
|
+
return {
|
|
1857
|
+
thinking: { type: "adaptive" },
|
|
1858
|
+
maxTokens,
|
|
1859
|
+
outputConfig: { effort: level === "ultra" ? "max" : level }
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1821
1862
|
if (isAdaptiveThinkingModel(model)) {
|
|
1822
1863
|
let effort = level;
|
|
1823
1864
|
if (effort === "xhigh" && !/fable-5|mythos-5|opus-5|opus-4-8|opus-4-7|sonnet-5[-.]5|haiku-5[-.]5/.test(model)) {
|
|
@@ -2027,6 +2068,14 @@ function toCodexReasoningEffort(level, model) {
|
|
|
2027
2068
|
if (level !== "ultra") return level;
|
|
2028
2069
|
return model === "gpt-6-astra" || model === "gpt-6.1-sol" ? "xhigh" : "max";
|
|
2029
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
|
+
}
|
|
2030
2079
|
function toOpenAIReasoningEffort(level, model) {
|
|
2031
2080
|
const effort = level === "max" || level === "ultra" ? "xhigh" : level;
|
|
2032
2081
|
if (model.startsWith("fugu") && (effort === "low" || effort === "medium")) {
|
|
@@ -2339,8 +2388,7 @@ async function* runStream(options) {
|
|
|
2339
2388
|
...system ? { system } : {},
|
|
2340
2389
|
...thinking ? { thinking } : {},
|
|
2341
2390
|
...outputConfig ? { output_config: outputConfig } : {},
|
|
2342
|
-
|
|
2343
|
-
...options.topP != null ? { top_p: options.topP } : {},
|
|
2391
|
+
// API version 2026-10-21 (SDK 1.x) rejects temperature/top_p/top_k with a 400.
|
|
2344
2392
|
...options.stop ? { stop_sequences: options.stop } : {},
|
|
2345
2393
|
...(deferredPlan?.live ?? options.tools)?.length || options.serverTools?.length || options.webSearch ? (() => {
|
|
2346
2394
|
const reservedServerNames = /* @__PURE__ */ new Set();
|
|
@@ -2441,7 +2489,7 @@ async function* runStream(options) {
|
|
|
2441
2489
|
yield* synthesizeEventsFromMessage(message);
|
|
2442
2490
|
return messageToResponse(message);
|
|
2443
2491
|
} catch (err) {
|
|
2444
|
-
throw toError(err, { fastMode: useFastMode });
|
|
2492
|
+
throw toError(err, { fastMode: useFastMode, provider: options.provider });
|
|
2445
2493
|
}
|
|
2446
2494
|
}
|
|
2447
2495
|
const contentParts = [];
|
|
@@ -2557,7 +2605,7 @@ async function* runStream(options) {
|
|
|
2557
2605
|
const rawPartial = accum.argsJson;
|
|
2558
2606
|
const snippet = rawPartial.length > 200 ? `${rawPartial.slice(0, 200)}\u2026` : rawPartial;
|
|
2559
2607
|
throw new ProviderError(
|
|
2560
|
-
|
|
2608
|
+
options.provider,
|
|
2561
2609
|
`Tool "${accum.toolName}" input JSON was truncated in the stream (${rawPartial.length} bytes): ${snippet}; ${parseErr.message}`,
|
|
2562
2610
|
{ cause: parseErr }
|
|
2563
2611
|
);
|
|
@@ -2660,15 +2708,15 @@ async function* runStream(options) {
|
|
|
2660
2708
|
}
|
|
2661
2709
|
}
|
|
2662
2710
|
} catch (err) {
|
|
2663
|
-
throw toError(err, { fastMode: useFastMode });
|
|
2711
|
+
throw toError(err, { fastMode: useFastMode, provider: options.provider });
|
|
2664
2712
|
}
|
|
2665
2713
|
if (!receivedAnyEvent) {
|
|
2666
|
-
throw new ProviderError(
|
|
2714
|
+
throw new ProviderError(options.provider, "Stream ended without producing any events.", {
|
|
2667
2715
|
statusCode: 504
|
|
2668
2716
|
});
|
|
2669
2717
|
}
|
|
2670
2718
|
if (stopReason === null) {
|
|
2671
|
-
throw new ProviderError(
|
|
2719
|
+
throw new ProviderError(options.provider, "Stream ended before completion (no stop_reason).", {
|
|
2672
2720
|
statusCode: 504,
|
|
2673
2721
|
cause: { partialContent: contentParts, outputTokens }
|
|
2674
2722
|
});
|
|
@@ -2810,6 +2858,7 @@ function readUnifiedRateLimit(headers) {
|
|
|
2810
2858
|
return { rejected: status === "rejected", ...resetsAt ? { resetsAt } : {} };
|
|
2811
2859
|
}
|
|
2812
2860
|
function toError(err, context) {
|
|
2861
|
+
const provider = context?.provider ?? "anthropic";
|
|
2813
2862
|
if (err instanceof ProviderError) return err;
|
|
2814
2863
|
if (err instanceof import_sdk.default.APIError) {
|
|
2815
2864
|
const errorBody = err.error;
|
|
@@ -2843,22 +2892,22 @@ function toError(err, context) {
|
|
|
2843
2892
|
}
|
|
2844
2893
|
if (isHardBillingMessage(message)) {
|
|
2845
2894
|
const usageMessage = /usage limit reached/i.test(message) ? message : `usage limit reached: ${message}`;
|
|
2846
|
-
return new ProviderError(
|
|
2895
|
+
return new ProviderError(provider, usageMessage, {
|
|
2847
2896
|
statusCode: err.status,
|
|
2848
2897
|
...requestId ? { requestId } : {},
|
|
2849
2898
|
cause: err
|
|
2850
2899
|
});
|
|
2851
2900
|
}
|
|
2852
|
-
return new ProviderError(
|
|
2901
|
+
return new ProviderError(provider, message, {
|
|
2853
2902
|
statusCode: err.status,
|
|
2854
2903
|
...requestId ? { requestId } : {},
|
|
2855
2904
|
cause: err
|
|
2856
2905
|
});
|
|
2857
2906
|
}
|
|
2858
2907
|
if (err instanceof Error) {
|
|
2859
|
-
return new ProviderError(
|
|
2908
|
+
return new ProviderError(provider, err.message, { cause: err });
|
|
2860
2909
|
}
|
|
2861
|
-
return new ProviderError(
|
|
2910
|
+
return new ProviderError(provider, String(err));
|
|
2862
2911
|
}
|
|
2863
2912
|
|
|
2864
2913
|
// src/providers/openai.ts
|
|
@@ -3062,7 +3111,13 @@ async function* runStream2(options) {
|
|
|
3062
3111
|
...effectiveTemp != null && !options.thinking && !hasFixedKimiSampling ? { temperature: effectiveTemp } : {},
|
|
3063
3112
|
...options.topP != null && !hasFixedKimiSampling ? { top_p: options.topP } : {},
|
|
3064
3113
|
...options.stop ? { stop: options.stop } : {},
|
|
3065
|
-
...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 && !isLocal ? {
|
|
3114
|
+
...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 && !isLocal ? {
|
|
3115
|
+
reasoning_effort: effortForRequest(
|
|
3116
|
+
options.thinking,
|
|
3117
|
+
options.model,
|
|
3118
|
+
options.thinkingLevels
|
|
3119
|
+
)
|
|
3120
|
+
} : {},
|
|
3066
3121
|
...options.tools?.length ? {
|
|
3067
3122
|
tools: toOpenAITools(options.tools, {
|
|
3068
3123
|
strict: supportsStrictToolSampling(options.provider) && (options.strictTools ?? true)
|