pi-freeflow 1.32.2 → 1.33.0
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/CHANGELOG.md +21 -0
- package/README.md +7 -9
- package/package.json +1 -1
- package/src/catalog.ts +10 -2
- package/src/index.ts +1 -1
- package/src/models.ts +9 -40
- package/src/opencode-fingerprint.ts +43 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.33.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- One new free model, picked the same way as the rest (`/model` → `freeflow` → pick):
|
|
8
|
+
|
|
9
|
+
- **Ling 3.1 Flash** on KiloCode (`inclusionai/ling-3.1-flash`, short name `ling-3.1-flash:kilo`) — 262K context, text answers.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Remove four free models that stopped serving upstream, so they no longer appear in the model list:
|
|
14
|
+
|
|
15
|
+
- **MiMo V2.5** on OpenCode (`mimo-v2.5-free`) — upstream answers `401 Model is not supported`.
|
|
16
|
+
- **Exo** on OpenCode (`exo-free`) — upstream answers `503 Endpoint is unavailable`.
|
|
17
|
+
- **Space Bunny Alpha** on KiloCode (`stealth/space-bunny-alpha`) — upstream answers `404 does not exist`.
|
|
18
|
+
- **Qwen 3.8 27B** on KiloCode (`qwen/qwen3.8-27b:free`) — upstream answers `404 currently unavailable`.
|
|
19
|
+
|
|
20
|
+
All four are excluded permanently, so a stale on-disk catalog or a later catalog refresh cannot bring them back.
|
|
21
|
+
|
|
22
|
+
- Requests that ask for a thinking level a model does not offer no longer fail: the level is clamped to the highest level that model supports (previously only Muse Spark was covered — now every model is), and a chat-style `response_format` left on a responses request is dropped instead of rejected upstream.
|
|
23
|
+
|
|
3
24
|
## 1.32.2
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pi-freeflow
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 31 free models with up to 1M context. No API keys to manage. Add your own relays to spread requests across more IPs.
|
|
4
4
|
|
|
5
5
|
Thin by design: a model list, a relay proxy, and a log. The host (`pi-ai`) handles thinking, normalization, and provider behavior.
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ Thin by design: a model list, a relay proxy, and a log. The host (`pi-ai`) handl
|
|
|
16
16
|
|
|
17
17
|
| Feature | What it does | Cost |
|
|
18
18
|
| :--- | :--- | :--- |
|
|
19
|
-
| **
|
|
19
|
+
| **31 free models** | 10 from OpenCode Zen, 16 from KiloCode Gateway, 5 from Cline, context windows up to 1M. Full list below. | **$0** |
|
|
20
20
|
| **Relay pool** | Route requests through your own Cloudflare Workers and Vercel Edge relays. Traffic spreads across the pool, or stays on one sticky relay. A relay that rate-limits, times out, or drops the connection cools down while healthy ones take its traffic. | **$0** beyond your platforms' free tiers |
|
|
21
21
|
| **Automatic fallback** | When every relay is cooling down, requests go direct to upstream instead of failing. | **$0** |
|
|
22
22
|
| **Short model names** | Every model has a slash-free, colon-free alias, plus an optional `:effort` suffix for thinking depth. You type `freeflow/<name>`. | **$0** |
|
|
@@ -185,13 +185,13 @@ The same command set works identically in OMP and Pi:
|
|
|
185
185
|
|
|
186
186
|
---
|
|
187
187
|
|
|
188
|
-
###
|
|
188
|
+
### 31 models, one command
|
|
189
189
|
|
|
190
190
|
```bash
|
|
191
191
|
/model → freeflow → pick
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
#### OpenCode Zen (
|
|
194
|
+
#### OpenCode Zen (10 models), Responses and Chat API
|
|
195
195
|
|
|
196
196
|
Good defaults for long coding sessions and agentic work.
|
|
197
197
|
|
|
@@ -199,7 +199,6 @@ Good defaults for long coding sessions and agentic work.
|
|
|
199
199
|
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
200
200
|
| `muse-spark-1.2-contributor-free` | Meta Superintelligence Labs | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh` | ✅ |
|
|
201
201
|
| `muse-spark-1.3-contributor-free` | Meta Superintelligence Labs | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh` | ✅ |
|
|
202
|
-
| `mimo-v2.5-free` | Xiaomi MiMo | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh`\* | ✅ |
|
|
203
202
|
| `mimo-v2.6-flash-free` | Xiaomi MiMo | **1M** (1.048.576) | **131K** (131.072) | `minimal … xhigh`\* | ✅ |
|
|
204
203
|
| `nemotron-3.5-lightning-free` | NVIDIA | **1M** (1.000.000) | **262K** (262.144) | `minimal … xhigh` | ❌ |
|
|
205
204
|
| `nemotron-3-ultra-free` | NVIDIA | **1M** (1.000.000) | **128K** (128.000) | `minimal … xhigh` | ❌ |
|
|
@@ -209,7 +208,7 @@ Good defaults for long coding sessions and agentic work.
|
|
|
209
208
|
| `fledge-alpha-free` | Stealth preview (lab undisclosed) | **1M** (1.048.576) | **131K** (131.072) | `low / high / max` | ✅ |
|
|
210
209
|
| `ling-3.1-flash-free` | Inclusion AI | **262K** (262.144) | **32K** (32.768) | `minimal … max` | ❌ |
|
|
211
210
|
|
|
212
|
-
#### KiloCode Gateway (
|
|
211
|
+
#### KiloCode Gateway (16 models), OpenRouter compatible
|
|
213
212
|
|
|
214
213
|
Keyless access. Short aliases work for every row (the full ID is in parentheses).
|
|
215
214
|
|
|
@@ -229,9 +228,8 @@ Keyless access. Short aliases work for every row (the full ID is in parentheses)
|
|
|
229
228
|
| `content-safety` (`nvidia/...:free`) | NVIDIA | **128K** (128.000) | **8K** (8.192) | ❌ *(non-thinking)* | ✅ |
|
|
230
229
|
| `ling-3.0-flash-sante` (`inclusionai/ling-3.0-flash-sante:free`) | Inclusion AI | **262K** (262.144) | **32K** (32.768) | `minimal…xhigh`\* | ❌ |
|
|
231
230
|
| `step-3.7-flash` (`stepfun/...:free`) | StepFun | **262K** (262.144) | **262K** (262.144) | `minimal…xhigh`\* | ✅ |
|
|
232
|
-
| `space-bunny-alpha` (`stealth/...`) | Stealth preview (lab undisclosed) | **1M** (1.000.000) | **512K** (524.288) | `minimal…xhigh`\* | ✅ |
|
|
233
231
|
| `inkling-small` (`thinkingmachines/inkling-small:free`) | Thinking Machines | **1M** (1.048.576) | **262K** (262.144) | `minimal…xhigh`\* | ✅ |
|
|
234
|
-
| `
|
|
232
|
+
| `ling-3.1-flash-kilo` (`inclusionai/ling-3.1-flash`) | Inclusion AI | **262K** (262.144) | **32K** (32.768) | `minimal…xhigh`\* | ❌ |
|
|
235
233
|
|
|
236
234
|
\* Levels are forwarded as-is through the OpenRouter-style nested `reasoning` parameter; effort mapping is decided by each model. MiMo collapses `minimal→low` and `xhigh→high` upstream, so its selector shows 5 labels but only 3 distinct effort values.
|
|
237
235
|
|
|
@@ -344,7 +342,7 @@ pnpm smoke # verifies extensions/index.ts loads without crashing
|
|
|
344
342
|
```
|
|
345
343
|
src/
|
|
346
344
|
├── index.ts # extension entry, lifecycle hooks
|
|
347
|
-
├── models.ts #
|
|
345
|
+
├── models.ts # 31-model catalog definitions
|
|
348
346
|
├── catalog.ts # model catalog cache (24h disk)
|
|
349
347
|
├── proxy.ts # local proxy server (127.0.0.1:28180)
|
|
350
348
|
├── relay.ts # relay fetch, rolling on retryable failures
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-freeflow",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.33.0",
|
|
5
5
|
"description": "Thin provider for OMP/Pi — model list + dumb relay proxy + log; host pi-ai owns thinking/normalization",
|
|
6
6
|
"main": "extensions/index.ts",
|
|
7
7
|
"types": "src/index.ts",
|
package/src/catalog.ts
CHANGED
|
@@ -42,6 +42,10 @@ import type {
|
|
|
42
42
|
* reproduced across separate runs on different days while every other Zen model answers.
|
|
43
43
|
* - inclusionai/ling-3.0-flash-fin:free: gone from Kilo (2026-10-01) — keyless chat 404
|
|
44
44
|
* "The requested model ... does not exist".
|
|
45
|
+
* - mimo-v2.5-free: decommissioned upstream (2026-10-07) — HTTP 401 "Model mimo-v2.5-free is not supported".
|
|
46
|
+
* - exo-free: unserved upstream (2026-10-07) — HTTP 503 "Error from provider (Console): Upstream request failed: Endpoint is unavailable.".
|
|
47
|
+
* - stealth/space-bunny-alpha: gone from Kilo (2026-10-07) — keyless chat 404 "The requested model ... does not exist".
|
|
48
|
+
* - qwen/qwen3.8-27b:free: gone from Kilo (2026-10-07) — keyless chat 404 "The requested model is currently unavailable.".
|
|
45
49
|
*/
|
|
46
50
|
export const DEAD_MODEL_IDS = new Set<string>([
|
|
47
51
|
"jev-1.13-free",
|
|
@@ -61,13 +65,17 @@ export const DEAD_MODEL_IDS = new Set<string>([
|
|
|
61
65
|
"inclusionai/ling-3.0-flash-vl:free",
|
|
62
66
|
"ling-3.0-flash-fin-free",
|
|
63
67
|
"inclusionai/ling-3.0-flash-fin:free",
|
|
68
|
+
"mimo-v2.5-free",
|
|
69
|
+
"exo-free",
|
|
70
|
+
"stealth/space-bunny-alpha",
|
|
71
|
+
"qwen/qwen3.8-27b:free",
|
|
64
72
|
]);
|
|
65
73
|
/**
|
|
66
74
|
* Free-tier allowlist for anything entering the picker via network or stale disk.
|
|
67
75
|
* Upstream lists paid models alongside free ones (e.g. claude-fable-5-1,
|
|
68
76
|
* claude-opus-4-*, gemini-3-*) so a bare upstream merge leaks paid entries that
|
|
69
77
|
* fail with 401 Missing API key. Known static IDs without a free suffix
|
|
70
|
-
* (e.g. big-pickle,
|
|
78
|
+
* (e.g. big-pickle, inclusionai/ling-3.1-flash) stay allowed via MODEL_MAP.
|
|
71
79
|
*/
|
|
72
80
|
export function isFreeCatalogId(id: string): boolean {
|
|
73
81
|
if (typeof id !== "string" || id.length === 0) return false;
|
|
@@ -100,7 +108,7 @@ export function sanitizeCatalogModels(models: RegisteredModel[]): RegisteredMode
|
|
|
100
108
|
}
|
|
101
109
|
/**
|
|
102
110
|
* In-memory cache of currently active/available free models.
|
|
103
|
-
* Initialized with all
|
|
111
|
+
* Initialized with all 31 verified models for 0ms instant availability.
|
|
104
112
|
*/
|
|
105
113
|
let aliveCatalog: RegisteredModel[] = ALL_MODELS.map((m) => ({
|
|
106
114
|
...m,
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-freeflow — Modular, high-resiliency LLM extension for Pi & Oh My Pi (OMP)
|
|
3
3
|
*
|
|
4
|
-
* Provides access to
|
|
4
|
+
* Provides access to 31 free models (10 OpenCode Zen + 16 KiloCode Gateway + 5 Cline) with:
|
|
5
5
|
* - Single-port daemon reuse on 28180 across concurrent subagents
|
|
6
6
|
* - Multi-cloud rolling egress relays (Vercel Edge, Cloudflare, Deno)
|
|
7
7
|
* - 0ms instant startup with verified static catalog and background live health checks
|
package/src/models.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Static model definitions and upstream routing catalogs for pi-freeflow
|
|
3
3
|
*
|
|
4
|
-
* Defines the
|
|
4
|
+
* Defines the 31 verified free models (live-verified 2026-10-07):
|
|
5
5
|
* - 10 OpenCode Zen models (2 Responses API + 8 Chat Completions)
|
|
6
|
-
* -
|
|
6
|
+
* - 16 KiloCode Keyless Gateway models (OpenRouter format)
|
|
7
7
|
* - 5 Cline direct-only models (per-user pool)
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -50,23 +50,6 @@ export const OPENCODE_MODELS: ModelDef[] = [
|
|
|
50
50
|
max: null,
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
|
-
{
|
|
54
|
-
id: "mimo-v2.5-free",
|
|
55
|
-
name: "MiMo V2.5 [OpenCode]",
|
|
56
|
-
reasoning: true,
|
|
57
|
-
contextWindow: 1_048_576,
|
|
58
|
-
maxTokens: 131_072,
|
|
59
|
-
input: ["text", "image"],
|
|
60
|
-
thinkingLevelMap: {
|
|
61
|
-
off: null,
|
|
62
|
-
minimal: "low",
|
|
63
|
-
low: "low",
|
|
64
|
-
medium: "medium",
|
|
65
|
-
high: "high",
|
|
66
|
-
xhigh: "high",
|
|
67
|
-
max: null,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
53
|
{
|
|
71
54
|
id: "mimo-v2.6-flash-free",
|
|
72
55
|
name: "MiMo V2.6 Flash [OpenCode]",
|
|
@@ -356,19 +339,6 @@ export const KILO_MODELS: ModelDef[] = [
|
|
|
356
339
|
thinkingFormat: "openrouter",
|
|
357
340
|
thinkingLevelMap: KILO_REASONING_MAP,
|
|
358
341
|
},
|
|
359
|
-
{
|
|
360
|
-
// Added 2026-09-28: live on the Kilo free list (isFree:true, 0/0
|
|
361
|
-
// pricing, 1M context, text+image in) + keyless chat 200. Passes
|
|
362
|
-
// isFreeCatalogId via MODEL_MAP (no :free suffix by design).
|
|
363
|
-
id: "stealth/space-bunny-alpha",
|
|
364
|
-
name: "Space Bunny Alpha [Kilo]",
|
|
365
|
-
reasoning: true,
|
|
366
|
-
contextWindow: 1_000_000,
|
|
367
|
-
maxTokens: 524_288,
|
|
368
|
-
input: ["text", "image"],
|
|
369
|
-
thinkingFormat: "openrouter",
|
|
370
|
-
thinkingLevelMap: KILO_REASONING_MAP,
|
|
371
|
-
},
|
|
372
342
|
{
|
|
373
343
|
id: "thinkingmachines/inkling-small:free",
|
|
374
344
|
name: "Inkling Small [Kilo]",
|
|
@@ -390,12 +360,13 @@ export const KILO_MODELS: ModelDef[] = [
|
|
|
390
360
|
thinkingLevelMap: KILO_REASONING_MAP,
|
|
391
361
|
},
|
|
392
362
|
{
|
|
393
|
-
|
|
394
|
-
|
|
363
|
+
// Added 2026-10-07: live on Kilo free list (isFree:true, 0/0 pricing, 262K ctx)
|
|
364
|
+
id: "inclusionai/ling-3.1-flash",
|
|
365
|
+
name: "Ling 3.1 Flash [Kilo]",
|
|
395
366
|
reasoning: true,
|
|
396
367
|
contextWindow: 262_144,
|
|
397
|
-
maxTokens:
|
|
398
|
-
input: ["text"
|
|
368
|
+
maxTokens: 32_768,
|
|
369
|
+
input: ["text"],
|
|
399
370
|
thinkingFormat: "openrouter",
|
|
400
371
|
thinkingLevelMap: KILO_REASONING_MAP,
|
|
401
372
|
},
|
|
@@ -517,7 +488,6 @@ export const MODEL_ALIASES: Record<string, string> = {
|
|
|
517
488
|
"content-safety": "nvidia/nemotron-3.5-content-safety:free",
|
|
518
489
|
"ling-3.0-flash-sante": "inclusionai/ling-3.0-flash-sante:free",
|
|
519
490
|
"step-3.7-flash": "stepfun/step-3.7-flash:free",
|
|
520
|
-
"space-bunny-alpha": "stealth/space-bunny-alpha",
|
|
521
491
|
"inkling-small": "thinkingmachines/inkling-small:free",
|
|
522
492
|
"mimo-v2.6-flash": "mimo-v2.6-flash-free",
|
|
523
493
|
"space-bunny": "space-bunny-free",
|
|
@@ -525,7 +495,7 @@ export const MODEL_ALIASES: Record<string, string> = {
|
|
|
525
495
|
"longcat": "longcat-2.5-preview-free",
|
|
526
496
|
"fledge-alpha": "fledge-alpha-free",
|
|
527
497
|
"ling-3.1-flash": "ling-3.1-flash-free",
|
|
528
|
-
"
|
|
498
|
+
"ling-3.1-flash:kilo": "inclusionai/ling-3.1-flash",
|
|
529
499
|
// provider-prefixed short aliases (slash-normalized)
|
|
530
500
|
"laguna-s-2.1:free": "poolside/laguna-s-2.1:free",
|
|
531
501
|
"laguna-xs-2.1:free": "poolside/laguna-xs-2.1:free",
|
|
@@ -559,9 +529,8 @@ export const CLINE_MODEL_IDS = new Set<string>([
|
|
|
559
529
|
.filter(([_, target]) => CLINE_MODELS.some((cm) => cm.id === target))
|
|
560
530
|
.map(([alias]) => alias),
|
|
561
531
|
]);
|
|
562
|
-
|
|
563
532
|
/**
|
|
564
|
-
* Combined list of all
|
|
533
|
+
* Combined list of all 31 static free models (canonical)
|
|
565
534
|
*/
|
|
566
535
|
export const ALL_MODELS: ModelDef[] = [...OPENCODE_MODELS, ...KILO_MODELS, ...CLINE_MODELS];
|
|
567
536
|
|
|
@@ -38,6 +38,35 @@ import {
|
|
|
38
38
|
type FindGlobRestore,
|
|
39
39
|
type FingerprintToolName,
|
|
40
40
|
} from "./tool-translation.ts";
|
|
41
|
+
import { getModelDef } from "./models.ts";
|
|
42
|
+
import type { ThinkingLevel } from "./types.ts";
|
|
43
|
+
|
|
44
|
+
/** Effort levels in descending priority (off excluded — clamping to off is never correct). */
|
|
45
|
+
const EFFORT_DESCENDING: ThinkingLevel[] = ["max", "xhigh", "high", "medium", "low", "minimal"];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Find the highest supported effort for a model whose thinkingLevelMap
|
|
49
|
+
* declares the requested effort as null. Returns null when no clamp is
|
|
50
|
+
* needed (effort is supported or model has no map).
|
|
51
|
+
*/
|
|
52
|
+
function clampedEffortFor(model: string, effort: string): string | null {
|
|
53
|
+
const def = getModelDef(model);
|
|
54
|
+
if (!def?.thinkingLevelMap) return null;
|
|
55
|
+
const map = def.thinkingLevelMap;
|
|
56
|
+
// "off" means disable reasoning entirely — never clamp it even when map says null
|
|
57
|
+
if (effort.toLowerCase() === "off") return null;
|
|
58
|
+
// "ultra" is an unofficial alias for beyond-max; treat as "max"
|
|
59
|
+
const key = (effort.toLowerCase() === "ultra" ? "max" : effort.toLowerCase()) as ThinkingLevel;
|
|
60
|
+
// Effort is supported (non-null) → no clamp
|
|
61
|
+
if (key in map && map[key] !== null && map[key] !== undefined) return null;
|
|
62
|
+
// Effort key is unknown and not "ultra" → let upstream decide
|
|
63
|
+
if (!(key in map) && effort.toLowerCase() !== "ultra") return null;
|
|
64
|
+
// Find highest supported effort
|
|
65
|
+
for (const level of EFFORT_DESCENDING) {
|
|
66
|
+
if (map[level] !== null && map[level] !== undefined) return level;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
41
70
|
|
|
42
71
|
/**
|
|
43
72
|
* Safely extract the tool name whether formatted in Chat Completions style
|
|
@@ -262,14 +291,15 @@ export function ensureMessagesFingerprintTools(body: Record<string, unknown>, om
|
|
|
262
291
|
* - `max_tokens` / `max_completion_tokens` become `max_output_tokens` (copied
|
|
263
292
|
* when absent, dropped either way).
|
|
264
293
|
* - `temperature` / `top_p` are dropped (Responses rejects them).
|
|
294
|
+
* - `response_format` is dropped (Chat Completions field, Responses rejects it).
|
|
265
295
|
* - `parallel_tool_calls: false` is dropped; absent-or-true rides verbatim.
|
|
266
296
|
* - `tool_choice` is auto-or-absent: `"none"` (string or `{ type: "none" }`)
|
|
267
297
|
* is dropped, everything else rides (named choices still retarget find->glob).
|
|
268
298
|
* - `store` defaults to false (never persisted server-side).
|
|
269
|
-
* -
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
299
|
+
* - Reasoning effort is clamped per model: when the model's thinkingLevelMap
|
|
300
|
+
* declares the requested effort as null (unsupported), the effort is clamped
|
|
301
|
+
* to the highest supported level. Covers all OpenCode models generically
|
|
302
|
+
* (muse-spark, mimo, nemotron, etc.), not just a single model family.
|
|
273
303
|
* Zen-responses only: chat/messages paths keep their own fields, and Kilo/Cline
|
|
274
304
|
* bodies never reach here (the proxy fingerprints Zen bodies exclusively).
|
|
275
305
|
*/
|
|
@@ -287,23 +317,28 @@ export function normalizeResponsesBody(body: Record<string, unknown>): void {
|
|
|
287
317
|
delete body.max_completion_tokens;
|
|
288
318
|
delete body.temperature;
|
|
289
319
|
delete body.top_p;
|
|
320
|
+
delete body.response_format;
|
|
290
321
|
if (body.parallel_tool_calls === false) delete body.parallel_tool_calls;
|
|
291
322
|
const choice = body.tool_choice;
|
|
292
323
|
if (typeof choice === "string" ? choice.toLowerCase() === "none" : (
|
|
293
324
|
choice !== null && typeof choice === "object" && !Array.isArray(choice)
|
|
294
325
|
&& (choice as Record<string, unknown>).type === "none"
|
|
295
326
|
)) delete body.tool_choice;
|
|
296
|
-
if (typeof body.model === "string"
|
|
327
|
+
if (typeof body.model === "string") {
|
|
297
328
|
const reasoning = body.reasoning;
|
|
298
329
|
if (reasoning !== null && typeof reasoning === "object" && !Array.isArray(reasoning)) {
|
|
299
330
|
const effort = (reasoning as Record<string, unknown>).effort;
|
|
300
|
-
if (typeof effort === "string"
|
|
301
|
-
(
|
|
331
|
+
if (typeof effort === "string") {
|
|
332
|
+
const clamped = clampedEffortFor(body.model as string, effort);
|
|
333
|
+
if (clamped !== null) (reasoning as Record<string, unknown>).effort = clamped;
|
|
302
334
|
}
|
|
303
335
|
}
|
|
304
336
|
for (const key of ["reasoning_effort", "reasoningEffort"] as const) {
|
|
305
337
|
const flat = body[key];
|
|
306
|
-
if (typeof flat === "string"
|
|
338
|
+
if (typeof flat === "string") {
|
|
339
|
+
const clamped = clampedEffortFor(body.model as string, flat);
|
|
340
|
+
if (clamped !== null) body[key] = clamped;
|
|
341
|
+
}
|
|
307
342
|
}
|
|
308
343
|
}
|
|
309
344
|
if (body.store === undefined) body.store = false;
|