@gtrabanco/pi-nan-provider 0.10.3 → 1.0.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/AGENTS.md +68 -9
- package/README.es.md +57 -9
- package/README.md +57 -9
- package/package.json +5 -5
- package/scripts/models.generated.ts +2 -2
- package/src/commands.ts +6 -4
- package/src/context-overflow-classifier.ts +114 -23
- package/src/fetch-models.ts +147 -2
- package/src/images.ts +408 -0
- package/src/index.ts +5 -2
- package/src/provider-factory.ts +102 -30
package/AGENTS.md
CHANGED
|
@@ -65,13 +65,15 @@ factory in `src/provider-factory.ts`. A second provider-specific file is a smell
|
|
|
65
65
|
refactor back to the factory and add a config entry in `src/providers.ts` instead.
|
|
66
66
|
The `factory is shared` test in `test/provider-factory.test.ts` guards this contract.
|
|
67
67
|
|
|
68
|
-
## Version policy (strict semver)
|
|
68
|
+
## Version policy (strict semver, pi-aligned scheme)
|
|
69
69
|
|
|
70
70
|
Every PR that changes code MUST bump `package.json` version in the same PR; CI publishes only when the version differs from npm.
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- **
|
|
72
|
+
Since 1.0.0 the package version **aligns its MAJOR.MINOR with the pi release line the release is verified against** (pi 1.0.0 → package `1.0.x`; when pi ships 1.1.0, the next package release takes `1.1.0` even if it only carries fixes). The version marks the verified baseline, not the full support range — the npm `peerDependencies` range stays independent and usually wider.
|
|
73
|
+
|
|
74
|
+
- **PATCH** (`1.0.z`): bug fixes, docs, comment-only changes, catalog regeneration with identical values — the usual release cadence between pi minor releases.
|
|
75
|
+
- **MINOR** (`1.x.0`): reserved for tracking pi's minor releases (and may carry new features — new provider entries, new MCP tools, new env vars/config options).
|
|
76
|
+
- **MAJOR** (`x.0.0`): breaking changes, and re-alignment if pi ever ships a new major.
|
|
75
77
|
- Never reuse a published version; never publish with failing tests (CI gates publish on tests + typecheck).
|
|
76
78
|
- The npm registry is the source of truth for "published"; `.github/workflows/publish.yml` compares `package.json` against `npm view` and publishes only on difference.
|
|
77
79
|
|
|
@@ -247,23 +249,32 @@ Every PR that changes code MUST bump `package.json` version in the same PR; CI p
|
|
|
247
249
|
- Since 0.10.0, this package uses native MCP via `pi.registerMcpServer()`.
|
|
248
250
|
Both servers are session-scoped, visible in `/mcp` with source "extension".
|
|
249
251
|
Server names and tool IDs:
|
|
250
|
-
- `nan-search` → tools: `
|
|
252
|
+
- `nan-search` → tools: `mcp__nan_search__web_search`
|
|
251
253
|
(official remote, HTTP, `Authorization: Bearer <key>` header)
|
|
252
|
-
- `nan-media` → tools: `
|
|
253
|
-
(community stdio, `npx -y nan-mcp-server@1.1.2`, 120s timeout)
|
|
254
|
+
- `nan-media` → tools: `mcp__nan_media__generate_image/edit_image/text_to_speech/list_voices/speech_to_text/list_models/embed_text/rerank_documents`
|
|
255
|
+
(community stdio, `npx -y nan-mcp-server@1.1.2`, 120s timeout; 8 tools as of 1.1.2)
|
|
254
256
|
- `exposure: "direct"` on both — tools are declared to the model.
|
|
255
257
|
- User `mcp.json` entries with the same name take precedence.
|
|
256
258
|
- `/nan-mcp enable/disable` calls `pi.registerMcpServer()` / `pi.unregisterMcpServer()` directly.
|
|
257
259
|
- Missing `NAN_API_KEY` at load: web-search server NOT registered (console.warn).
|
|
258
260
|
NaN integration facts remain:
|
|
261
|
+
- pi >=0.99.2 sanitizes MCP tool/namespace names by replacing every character except
|
|
262
|
+
`[A-Za-z0-9_]` with `"_"` (0.99.0–0.99.1 kept `-`). So `mcp__nan-search__web_search`
|
|
263
|
+
became `mcp__nan_search__web_search` and `mcp__nan-media__*` became
|
|
264
|
+
`mcp__nan_media__*`. Server names differing only in `-`/`_` now collide
|
|
265
|
+
(`nan-search` and `nan-media` do not collide, they differ in more than one character).
|
|
266
|
+
- pi 0.99.2+/1.0 accept `McpServerConfig.description` (system-prompt listing plus
|
|
267
|
+
tool-search ranking); `validateMcpServerConfig` in 0.99.0–0.99.1 ignores unknown
|
|
268
|
+
keys, so passing `description` stays compatible.
|
|
259
269
|
- NaN's official remote MCP server: `https://api.nan.builders/mcp` (host
|
|
260
270
|
root, NOT /v1; JSON-RPC 2.0 over streamable HTTP, stateless; same `sk-`
|
|
261
271
|
key, shared rate limit/quota). Spec: https://nan.builders/openapi.json
|
|
262
272
|
(tag "MCP"). Currently exposes `web_search`.
|
|
263
273
|
- Community `nan-mcp-server` (https://github.com/luciferfran/nan-mcp-server):
|
|
264
274
|
stdio MCP server, version-pinned via NAN_MEDIA_MCP_VERSION (default 1.1.2)
|
|
265
|
-
or full command override via NAN_MEDIA_MCP_COMMAND. Tools: generate_image,
|
|
266
|
-
edit_image, text_to_speech, list_voices, speech_to_text
|
|
275
|
+
or full command override via NAN_MEDIA_MCP_COMMAND. Tools (1.1.2): generate_image,
|
|
276
|
+
edit_image, text_to_speech, list_voices, speech_to_text, list_models, embed_text,
|
|
277
|
+
rerank_documents.
|
|
267
278
|
- Automated check (scripts/check-nan-mcp-server.ts) compares the npm registry
|
|
268
279
|
against the pin weekly and files a `dependencies` issue when a newer release
|
|
269
280
|
exists, with a breaking/safe verdict from the live server tool surface.
|
|
@@ -282,3 +293,51 @@ Every PR that changes code MUST bump `package.json` version in the same PR; CI p
|
|
|
282
293
|
`mcp_servers_change` so pi's report stays silent, and warns once per session;
|
|
283
294
|
`/nan-mcp enable` persists the toggle without registering. Regression tests:
|
|
284
295
|
`test/mcp-host-connector.test.ts`.
|
|
296
|
+
- **pi 1.0.0 / pi-ai 1.0.0 compat** (verified 2026-10-01): the peer range is now
|
|
297
|
+
`">=0.99.0 <2"` (widened from `">=0.99.0 <1"`); the pi-ai 1.0.0 index/compat/types
|
|
298
|
+
type surfaces are identical to 0.99.1 (the release only adds a `./models` subpath,
|
|
299
|
+
Anthropic federation env consts, and a z.ai CN overflow pattern); the package's
|
|
300
|
+
tests and typecheck pass against 1.0.0 unchanged.
|
|
301
|
+
- **Native image models** (verified 2026-10-01): pi 1.0 lets a provider register
|
|
302
|
+
`ImageModel` entries (`type: "image"`) plus a `Provider.images` implementation map
|
|
303
|
+
keyed by a custom `ImageApi` id (`"nan-images"`); codemode reaches them with
|
|
304
|
+
`models.generateImages()` / `getModelOfType("image", provider, id)`, extensions with
|
|
305
|
+
`ctx.modelRegistry.generateImages()`. pi-ai never applies `filterModels` to
|
|
306
|
+
non-chat models, so image availability is not tier-filtered by the live `/models`
|
|
307
|
+
list — the 403 at call time is the tier signal. NaN endpoints and quotas are
|
|
308
|
+
documented in the package's image-model notes.
|
|
309
|
+
- **Cold-cache 524 wedge: measured practical ceiling + two-threshold classifier**
|
|
310
|
+
(issue #18, measured 2026-10-01 with direct cold curl probes, no pi in the
|
|
311
|
+
loop): api.nan.builders sits behind Cloudflare with a 120 s Proxy Read
|
|
312
|
+
Timeout. A COLD prompt of 208,036 tokens (~960 KB body) answered in 12.9 s;
|
|
313
|
+
~220k and ~286k tokens returned HTTP 524 `origin_response_timeout` at ~126 s
|
|
314
|
+
(JSON body or Cloudflare HTML page). Warm (cached) requests of the same size
|
|
315
|
+
answer in ~10 s. pi-ai (0.99.x–1.0.0) lists "524" in
|
|
316
|
+
`RETRYABLE_PROVIDER_ERROR_PATTERN` (retrying an identical cold payload always
|
|
317
|
+
fails) and `isContextOverflow()` matches nothing → the session wedges.
|
|
318
|
+
`src/context-overflow-classifier.ts` therefore has TWO independent
|
|
319
|
+
thresholds: the 400 branch (issue #3) keys on the model's `contextWindow`;
|
|
320
|
+
the 524 branch keys on `NAN_COLD_CACHE_CEILING_TOKENS` = 200,000 ONLY
|
|
321
|
+
(conservative midpoint below the last-working 208k measurement) — the
|
|
322
|
+
declared catalog windows are NOT reduced. A 524 below the ceiling stays a
|
|
323
|
+
transient retryable error. Compaction is a single pass with no hard-truncation
|
|
324
|
+
fallback in pi, so the rewrite rescues sessions that one compaction can bring
|
|
325
|
+
under the ceiling; far-over sessions still wedge (same limit as the #3 path).
|
|
326
|
+
- **Stale persisted catalog shadowing (issue #19, verified on pi-ai/pi 1.0.0):**
|
|
327
|
+
pi-ai's `createProvider`-built `refreshModels` restores
|
|
328
|
+
`context.stored.models` VERBATIM in the cache-only phase
|
|
329
|
+
(`dynamicModels = restored`, a closure variable unreachable from outside) and
|
|
330
|
+
`currentModels()` lets a dynamic entry REPLACE the static baseline entry with
|
|
331
|
+
the same id+type — so a `models-store.json` entry written by an older package
|
|
332
|
+
version (e.g. without `thinkingLevelMap`) shadows the newer generated
|
|
333
|
+
capability data whenever its `checkedAt` is fresh; `ModelsStoreEntry` has no
|
|
334
|
+
version/staleness signal. pi's `ModelsImpl` resolves models exclusively via
|
|
335
|
+
the provider object's `getModels()`/`getAllModels()`. Fix: `src/provider-factory.ts`
|
|
336
|
+
wraps both accessors (`withStaleCatalogOverlay`) to apply generated-catalog
|
|
337
|
+
capability data at READ time (chat ids from the generated catalog, image ids
|
|
338
|
+
from `baselineImageModels`) — the store decides which ids exist, the
|
|
339
|
+
generated catalog decides capability data; uncatalogued ids (live-only
|
|
340
|
+
`glm5.3`, ghosts) pass through untouched. `refreshModels` is NOT overridden:
|
|
341
|
+
the network phase already persists correct merged data. Option (a) of the
|
|
342
|
+
reporter (version-stamping persisted entries) is NOT implementable
|
|
343
|
+
provider-side: the store write path belongs to pi-ai.
|
package/README.es.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @gtrabanco/pi-nan-provider
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://github.com/gtrabanco/pi-nan-provider/releases)
|
|
5
5
|
|
|
6
6
|
[NaN Builders](https://nan.builders) model provider + MCP nativo para [pi](https://github.com/earendil-works/pi).
|
|
7
7
|
|
|
@@ -101,10 +101,13 @@ Consigue una clave en la [plataforma NaN](https://cloud.nan.builders/r/7GK06FX8)
|
|
|
101
101
|
|
|
102
102
|
## 🔌 MCP nativo
|
|
103
103
|
|
|
104
|
-
Dado que [pi 0.99.0 incluye un cliente MCP integrado](https://github.com/earendil-works/pi/blob/main/docs/usage.md), este paquete migra sus puentes a MCP nativo (requiere `pi >=0.99` —
|
|
104
|
+
Dado que [pi 0.99.0 incluye un cliente MCP integrado](https://github.com/earendil-works/pi/blob/main/docs/usage.md), este paquete migra sus puentes a MCP nativo (requiere `pi >=0.99, <2` — pi 0.99 hasta 1.0 son compatibles; el rango peer se amplió a `<2` en 0.11.0).
|
|
105
105
|
|
|
106
106
|
Ambos puentes están **activados por defecto** (por sesión, visibles en `/mcp`). Usa `/nan-mcp` para gestionarlos.
|
|
107
107
|
|
|
108
|
+
> [!TIP]
|
|
109
|
+
> ¿Prefieres configurar los servidores MCP de NaN tú mismo? Desde pi 0.99.2/1.0, un servidor MCP HTTP en el `mcp.json` global puede autenticarse con tu token de `/login nan` en vez de una clave copiada: `{ "auth": { "provider": "nan" } }` (solo en el `mcp.json` global; requiere `https`). El registro integrado de `nan-search` sigue usando `NAN_API_KEY`/la credencial almacenada.
|
|
110
|
+
|
|
108
111
|
### 🛠️ Comando de Gestión: `/nan-mcp`
|
|
109
112
|
|
|
110
113
|
| Comando | Efecto |
|
|
@@ -132,7 +135,7 @@ con un único aviso por sesión, mantiene los registros inactivos y deja que `/n
|
|
|
132
135
|
el toggle sin registrar un servidor que nunca podrá conectarse. Para tener MCP en la CLI, activa el
|
|
133
136
|
conector con `pi config` → Built-in extensions → `mcp`.
|
|
134
137
|
|
|
135
|
-
### Funciones de pi 0.99 y modelos NaN
|
|
138
|
+
### Funciones de pi 0.99-1.0 y modelos NaN
|
|
136
139
|
|
|
137
140
|
- **Modelos virtuales** — registra un router bajo `nan` (`pi.registerVirtualModel({ provider: "nan", ... })`);
|
|
138
141
|
enruta a los modelos físicos de NaN sin configuración por parte del proveedor. El guard de
|
|
@@ -145,12 +148,36 @@ conector con `pi config` → Built-in extensions → `mcp`.
|
|
|
145
148
|
endpoints de embedding/rerank/audio/imagen), así que este proveedor no registra modelos
|
|
146
149
|
`type: "classifier"`.
|
|
147
150
|
|
|
151
|
+
- **Modelos de imagen nativos de NaN** — flux-2-klein (text-to-image + image-to-image) y
|
|
152
|
+
qwen-image-2.1 (text-to-image) se registran como modelos de imagen de pi (`type: "image"`,
|
|
153
|
+
api `"nan-images"`). No aparecen en `/model`; se acceden desde codemode
|
|
154
|
+
con `models.generateImages(model, { input })` o desde extensiones con
|
|
155
|
+
`ctx.modelRegistry.generateImages()`:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
const painter = await models.getModelOfType("image", "nan", "flux-2-klein")
|
|
159
|
+
const result = await models.generateImages(painter, { input: [{ type: "text", text: "A red fox in the snow, watercolor" }] })
|
|
160
|
+
if (result.stopReason !== "stop") return result.errorMessage
|
|
161
|
+
for (const block of result.output) if (block.type === "image") image(block)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Ambos modelos necesitan el tier de membresía "inference" (403 de lo contrario); los endpoints
|
|
165
|
+
de imagen tienen su propio límite de tasa separado del chat (20 peticiones/min, 100
|
|
166
|
+
peticiones/mes compartido entre los dos modelos) y no consumen el presupuesto de tokens del
|
|
167
|
+
chat. [docs de NaN](https://nan.builders/docs/models) y [OpenAPI](https://nan.builders/openapi.json)
|
|
168
|
+
(comprobado 2026-10-01). El servidor MCP nan-media de la comunidad ya ofrece la misma
|
|
169
|
+
generación y edición como herramientas MCP, mientras que los modelos de imagen nativos permiten
|
|
170
|
+
que codemode y las extensiones los llamen directamente.
|
|
171
|
+
|
|
148
172
|
---
|
|
149
173
|
|
|
150
174
|
### 1. Servidor MCP oficial de NaN
|
|
151
175
|
*Puente oficial para herramientas remotas vía [https://api.nan.builders/mcp](https://nan.builders/docs/api).*
|
|
152
176
|
|
|
153
|
-
- **`
|
|
177
|
+
- **`mcp__nan_search__web_search(query, ...)`**: Realiza búsquedas web a través del gateway de NaN.
|
|
178
|
+
> pi 0.99.0–0.99.1 usaba la forma con guiones (`mcp__nan-search__web_search`); pi 0.99.2+ sanitiza
|
|
179
|
+
> los nombres de herramientas a solo `[A-Za-z0-9_]`, así que los guiones se convirtieron en
|
|
180
|
+
> guiones bajos. La forma con guiones bajos aplica desde 0.99.2 hasta pi 1.0.
|
|
154
181
|
|
|
155
182
|
### 2. Servidor MCP de Media (Comunidad)
|
|
156
183
|
*Conecta [`nan-mcp-server`](https://github.com/luciferfran/nan-mcp-server) mediante un cliente stdio local mínimo.*
|
|
@@ -160,11 +187,19 @@ conector con `pi config` → Built-in extensions → `mcp`.
|
|
|
160
187
|
|
|
161
188
|
| Herramienta | Propósito |
|
|
162
189
|
| :--- | :--- |
|
|
163
|
-
| `
|
|
164
|
-
| `
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
190
|
+
| `mcp__nan_media__generate_image` | Generación de imágenes (flux-2-klein) |
|
|
191
|
+
| `mcp__nan_media__edit_image` | Edición imagen→imagen (flux-2-klein) |
|
|
192
|
+
| `mcp__nan_media__text_to_speech` | Síntesis de audio (kokoro) |
|
|
193
|
+
| `mcp__nan_media__list_voices` | Listar voces disponibles |
|
|
194
|
+
| `mcp__nan_media__speech_to_text` | Transcripción de audio (whisper) |
|
|
195
|
+
| `mcp__nan_media__list_models` | Listar los ids de modelos que alcanza la key |
|
|
196
|
+
| `mcp__nan_media__embed_text` | Embeddings de texto (qwen3-embedding) |
|
|
197
|
+
| `mcp__nan_media__rerank_documents` | Reordenación de documentos (Qwen3-Reranker) |
|
|
198
|
+
|
|
199
|
+
> pi 0.99.0–0.99.1 usaba las formas con guiones (`mcp__nan-search__web_search`,
|
|
200
|
+
> `mcp__nan-media__*`); pi 0.99.2+ sanitiza los nombres de herramientas a solo
|
|
201
|
+
> `[A-Za-z0-9_]`, así que los guiones se convirtieron en guiones bajos. La forma
|
|
202
|
+
> con guiones bajos aplica desde 0.99.2 hasta pi 1.0.
|
|
168
203
|
|
|
169
204
|
#### 🔧 Configuración del Puente de Media
|
|
170
205
|
|
|
@@ -289,6 +324,8 @@ Los errores siguen siendo accionables: `401` → ejecuta `/login nan` o corrige
|
|
|
289
324
|
|
|
290
325
|
Catálogo base (verificado contra [docs de NaN](https://nan.builders/docs/models) y [OpenAPI](https://nan.builders/openapi.json)).
|
|
291
326
|
|
|
327
|
+
Modelos de chat:
|
|
328
|
+
|
|
292
329
|
| Modelo | Contexto | Máx. Salida | Entrada | Razonamiento |
|
|
293
330
|
| :--- | :--- | :--- | :--- | :---: |
|
|
294
331
|
| `qwen3.6` | 262,144 | 65,536 | texto, imagen | ✅ |
|
|
@@ -298,6 +335,17 @@ Catálogo base (verificado contra [docs de NaN](https://nan.builders/docs/models
|
|
|
298
335
|
| `glm5.3-flash` | 1,000,000 | 131,072 | texto, imagen | ✅ |
|
|
299
336
|
| `qwen3.8-flash` | 262,144 | 131,072 | texto, imagen | ✅ |
|
|
300
337
|
|
|
338
|
+
> [!NOTE]
|
|
339
|
+
> **Techo práctico con caché de prompt fría (issue #18).** Las ventanas de contexto de la tabla son las ventanas reales de los modelos, pero el borde de api.nan.builders (Cloudflare) aborta las respuestas que superan su Proxy Read Timeout de 120 s. Medido con peticiones frías directas (relleno único, sin caché de prompt; 2026-10-01): 208.036 tokens (~960 KB de cuerpo) respondió en 12,9 s mientras que ~220k y ~286k tokens devolvieron **HTTP 524 `origin_response_timeout`** a los ~126 s; los mismos tamaños con caché cálida responden en ~10 s. El techo práctico para peticiones frías queda por tanto muy por debajo de las ventanas declaradas. Desde 1.0.0 el provider reclasifica un 524 en una petición estimada por encima de 200.000 tokens (`NAN_COLD_CACHE_CEILING_TOKENS`) como desbordamiento de contexto para que pi compacte y reintente en vez de quedarse colgado; un 524 en una petición menor se deja intacto (timeout transitorio genuino).
|
|
340
|
+
|
|
341
|
+
Modelos de imagen no-chat (disponibles vía `models.generateImages()` /
|
|
342
|
+
`getModelOfType("image", …)`, no a través de `/model`):
|
|
343
|
+
|
|
344
|
+
| Modelo | Entrada | Propósito |
|
|
345
|
+
| :--- | :--- | :--- |
|
|
346
|
+
| `flux-2-klein` | texto, imagen | Text-to-image + image-to-image |
|
|
347
|
+
| `qwen-image-2.1` | texto | Text-to-image |
|
|
348
|
+
|
|
301
349
|
> [!NOTE]
|
|
302
350
|
> `mimo-v2.6-flash` está servido por NaN y desde el 2026-09-29 aparece también en models.dev (proveedor `nan`); la entrada manual que lo sostenía se conserva como fallback y su nota de procedencia sigue unida a la entrada generada.
|
|
303
351
|
>
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @gtrabanco/pi-nan-provider
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://github.com/gtrabanco/pi-nan-provider/releases)
|
|
5
5
|
|
|
6
6
|
[NaN Builders](https://nan.builders) model provider + native MCP servers for [pi](https://github.com/earendil-works/pi).
|
|
7
7
|
|
|
@@ -101,10 +101,13 @@ Get a key from the [NaN platform](https://cloud.nan.builders/r/7GK06FX8) (user s
|
|
|
101
101
|
|
|
102
102
|
## 🔌 Native MCP Servers
|
|
103
103
|
|
|
104
|
-
Since [pi 0.99.0 ships a built-in MCP client](https://github.com/earendil-works/pi/blob/main/docs/usage.md), this package migrates its bridges to native MCP (requires `pi >=0.99` —
|
|
104
|
+
Since [pi 0.99.0 ships a built-in MCP client](https://github.com/earendil-works/pi/blob/main/docs/usage.md), this package migrates its bridges to native MCP (requires `pi >=0.99, <2` — pi 0.99 through 1.0 are supported; the peer range was widened to `<2` in 0.11.0).
|
|
105
105
|
|
|
106
106
|
Both bridges are **enabled by default** (session-scoped, visible in `/mcp`). Use `/nan-mcp` to manage them.
|
|
107
107
|
|
|
108
|
+
> [!TIP]
|
|
109
|
+
> Prefer to configure NaN's MCP servers yourself? Since pi 0.99.2/1.0 an HTTP MCP server in the global `mcp.json` can authenticate with your `/login nan` token instead of a copied key: `{ "auth": { "provider": "nan" } }` (global `mcp.json` only; `https` required). The bundled `nan-search` registration keeps using `NAN_API_KEY`/the stored credential.
|
|
110
|
+
|
|
108
111
|
### 🛠️ Management Command: `/nan-mcp`
|
|
109
112
|
|
|
110
113
|
| Command | Effect |
|
|
@@ -131,7 +134,7 @@ per session, keeps the registrations idle, and lets `/nan-mcp enable` persist th
|
|
|
131
134
|
registering a server that can never connect. To get MCP in the CLI, enable the connector with
|
|
132
135
|
`pi config` → Built-in extensions → `mcp`.
|
|
133
136
|
|
|
134
|
-
### pi 0.99 features and NaN models
|
|
137
|
+
### pi 0.99-1.0 features and NaN models
|
|
135
138
|
|
|
136
139
|
- **Virtual models** — register a router under `nan` (`pi.registerVirtualModel({ provider: "nan", ... })`);
|
|
137
140
|
it routes to the physical NaN models with no provider-side configuration. The cross-model thinking
|
|
@@ -142,12 +145,36 @@ registering a server that can never connect. To get MCP in the CLI, enable the c
|
|
|
142
145
|
- **Classifier models** — not applicable: NaN exposes no classifier API (chat plus
|
|
143
146
|
embedding/rerank/audio/image endpoints only), so this provider registers no `type: "classifier"` models.
|
|
144
147
|
|
|
148
|
+
- **Native NaN image models** — flux-2-klein (text-to-image + image-to-image) and
|
|
149
|
+
qwen-image-2.1 (text-to-image) are registered as pi image models (`type: "image"`,
|
|
150
|
+
api `"nan-images"`). They do not appear in `/model`; they are reached through codemode
|
|
151
|
+
with `models.generateImages(model, { input })` or from extensions with
|
|
152
|
+
`ctx.modelRegistry.generateImages()`:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
const painter = await models.getModelOfType("image", "nan", "flux-2-klein")
|
|
156
|
+
const result = await models.generateImages(painter, { input: [{ type: "text", text: "A red fox in the snow, watercolor" }] })
|
|
157
|
+
if (result.stopReason !== "stop") return result.errorMessage
|
|
158
|
+
for (const block of result.output) if (block.type === "image") image(block)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Both models need the "inference" membership tier (403 otherwise); the image endpoints
|
|
162
|
+
are rate-limited separately from chat (20 requests/min, 100 requests/month shared
|
|
163
|
+
between the two models) and do not consume the chat token budget.
|
|
164
|
+
[NaN docs](https://nan.builders/docs/models) and [OpenAPI](https://nan.builders/openapi.json)
|
|
165
|
+
(checked 2026-10-01). The community nan-media MCP server already offers the same
|
|
166
|
+
generation and editing as MCP tools, while the native image models let codemode and
|
|
167
|
+
extensions call them directly.
|
|
168
|
+
|
|
145
169
|
---
|
|
146
170
|
|
|
147
171
|
### 1. Official NaN MCP Server
|
|
148
172
|
*Official remote MCP server registered natively via `pi.registerMcpServer()`.*
|
|
149
173
|
|
|
150
|
-
- **`
|
|
174
|
+
- **`mcp__nan_search__web_search(query, ...)`**: Performs web searches through NaN's gateway.
|
|
175
|
+
> pi 0.99.0–0.99.1 used the hyphenated form (`mcp__nan-search__web_search`); pi 0.99.2+ sanitizes
|
|
176
|
+
> tool names to `[A-Za-z0-9_]` only, so hyphens became underscores. The underscore form applies
|
|
177
|
+
> from 0.99.2 onward through pi 1.0.
|
|
151
178
|
|
|
152
179
|
### 2. Community Media MCP Server
|
|
153
180
|
*Bridges [`nan-mcp-server`](https://github.com/luciferfran/nan-mcp-server) via a minimal local stdio client.*
|
|
@@ -157,11 +184,19 @@ registering a server that can never connect. To get MCP in the CLI, enable the c
|
|
|
157
184
|
|
|
158
185
|
| Tool | Purpose |
|
|
159
186
|
| :--- | :--- |
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
164
|
-
| `
|
|
187
|
+
| `mcp__nan_media__generate_image` | Image generation (flux-2-klein) |
|
|
188
|
+
| `mcp__nan_media__edit_image` | Image-to-image editing (flux-2-klein) |
|
|
189
|
+
| `mcp__nan_media__text_to_speech` | Audio synthesis (kokoro) |
|
|
190
|
+
| `mcp__nan_media__list_voices` | List available voices |
|
|
191
|
+
| `mcp__nan_media__speech_to_text` | Audio transcription (whisper) |
|
|
192
|
+
| `mcp__nan_media__list_models` | List the NaN model ids the key can reach |
|
|
193
|
+
| `mcp__nan_media__embed_text` | Text embeddings (qwen3-embedding) |
|
|
194
|
+
| `mcp__nan_media__rerank_documents` | Document reranking (Qwen3-Reranker) |
|
|
195
|
+
|
|
196
|
+
> pi 0.99.0–0.99.1 used the hyphenated forms (`mcp__nan-search__web_search`,
|
|
197
|
+
> `mcp__nan-media__*`); pi 0.99.2+ sanitizes tool names to `[A-Za-z0-9_]` only,
|
|
198
|
+
> so hyphens became underscores. The underscore form applies from 0.99.2 onward
|
|
199
|
+
> through pi 1.0.
|
|
165
200
|
|
|
166
201
|
#### 🔧 Media Bridge Configuration
|
|
167
202
|
|
|
@@ -286,6 +321,8 @@ Failures stay actionable: `401` → run `/login nan` or fix `NAN_API_KEY`, `404`
|
|
|
286
321
|
|
|
287
322
|
Baseline catalog (verified against [NaN docs](https://nan.builders/docs/models) and [OpenAPI](https://nan.builders/openapi.json)).
|
|
288
323
|
|
|
324
|
+
Chat models:
|
|
325
|
+
|
|
289
326
|
| Model | Context | Max Output | Input | Reasoning |
|
|
290
327
|
| :--- | :--- | :--- | :--- | :---: |
|
|
291
328
|
| `qwen3.6` | 262,144 | 65,536 | text, image | ✅ |
|
|
@@ -295,6 +332,17 @@ Baseline catalog (verified against [NaN docs](https://nan.builders/docs/models)
|
|
|
295
332
|
| `glm5.3-flash` | 1,000,000 | 131,072 | text, image | ✅ |
|
|
296
333
|
| `qwen3.8-flash` | 262,144 | 131,072 | text, image | ✅ |
|
|
297
334
|
|
|
335
|
+
> [!NOTE]
|
|
336
|
+
> **Cold prompt cache practical ceiling (issue #18).** The context windows above are the models' real windows, but the api.nan.builders edge (Cloudflare) aborts responses past its 120 s Proxy Read Timeout. Measured with direct cold requests (unique filler, no prompt cache; 2026-10-01): 208,036 tokens (~960 KB body) answered in 12.9 s while ~220k and ~286k tokens returned **HTTP 524 `origin_response_timeout`** at ~126 s; the same sizes with a warm prompt cache answer in ~10 s. The practical ceiling for cold requests is therefore well below the declared windows. Since 1.0.0 the provider reclassifies a 524 on an estimated over-200,000-token request (`NAN_COLD_CACHE_CEILING_TOKENS`) as a context overflow so pi compacts and retries instead of wedging; a 524 on a smaller request is left alone (genuine transient timeout).
|
|
337
|
+
|
|
338
|
+
Non-chat image models (available through `models.generateImages()` /
|
|
339
|
+
`getModelOfType("image", …)`, not through `/model`):
|
|
340
|
+
|
|
341
|
+
| Model | Input | Purpose |
|
|
342
|
+
| :--- | :--- | :--- |
|
|
343
|
+
| `flux-2-klein` | text, image | Text-to-image + image-to-image |
|
|
344
|
+
| `qwen-image-2.1` | text | Text-to-image |
|
|
345
|
+
|
|
298
346
|
> [!NOTE]
|
|
299
347
|
> `mimo-v2.6-flash` is served by NaN and has been listed by models.dev provider `nan` since 2026-09-29. Before that it entered the catalog through a manual-only entry; that entry is kept as a fallback (re-emitted automatically if models.dev drops the model again) and its provenance note stays attached to the generated entry.
|
|
300
348
|
>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtrabanco/pi-nan-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "NaN Builders (api.nan.builders) model provider for pi - OpenAI-compatible registration with a models.dev-generated fallback, tier-aware live catalog, and MCP bridges (official web search + optional community media server)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"check-pi-sdk-versions": "node scripts/check-pi-sdk-versions.mjs --report"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@earendil-works/pi-ai": ">=0.99.0 <
|
|
46
|
-
"@earendil-works/pi-coding-agent": ">=0.99.0 <
|
|
45
|
+
"@earendil-works/pi-ai": ">=0.99.0 <2",
|
|
46
|
+
"@earendil-works/pi-coding-agent": ">=0.99.0 <2"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@earendil-works/pi-ai": "0.
|
|
50
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
49
|
+
"@earendil-works/pi-ai": "1.0.0",
|
|
50
|
+
"@earendil-works/pi-coding-agent": "1.0.0",
|
|
51
51
|
"@types/bun": "1.4",
|
|
52
52
|
"typescript": "7"
|
|
53
53
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This file is auto-generated by scripts/generate-models.ts
|
|
2
2
|
// Do not edit manually — run `bun run generate-models` to update.
|
|
3
3
|
//
|
|
4
|
-
// Source: https://models.dev/api.json (provider "nan"), fetched 2026-10-
|
|
4
|
+
// Source: https://models.dev/api.json (provider "nan"), fetched 2026-10-02T00:02:21.641Z
|
|
5
5
|
// Provenance: every contextWindow/maxTokens/input/cost value traces to
|
|
6
6
|
// models.dev or to the per-entry notes below. Nothing is invented; entries
|
|
7
7
|
// models.dev documents incompletely are omitted and flagged instead.
|
|
@@ -455,7 +455,7 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
|
|
|
455
455
|
export const GENERATED_CATALOG_META = {
|
|
456
456
|
source: "https://models.dev/api.json",
|
|
457
457
|
modelsDevProvider: "nan",
|
|
458
|
-
fetchedAt: "2026-10-
|
|
458
|
+
fetchedAt: "2026-10-02T00:02:21.641Z",
|
|
459
459
|
modelCount: 6,
|
|
460
460
|
models: ["deepseek-v4-flash","gemma4","glm5.3-flash","mimo-v2.6-flash","qwen3.6","qwen3.8-flash"],
|
|
461
461
|
notes: [
|
package/src/commands.ts
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* package registers:
|
|
4
4
|
*
|
|
5
5
|
* - `nan-search` — the official NaN remote MCP server (api.nan.builders/mcp),
|
|
6
|
-
* exposing `
|
|
6
|
+
* exposing `mcp__nan_search__web_search` (pi >=0.99.2). Default: enabled.
|
|
7
|
+
* (pi 0.99.0–0.99.1 used the hyphenated form `mcp__nan-search__web_search`.)
|
|
7
8
|
* - `nan-media` — the community stdio media server (flux-2-klein / kokoro /
|
|
8
|
-
* whisper), exposing `
|
|
9
|
+
* whisper), exposing `mcp__nan_media__generate_image` etc. Default: enabled.
|
|
10
|
+
* (pi 0.99.0–0.99.1 used the hyphenated form `mcp__nan-media__*`.)
|
|
9
11
|
*
|
|
10
12
|
* Both are session-scoped native servers (visible in /mcp, source "extension").
|
|
11
13
|
* /nan-mcp enables/disables them via pi.registerMcpServer / pi.unregisterMcpServer,
|
|
@@ -74,8 +76,8 @@ function serverName(bridge: BridgeKey): string {
|
|
|
74
76
|
/** Describe the bridge in the status message. */
|
|
75
77
|
function bridgeDescription(bridge: BridgeKey): string {
|
|
76
78
|
return bridge === "webSearch"
|
|
77
|
-
? "web-search bridge (official NaN MCP →
|
|
78
|
-
: "nan-mcp-server bridge (community media MCP →
|
|
79
|
+
? "web-search bridge (official NaN MCP → mcp__nan_search__web_search)"
|
|
80
|
+
: "nan-mcp-server bridge (community media MCP → mcp__nan_media__generate_image/edit_image/text_to_speech/list_voices/speech_to_text)";
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
/** Check if a native server of this name is currently registered. */
|
|
@@ -23,22 +23,50 @@
|
|
|
23
23
|
*
|
|
24
24
|
* This module makes the package **re-check the request size on the way out**
|
|
25
25
|
* (one of the two upstream fixes the tracking issue names). After the request is
|
|
26
|
-
* sent and the gateway answers,
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
26
|
+
* sent and the gateway answers, two independent branches reclassify:
|
|
27
|
+
*
|
|
28
|
+
* 1. **HTTP 400 branch** — keys on the **model's context window**. When the
|
|
29
|
+
* terminal error carries NaN's generic 400 marker AND the estimated input
|
|
30
|
+
* tokens exceed the destination model's `contextWindow`, the message is
|
|
31
|
+
* rewritten. A 400 may be caused by other schema violations, so the rewrite
|
|
32
|
+
* conservatively requires the request to be over the window.
|
|
33
|
+
*
|
|
34
|
+
* 2. **HTTP 524 branch** — keys on a **fixed cold-cache ceiling**
|
|
35
|
+
* (`NAN_COLD_CACHE_CEILING_TOKENS`, 200,000). Cloudflare's 524
|
|
36
|
+
* origin_response_timeout fires when a large cold prompt hangs past the 120s
|
|
37
|
+
* proxy read timeout. A 524 on an over-ceiling request is an overflow
|
|
38
|
+
* masquerading as a timeout; a 524 under the ceiling is a genuine transient
|
|
39
|
+
* timeout. The ceiling is a *practical endpoint limit* (Cloudflare 120s vs
|
|
40
|
+
* cold-prompt prefill), NOT a model capability — so the model's
|
|
41
|
+
* `contextWindow` is irrelevant to the 524 reclassify decision. The rewritten
|
|
42
|
+
* message still mentions the model window for user clarity, but the
|
|
43
|
+
* overflow-classification hinges only on the ceiling.
|
|
44
|
+
*
|
|
45
|
+
* The original provider text is preserved in rewritten messages. The
|
|
46
|
+
* reclassification is deliberately conservative so unrelated errors are never
|
|
35
47
|
* mislabelled as overflow.
|
|
36
48
|
*
|
|
49
|
+
* ---
|
|
50
|
+
*
|
|
51
|
+
* Cold-cache 524 (issue #18): large COLD prompts (≳1 MB body / ~220k tokens) to
|
|
52
|
+
* https://api.nan.builders/v1/chat/completions hang past Cloudflare's 120s Proxy
|
|
53
|
+
* Read Timeout → HTTP 524 origin_response_timeout with JSON body
|
|
54
|
+
* {"status":524,"error_name":"origin_response_timeout","retryable":true,...}
|
|
55
|
+
* (sometimes a Cloudflare HTML page for even larger bodies). Measured: cold
|
|
56
|
+
* 208,036 tokens (960 KB) succeeds (TTFT 12.9 s); ~220k and ~286k tokens cold
|
|
57
|
+
* → 524 at ~126 s. pi-ai classifies 524 as RETRYABLE (retrying an identical cold
|
|
58
|
+
* payload always fails) and its `isContextOverflow()` matches nothing, so the
|
|
59
|
+
* session wedges. When the error is a 524 AND the estimated input tokens exceed
|
|
60
|
+
* `NAN_COLD_CACHE_CEILING_TOKENS`, the error is rewritten into a pi-recognizable
|
|
61
|
+
* overflow message so pi compacts and retries instead of wedging. The original
|
|
62
|
+
* provider text is preserved. A 524 on a small request (under the ceiling) is
|
|
63
|
+
* left untouched — it is a genuine transient timeout.
|
|
64
|
+
*
|
|
37
65
|
* Estimation ratio: the same chars/3.47 the issue's offline session measurements
|
|
38
66
|
* use (`chars/3.47 + ~31K tools + ~12K system`). The system prompt and tool
|
|
39
67
|
* schemas are counted explicitly, so the constant is only applied to the
|
|
40
68
|
* message/character budget. This is a heuristic for deciding whether a generic
|
|
41
|
-
* 400 is plausibly an overflow — it never fabricates model metadata.
|
|
69
|
+
* 400 / 524 is plausibly an overflow — it never fabricates model metadata.
|
|
42
70
|
*/
|
|
43
71
|
|
|
44
72
|
import type {
|
|
@@ -52,6 +80,19 @@ import type {
|
|
|
52
80
|
/** The exact generic body NaN's gateway returns for an over-window request. */
|
|
53
81
|
export const NAN_GENERIC_BAD_REQUEST = "Invalid request. Check your request parameters.";
|
|
54
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Cloudflare 524 origin_response_timeout cold-cache ceiling (tokens).
|
|
85
|
+
*
|
|
86
|
+
* Measured (issue #18 repro): cold 208,036 tokens (960 KB) succeeds
|
|
87
|
+
* (TTFT 12.9 s); ~220k and ~286k tokens cold → 524 at ~126 s. 200,000 is a
|
|
88
|
+
* conservative midpoint — well below the working 208k measurement — so the
|
|
89
|
+
* working cold cache path is not affected while larger cold requests hit this
|
|
90
|
+
* rewrite. This is a practical endpoint ceiling (Cloudflare 120s proxy read
|
|
91
|
+
* timeout vs cold prompt prefill), NOT a model capability — contextWindow in
|
|
92
|
+
* the catalog must NOT change.
|
|
93
|
+
*/
|
|
94
|
+
export const NAN_COLD_CACHE_CEILING_TOKENS = 200_000;
|
|
95
|
+
|
|
55
96
|
/**
|
|
56
97
|
* Characters per token used for the request-size estimate. Same ratio as the
|
|
57
98
|
* issue's offline measurements (chars/3.47); a conservative, provider-agnostic
|
|
@@ -93,11 +134,34 @@ export function isGenericNanBadRequest(message: Pick<AssistantMessage, "errorMes
|
|
|
93
134
|
return typeof message?.errorMessage === "string" && message.errorMessage.includes(NAN_GENERIC_BAD_REQUEST);
|
|
94
135
|
}
|
|
95
136
|
|
|
137
|
+
/**
|
|
138
|
+
* True when a terminal error message carries a Cloudflare 524
|
|
139
|
+
* origin_response_timeout marker. Matches the 524 status followed by either
|
|
140
|
+
* `origin_response_timeout` in the JSON body, or Cloudflare HTML markers
|
|
141
|
+
* (`Error 524` / `A timeout occurred`). Conservative: only these known
|
|
142
|
+
* markers are matched, not any arbitrary 524 response.
|
|
143
|
+
*/
|
|
144
|
+
function isCloudflare524(message: Pick<AssistantMessage, "errorMessage"> | undefined): boolean {
|
|
145
|
+
const text = message?.errorMessage;
|
|
146
|
+
if (typeof text !== "string") return false;
|
|
147
|
+
// Match the status code + origin_response_timeout JSON body.
|
|
148
|
+
if (text.includes("524") && text.includes("origin_response_timeout")) return true;
|
|
149
|
+
// Cloudflare HTML error page variant (occurs for very large bodies).
|
|
150
|
+
if (text.includes("524") && (text.includes("Error 524") || text.includes("A timeout occurred"))) return true;
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
96
154
|
/**
|
|
97
155
|
* Reclassify a terminal assistant error as a context overflow when — and only
|
|
98
156
|
* when — NaN returned its generic 400 for a request estimated to exceed the
|
|
99
|
-
* model's context window
|
|
100
|
-
*
|
|
157
|
+
* model's context window, OR when a Cloudflare 524 fires for an over-ceiling
|
|
158
|
+
* cold request. Mutates and returns the message (the same object flows through
|
|
159
|
+
* the error event and `result()`).
|
|
160
|
+
*
|
|
161
|
+
* The two error paths use independent thresholds:
|
|
162
|
+
* - 400: estimated > contextWindow (conservative; a 400 may be schema-related)
|
|
163
|
+
* - 524: estimated > CEILING (a 524 is either overflow or transient;
|
|
164
|
+
* the ceiling is the discriminator)
|
|
101
165
|
*/
|
|
102
166
|
export function classifyContextOverflowError<T extends AssistantMessage>(
|
|
103
167
|
message: T,
|
|
@@ -105,16 +169,43 @@ export function classifyContextOverflowError<T extends AssistantMessage>(
|
|
|
105
169
|
estimatedInputTokens: number,
|
|
106
170
|
): T {
|
|
107
171
|
if (message.stopReason !== "error") return message;
|
|
108
|
-
|
|
109
|
-
const contextWindow = model?.contextWindow
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
message
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
172
|
+
|
|
173
|
+
const contextWindow = model?.contextWindow;
|
|
174
|
+
const hasValidWindow = Number.isFinite(contextWindow) && contextWindow! > 0;
|
|
175
|
+
|
|
176
|
+
// --- 400 branch: keys on the model's context window ---
|
|
177
|
+
if (isGenericNanBadRequest(message) && hasValidWindow
|
|
178
|
+
&& Number.isFinite(estimatedInputTokens)
|
|
179
|
+
&& estimatedInputTokens > contextWindow!) {
|
|
180
|
+
message.errorMessage =
|
|
181
|
+
`Requested token count exceeds the model's maximum context length of ${contextWindow!} tokens ` +
|
|
182
|
+
`(estimated ${Math.ceil(estimatedInputTokens)} input tokens). ` +
|
|
183
|
+
`NaN's gateway answered HTTP 400 "${NAN_GENERIC_BAD_REQUEST}" instead of naming the overflow, ` +
|
|
184
|
+
`so the context must be compacted before retrying.`;
|
|
185
|
+
return message;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// --- 524 branch: keys on the cold-cache ceiling only ---
|
|
189
|
+
// A 524 origin_response_timeout on an over-ceiling request means the edge
|
|
190
|
+
// timed out while prefilling a large cold prompt — it is an overflow, not a
|
|
191
|
+
// transient error. Small-requests that hit 524 are genuine timeouts.
|
|
192
|
+
if (isCloudflare524(message)
|
|
193
|
+
&& Number.isFinite(estimatedInputTokens)
|
|
194
|
+
&& estimatedInputTokens > NAN_COLD_CACHE_CEILING_TOKENS) {
|
|
195
|
+
const windowClause = hasValidWindow
|
|
196
|
+
? ` (declared model window: ${contextWindow!} tokens)`
|
|
197
|
+
: "";
|
|
198
|
+
message.errorMessage =
|
|
199
|
+
`Requested token count exceeds the maximum context length of ${NAN_COLD_CACHE_CEILING_TOKENS} tokens ` +
|
|
200
|
+
`servable with a cold prompt cache through NaN's endpoint` +
|
|
201
|
+
windowClause +
|
|
202
|
+
`, so the context must be compacted before retrying. ` +
|
|
203
|
+
`(estimated ${Math.ceil(estimatedInputTokens)} input tokens; ` +
|
|
204
|
+
`Cloudflare answers HTTP 524 origin_response_timeout past the 120s proxy read timeout). ` +
|
|
205
|
+
`Original error: ${message.errorMessage}.`;
|
|
206
|
+
return message;
|
|
207
|
+
}
|
|
208
|
+
|
|
118
209
|
return message;
|
|
119
210
|
}
|
|
120
211
|
|
|
@@ -178,4 +269,4 @@ export function withContextOverflowClassification(api: ProviderStreams): Provide
|
|
|
178
269
|
streamSimple: (model, context, options) =>
|
|
179
270
|
classifyStreamContextOverflow(api.streamSimple(model, context, options), model, estimateRequestTokens(context)),
|
|
180
271
|
};
|
|
181
|
-
}
|
|
272
|
+
}
|