@gtrabanco/pi-nan-provider 0.6.11 → 0.7.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/README.md CHANGED
@@ -236,9 +236,25 @@ Baseline catalog (verified against [NaN docs](https://nan.builders/docs/models)
236
236
  | `gemma4` | 262,144 | 32,768 | text, image | ✅ |
237
237
  | `deepseek-v4-flash` | 1,000,000 | 384,000 | text, image | ✅ |
238
238
  | `mimo-v2.5` | 1,048,576 | 131,072 | text, image | ✅ |
239
+ | `mimo-v2.6-flash` | 1,048,576 | 131,072 | text, image | ✅ |
239
240
  | `glm5.3-flash` | 1,000,000 | 131,072 | text, image | ✅ |
240
241
  | `qwen3.8-flash` | 262,144 | 131,072 | text, image | ✅ |
241
242
 
243
+ > [!NOTE]
244
+ > `mimo-v2.6-flash` is served by NaN but not yet listed on models.dev provider `nan`; it enters the catalog through a manual-only entry with the same limits as `mimo-v2.5`. The model will be auto-detected from models.dev once added there.
245
+
246
+ ---
247
+
248
+ ## 🧠 Reasoning controls
249
+
250
+ NaN's `reasoning_effort` parameter controls how much the model thinks before answering — but the degree of control varies by model:
251
+
252
+ | Model | Reasoning effort | How it works |
253
+ | :--- | :--- | :--- |
254
+ | `glm5.3`, `glm5.3-flash` | `low` · `medium` · `high` · `max` | Fully controllable — higher values let the model reason longer |
255
+ | `qwen3.6`, `gemma4` | `none` · `minimal` · `low` · `medium` · `high` · `max` | `none`/`minimal` skip reasoning entirely; others cap at 2K / 8K / 16K / 32K tokens |
256
+ | `deepseek-v4-flash`, `qwen3.8-flash`, `mimo-v2.5`, `mimo-v2.6-flash` | *(accepted but not adjustable)* | The parameter is accepted and never rejected, but the model manages its own reasoning depth — it is never an error to send a value these models don't adjust |
257
+
242
258
  ---
243
259
 
244
260
  ## 🚀 Development
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gtrabanco/pi-nan-provider",
3
- "version": "0.6.11",
3
+ "version": "0.7.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",
@@ -38,7 +38,8 @@
38
38
  "check-nan-mcp-server": "bun run scripts/check-nan-mcp-server.ts",
39
39
  "prepublishOnly": "bun run generate-models && bun test && bun run typecheck",
40
40
  "test": "bun test",
41
- "typecheck": "bunx tsc --noEmit"
41
+ "typecheck": "bunx tsc --noEmit",
42
+ "check-pi-sdk-versions": "node scripts/check-pi-sdk-versions.mjs --report"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "@earendil-works/pi-ai": ">=0.83.0 <1",
@@ -21,7 +21,28 @@
21
21
  */
22
22
 
23
23
  import type { GeneratedModelEntry } from "../src/fetch-models.ts";
24
- import { MANUAL_OVERRIDES } from "./manual-overrides.ts";
24
+ import type { ManualModelOverride } from "./manual-overrides.ts";
25
+ import {
26
+ MANUAL_OVERRIDES,
27
+ MANUAL_ONLY_MODEL_IDS,
28
+ REASONING_EFFORT_VALUES,
29
+ } from "./manual-overrides.ts";
30
+
31
+ /**
32
+ * Reasoning effort values sourced from the NaN docs (https://nan.builders/docs/models
33
+ * #controlling-reasoning, checked 2026-09-25). models.dev has no reasoning_effort_values
34
+ * field — only reasoning_options which is [{type:"toggle"}] or [] — so the actual
35
+ * effort levels must be hand-maintained from the docs. An empty array means the
36
+ * parameter is accepted but depth is not adjustable by the user.
37
+ */
38
+ const REASONING_EFFORT_VALUES_FROM_DOCS: Record<string, string[]> = {
39
+ ...REASONING_EFFORT_VALUES,
40
+ // deepseek-v4-flash: any value (no effect) — model decides per request
41
+ "deepseek-v4-flash": [],
42
+ // qwen3.8-flash, mimo-v2.5: accepted, depth not adjustable
43
+ "qwen3.8-flash": [],
44
+ "mimo-v2.5": [],
45
+ };
25
46
 
26
47
  const MODELS_DEV_API_URL = "https://models.dev/api.json";
27
48
  const SOURCE_PROVIDER_ID = "nan";
@@ -158,6 +179,43 @@ function normalizeInput(modalitiesInput: string[] | undefined, modelId: string):
158
179
  return input;
159
180
  }
160
181
 
182
+ /**
183
+ * Build a GeneratedModelEntry for a model that is not yet on models.dev
184
+ * (MANUAL_ONLY_MODEL_IDS). Uses conservative but accurate defaults from the
185
+ * NaN docs so that the live /models refresh does not hand them
186
+ * UNKNOWN_MODEL_LIMITS.
187
+ */
188
+ function buildManualOnlyModelEntry(
189
+ modelId: string,
190
+ detail: string,
191
+ override: ManualModelOverride | undefined,
192
+ ): GeneratedModel {
193
+ const reasoningEffortValues =
194
+ override?.reasoningEffortValues ??
195
+ REASONING_EFFORT_VALUES_FROM_DOCS[modelId] ??
196
+ undefined;
197
+
198
+ return {
199
+ entry: {
200
+ id: modelId,
201
+ name: override?.name ?? modelId,
202
+ reasoning: override?.reasoning ?? true,
203
+ input: override?.input ?? ["text", "image"],
204
+ cost: override?.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
205
+ contextWindow: override?.contextWindow ?? 1_048_576,
206
+ maxTokens: override?.maxTokens ?? 131_072,
207
+ reasoningEffortValues,
208
+ compat: { ...NAN_COMPAT },
209
+ notes: [
210
+ NAN_COMPAT_NOTE,
211
+ `manual-only: "${modelId}" not yet on models.dev provider nan (${detail}).`,
212
+ ...(override ? [override.note] : []),
213
+ ],
214
+ extras: {},
215
+ },
216
+ };
217
+ }
218
+
161
219
  function convertModel(modelId: string, m: ModelsDevModel): GeneratedModel | { skip: string } {
162
220
  const removedReason = PROVIDER_REMOVED_MODEL_IDS[modelId];
163
221
  if (removedReason) {
@@ -184,6 +242,13 @@ function convertModel(modelId: string, m: ModelsDevModel): GeneratedModel | { sk
184
242
  console.log(`generate-models: manual override for "${modelId}" (${overridden.join(", ")})`);
185
243
  }
186
244
 
245
+ // Reasoning effort values sourced from the NaN docs (models.dev does not
246
+ // expose them — only reasoning_options: [{type:"toggle"}] or []).
247
+ // An empty array means the parameter is accepted but depth is not
248
+ // adjustable by the user; the model manages its own reasoning depth.
249
+ const reasoningEffortValues =
250
+ override?.reasoningEffortValues ?? REASONING_EFFORT_VALUES_FROM_DOCS[modelId] ?? undefined;
251
+
187
252
  return {
188
253
  entry: {
189
254
  id: modelId,
@@ -198,6 +263,7 @@ function convertModel(modelId: string, m: ModelsDevModel): GeneratedModel | { sk
198
263
  },
199
264
  contextWindow: override?.contextWindow ?? contextWindow,
200
265
  maxTokens: override?.maxTokens ?? maxTokens,
266
+ reasoningEffortValues,
201
267
  compat: { ...NAN_COMPAT },
202
268
  notes: [
203
269
  NAN_COMPAT_NOTE,
@@ -235,6 +301,17 @@ async function main(): Promise<void> {
235
301
  }
236
302
  }
237
303
 
304
+ // Add manual-only models (not yet on models.dev).
305
+ for (const [modelId, detail] of Object.entries(MANUAL_ONLY_MODEL_IDS)) {
306
+ const override = MANUAL_OVERRIDES[modelId];
307
+ if (override) {
308
+ console.log(`generate-models: manual override for "${modelId}" (${Object.keys(override).filter((k) => k !== "note").join(", ")})`);
309
+ }
310
+ const result = buildManualOnlyModelEntry(modelId, detail, MANUAL_OVERRIDES[modelId]);
311
+ entries.push(result.entry);
312
+ console.log(`generate-models: added manual-only model "${modelId}"`);
313
+ }
314
+
238
315
  entries.sort((a, b) => a.id.localeCompare(b.id));
239
316
 
240
317
  const entryIds = new Set(entries.map((entry) => entry.id));
@@ -35,13 +35,69 @@ export interface ManualModelOverride {
35
35
  contextWindow?: number;
36
36
  /** Max output tokens override. */
37
37
  maxTokens?: number;
38
+ /** Reasoning effort values as declared by NaN docs. An empty array means the parameter is accepted but depth is model-managed (not adjustable by the user). */
39
+ reasoningEffortValues?: string[];
38
40
  /** Required provenance note; emitted verbatim onto the generated entry. */
39
41
  note: string;
40
42
  }
41
43
 
44
+ /**
45
+ * Reasoning effort values sourced from the NaN docs (https://nan.builders/docs/models
46
+ * #controlling-reasoning, checked 2026-09-25).
47
+ *
48
+ * models.dev has NO `reasoning_effort_values` field — only `reasoning_options`
49
+ * (which is [{type:"toggle"}] or []), so this mapping must be hand-maintained
50
+ * from the docs. The values flow through MANUAL_OVERRIDES.reasoningEffortValues
51
+ * and appear in GeneratedModelEntry.reasoningEffortValues, where the pi
52
+ * model-selector can read them to show the correct granularity.
53
+ *
54
+ * Per-model contract from the docs:
55
+ * - glm5.3, glm5.3-flash: fully controllable (low/medium/high/max)
56
+ * - qwen3.6, gemma4: none/minimal skip reasoning, others cap depth
57
+ * - deepseek-v4-flash: any value accepted but model decides per-request
58
+ * - qwen3.8-flash, mimo-v2.5, mimo-v2.6-flash: accepted, depth not adjustable
59
+ *
60
+ * A model with reasoning_effort_values=[] means the parameter is accepted but
61
+ * the model manages its own reasoning depth — it is never an error.
62
+ */
63
+ export const REASONING_EFFORT_VALUES: Record<string, string[]> = {
64
+ "glm5.3": ["low", "medium", "high", "max"],
65
+ "glm5.3-flash": ["low", "medium", "high", "max"],
66
+ "qwen3.6": ["none", "minimal", "low", "medium", "high", "max"],
67
+ "gemma4": ["none", "minimal", "low", "medium", "high", "max"],
68
+ };
69
+
70
+ /**
71
+ * Models that NaN serves but models.dev provider nan hasn't listed yet.
72
+ * Mirrors the generator's MANUAL_ONLY_MODEL_IDS — kept in sync so the
73
+ * generator and the tests share the same source.
74
+ */
75
+ export const MANUAL_ONLY_MODEL_IDS: Record<string, string> = {
76
+ "mimo-v2.6-flash":
77
+ "omnimodal model (text, image, audio input) served by NaN, not yet listed on models.dev provider nan (checked 2026-09-25); included so the live /models refresh does not hand it UNKNOWN_MODEL_LIMITS. Same limits as mimo-v2.5: 1,048,576 / 131,072 / 1.0B monthly quota per member. Reasoning: accepted, depth not adjustable. Tool calling: yes. Streaming: yes. Input modalities (pi-representable): text, image (audio not representable in pi's Model type).",
78
+ };
79
+
80
+ /**
81
+ * Manual-only model entries with the full override shape for test assertions.
82
+ * Mirrors MANUAL_ONLY_MODEL_IDS so the generator and the tests share the
83
+ * same set of models.
84
+ */
85
+ export const MANUAL_ONLY_MODELS: Record<string, ManualModelOverride> = {
86
+ "mimo-v2.6-flash": {
87
+ input: ["text", "image"],
88
+ reasoning: true,
89
+ reasoningEffortValues: [],
90
+ contextWindow: 1_048_576,
91
+ maxTokens: 131_072,
92
+ note: "omnimodal model (text, image, audio input) served by NaN, not yet listed on models.dev provider nan (checked 2026-09-25); included so the live /models refresh does not hand it UNKNOWN_MODEL_LIMITS. Same limits as mimo-v2.5: 1,048,576 / 131,072 / 1.0B monthly quota per member. Reasoning: accepted, depth not adjustable. Tool calling: yes. Streaming: yes. Input modalities (pi-representable): text, image (audio not representable in pi's Model type).",
93
+ },
94
+ };
95
+
42
96
  export const MANUAL_OVERRIDES: Record<string, ManualModelOverride> = {
43
97
  "deepseek-v4-flash": {
44
98
  input: ["text", "image"],
45
- note: "input includes image: NaN serves the Vision-Exp variant ('takes images as input', https://nan.builders/docs/models; the image_url content-parts in https://nan.builders/openapi.json list deepseek-v4-flash among the vision models). models.dev provider nan also lists text+image now (DeepSeek V4.1 Flash entry, checked 2026-09-13; its 2026-09-07 snapshot listed text only), so this override is kept as a pin for the vision capability rather than as a divergence.",
99
+ // Any value accepted but model decides per-request (no effect).
100
+ reasoningEffortValues: [],
101
+ note: "input includes image: NaN serves the Vision-Exp variant ('takes images as input', https://nan.builders/docs/models; the image_url content-parts in https://nan.builders/openapi.json list deepseek-v4-flash among the vision models). models.dev provider nan also lists text+image now (DeepSeek V4.1 Flash entry, checked 2026-09-13; its 2026-09-07 snapshot listed text only), so this override is kept as a pin for the vision capability rather than as a divergence. reasoning_effort_values=[]: the model decides per-request how much to reason (https://nan.builders/docs/models, checked 2026-09-25).",
46
102
  },
47
103
  };
@@ -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-09-22T08:55:04.775Z
4
+ // Source: https://models.dev/api.json (provider "nan"), fetched 2026-09-25T22:43:00.930Z
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.
@@ -28,6 +28,7 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
28
28
  },
29
29
  "contextWindow": 1000000,
30
30
  "maxTokens": 384000,
31
+ "reasoningEffortValues": [],
31
32
  "compat": {
32
33
  "supportsDeveloperRole": false,
33
34
  "supportsReasoningEffort": true,
@@ -37,7 +38,7 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
37
38
  },
38
39
  "notes": [
39
40
  "compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested. supportsFinishReason true (2026-09-13, issue #2): the LiteLLM gateway intermittently closes SSE streams before emitting finish_reason; with true pi-ai raises 'Stream ended without finish_reason', which matches pi-ai's retryable-provider pattern ('ended without') and is retried automatically, whereas false silently synthesized stop/toolUse and stalled the turn mid-answer. supportsUsageInStreaming true (2026-09-16, issue #7): NaN's published schema is silent about stream_options, but the live gateway honors it — two identical streaming calls per model, differing only in stream_options: { include_usage: true }, returned 0 usage chunks without it and exactly 1 with it (prompt/completion/reasoning/cached token counts) on deepseek-v4-flash, glm5.3-flash, qwen3.6, mimo-v2.5 and gemma4, and a real pi session then recorded token counts where it recorded zeros. pi-ai only sends stream_options when this is not false, and the sanitizer forwards it when the model declares true, so chat models opt in by default and usage is reported (issue #4). A model that does not report streaming usage can still opt out per model with a models.json compat override (supportsUsageInStreaming: false); the sanitizer then strips stream_options and the payload stays strict.",
40
- "input includes image: NaN serves the Vision-Exp variant ('takes images as input', https://nan.builders/docs/models; the image_url content-parts in https://nan.builders/openapi.json list deepseek-v4-flash among the vision models). models.dev provider nan also lists text+image now (DeepSeek V4.1 Flash entry, checked 2026-09-13; its 2026-09-07 snapshot listed text only), so this override is kept as a pin for the vision capability rather than as a divergence."
41
+ "input includes image: NaN serves the Vision-Exp variant ('takes images as input', https://nan.builders/docs/models; the image_url content-parts in https://nan.builders/openapi.json list deepseek-v4-flash among the vision models). models.dev provider nan also lists text+image now (DeepSeek V4.1 Flash entry, checked 2026-09-13; its 2026-09-07 snapshot listed text only), so this override is kept as a pin for the vision capability rather than as a divergence. reasoning_effort_values=[]: the model decides per-request how much to reason (https://nan.builders/docs/models, checked 2026-09-25)."
41
42
  ],
42
43
  "extras": {
43
44
  "id": "deepseek-v4-flash",
@@ -89,6 +90,14 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
89
90
  },
90
91
  "contextWindow": 262144,
91
92
  "maxTokens": 32768,
93
+ "reasoningEffortValues": [
94
+ "none",
95
+ "minimal",
96
+ "low",
97
+ "medium",
98
+ "high",
99
+ "max"
100
+ ],
92
101
  "compat": {
93
102
  "supportsDeveloperRole": false,
94
103
  "supportsReasoningEffort": true,
@@ -152,6 +161,12 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
152
161
  },
153
162
  "contextWindow": 1000000,
154
163
  "maxTokens": 131072,
164
+ "reasoningEffortValues": [
165
+ "low",
166
+ "medium",
167
+ "high",
168
+ "max"
169
+ ],
155
170
  "compat": {
156
171
  "supportsDeveloperRole": false,
157
172
  "supportsReasoningEffort": true,
@@ -211,6 +226,7 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
211
226
  },
212
227
  "contextWindow": 1048576,
213
228
  "maxTokens": 131072,
229
+ "reasoningEffortValues": [],
214
230
  "compat": {
215
231
  "supportsDeveloperRole": false,
216
232
  "supportsReasoningEffort": true,
@@ -255,6 +271,35 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
255
271
  }
256
272
  }
257
273
  },
274
+ {
275
+ "id": "mimo-v2.6-flash",
276
+ "name": "mimo-v2.6-flash",
277
+ "reasoning": true,
278
+ "input": [
279
+ "text",
280
+ "image"
281
+ ],
282
+ "cost": {
283
+ "input": 0,
284
+ "output": 0,
285
+ "cacheRead": 0,
286
+ "cacheWrite": 0
287
+ },
288
+ "contextWindow": 1048576,
289
+ "maxTokens": 131072,
290
+ "compat": {
291
+ "supportsDeveloperRole": false,
292
+ "supportsReasoningEffort": true,
293
+ "supportsUsageInStreaming": true,
294
+ "supportsFinishReason": true,
295
+ "maxTokensField": "max_tokens"
296
+ },
297
+ "notes": [
298
+ "compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested. supportsFinishReason true (2026-09-13, issue #2): the LiteLLM gateway intermittently closes SSE streams before emitting finish_reason; with true pi-ai raises 'Stream ended without finish_reason', which matches pi-ai's retryable-provider pattern ('ended without') and is retried automatically, whereas false silently synthesized stop/toolUse and stalled the turn mid-answer. supportsUsageInStreaming true (2026-09-16, issue #7): NaN's published schema is silent about stream_options, but the live gateway honors it — two identical streaming calls per model, differing only in stream_options: { include_usage: true }, returned 0 usage chunks without it and exactly 1 with it (prompt/completion/reasoning/cached token counts) on deepseek-v4-flash, glm5.3-flash, qwen3.6, mimo-v2.5 and gemma4, and a real pi session then recorded token counts where it recorded zeros. pi-ai only sends stream_options when this is not false, and the sanitizer forwards it when the model declares true, so chat models opt in by default and usage is reported (issue #4). A model that does not report streaming usage can still opt out per model with a models.json compat override (supportsUsageInStreaming: false); the sanitizer then strips stream_options and the payload stays strict.",
299
+ "manual-only: \"mimo-v2.6-flash\" not yet on models.dev provider nan (omnimodal model (text, image, audio input) served by NaN, not yet listed on models.dev provider nan (checked 2026-09-25); included so the live /models refresh does not hand it UNKNOWN_MODEL_LIMITS. Same limits as mimo-v2.5: 1,048,576 / 131,072 / 1.0B monthly quota per member. Reasoning: accepted, depth not adjustable. Tool calling: yes. Streaming: yes. Input modalities (pi-representable): text, image (audio not representable in pi's Model type).)."
300
+ ],
301
+ "extras": {}
302
+ },
258
303
  {
259
304
  "id": "qwen3.6",
260
305
  "name": "Qwen3.6 35B-A3B",
@@ -271,6 +316,14 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
271
316
  },
272
317
  "contextWindow": 262144,
273
318
  "maxTokens": 65536,
319
+ "reasoningEffortValues": [
320
+ "none",
321
+ "minimal",
322
+ "low",
323
+ "medium",
324
+ "high",
325
+ "max"
326
+ ],
274
327
  "compat": {
275
328
  "supportsDeveloperRole": false,
276
329
  "supportsReasoningEffort": true,
@@ -334,6 +387,7 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
334
387
  },
335
388
  "contextWindow": 262144,
336
389
  "maxTokens": 131072,
390
+ "reasoningEffortValues": [],
337
391
  "compat": {
338
392
  "supportsDeveloperRole": false,
339
393
  "supportsReasoningEffort": true,
@@ -382,14 +436,15 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
382
436
  export const GENERATED_CATALOG_META = {
383
437
  source: "https://models.dev/api.json",
384
438
  modelsDevProvider: "nan",
385
- fetchedAt: "2026-09-22T08:55:04.775Z",
386
- modelCount: 6,
387
- models: ["deepseek-v4-flash","gemma4","glm5.3-flash","mimo-v2.5","qwen3.6","qwen3.8-flash"],
439
+ fetchedAt: "2026-09-25T22:43:00.930Z",
440
+ modelCount: 7,
441
+ models: ["deepseek-v4-flash","gemma4","glm5.3-flash","mimo-v2.5","mimo-v2.6-flash","qwen3.6","qwen3.8-flash"],
388
442
  notes: [
389
443
  "live-only: \"glm5.3\" kept out of the static catalog (premium-tier model (models.dev now documents it with 1M context / 131,072 max output; NaN docs https://nan.builders/docs/models + https://nan.builders/openapi.json, checked 2026-09-13) kept live-only so a non-premium key never sees a model it cannot call when the live /models fetch is unavailable; premium keys still get it via the /models refresh with conservative placeholder limits)",
390
444
  "provider-removed: \"glm5.2\" excluded from the catalog (removed by NaN (2026-09-05); absent from the official chat model list in https://nan.builders/openapi.json and https://nan.builders/docs/models (checked 2026-09-07) while models.dev provider nan still listed it — excluded so regeneration does not resurrect it)",
391
445
  "compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested. supportsFinishReason true (2026-09-13, issue #2): the LiteLLM gateway intermittently closes SSE streams before emitting finish_reason; with true pi-ai raises 'Stream ended without finish_reason', which matches pi-ai's retryable-provider pattern ('ended without') and is retried automatically, whereas false silently synthesized stop/toolUse and stalled the turn mid-answer. supportsUsageInStreaming true (2026-09-16, issue #7): NaN's published schema is silent about stream_options, but the live gateway honors it — two identical streaming calls per model, differing only in stream_options: { include_usage: true }, returned 0 usage chunks without it and exactly 1 with it (prompt/completion/reasoning/cached token counts) on deepseek-v4-flash, glm5.3-flash, qwen3.6, mimo-v2.5 and gemma4, and a real pi session then recorded token counts where it recorded zeros. pi-ai only sends stream_options when this is not false, and the sanitizer forwards it when the model declares true, so chat models opt in by default and usage is reported (issue #4). A model that does not report streaming usage can still opt out per model with a models.json compat override (supportsUsageInStreaming: false); the sanitizer then strips stream_options and the payload stays strict.",
392
- "input includes image: NaN serves the Vision-Exp variant ('takes images as input', https://nan.builders/docs/models; the image_url content-parts in https://nan.builders/openapi.json list deepseek-v4-flash among the vision models). models.dev provider nan also lists text+image now (DeepSeek V4.1 Flash entry, checked 2026-09-13; its 2026-09-07 snapshot listed text only), so this override is kept as a pin for the vision capability rather than as a divergence.",
446
+ "input includes image: NaN serves the Vision-Exp variant ('takes images as input', https://nan.builders/docs/models; the image_url content-parts in https://nan.builders/openapi.json list deepseek-v4-flash among the vision models). models.dev provider nan also lists text+image now (DeepSeek V4.1 Flash entry, checked 2026-09-13; its 2026-09-07 snapshot listed text only), so this override is kept as a pin for the vision capability rather than as a divergence. reasoning_effort_values=[]: the model decides per-request how much to reason (https://nan.builders/docs/models, checked 2026-09-25).",
447
+ "manual-only: \"mimo-v2.6-flash\" not yet on models.dev provider nan (omnimodal model (text, image, audio input) served by NaN, not yet listed on models.dev provider nan (checked 2026-09-25); included so the live /models refresh does not hand it UNKNOWN_MODEL_LIMITS. Same limits as mimo-v2.5: 1,048,576 / 131,072 / 1.0B monthly quota per member. Reasoning: accepted, depth not adjustable. Tool calling: yes. Streaming: yes. Input modalities (pi-representable): text, image (audio not representable in pi's Model type).).",
393
448
  "contextWindow 262,144: the earlier 1,000,000 override (maintainer-confirmed 2026-09-05) was withdrawn 2026-09-07 — the updated https://nan.builders/docs/models still states '262K token context, the model's native window' and models.dev agrees at 262,144; NaN docs are treated as the most reliable source (maintainer instruction, 2026-09-07)."
394
449
  ],
395
450
  } as const;
@@ -39,6 +39,13 @@ export interface GeneratedModelEntry {
39
39
  cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
40
40
  contextWindow: number;
41
41
  maxTokens: number;
42
+ /**
43
+ * Reasoning effort values as declared by NaN docs.
44
+ * An empty array means the parameter is accepted but depth is not
45
+ * adjustable by the user — the model manages its own reasoning depth.
46
+ * An undefined array means the field was not set (legacy / uncatalogued).
47
+ */
48
+ reasoningEffortValues?: string[];
42
49
  /** Compat applied to every NaN-compatible model (LiteLLM-confirmed, see scripts/generate-models.ts). */
43
50
  compat?: OpenAICompletionsCompat;
44
51
  /** Provenance notes for values overriding models.dev or needing manual confirmation. */
package/src/usage.ts CHANGED
@@ -37,6 +37,7 @@ export interface ModelQuota {
37
37
  export const MODEL_QUOTAS: readonly ModelQuota[] = [
38
38
  { model: "deepseek-v4-flash", label: "DeepSeek V4 Flash", monthlyCap: 3_000_000_000, rollingWindowCap: 0, rollingWindowHours: 0, premium: false },
39
39
  { model: "mimo-v2.5", label: "MiMo V2.5", monthlyCap: 1_000_000_000, rollingWindowCap: 0, rollingWindowHours: 0, premium: false },
40
+ { model: "mimo-v2.6-flash", label: "MiMo V2.6 Flash", monthlyCap: 1_000_000_000, rollingWindowCap: 0, rollingWindowHours: 0, premium: false },
40
41
  { model: "qwen3.6", label: "Qwen 3.6", monthlyCap: 0, rollingWindowCap: 0, rollingWindowHours: 0, premium: false },
41
42
  { model: "gemma4", label: "Gemma 4", monthlyCap: 0, rollingWindowCap: 0, rollingWindowHours: 0, premium: false },
42
43
  { model: "qwen3.8-flash", label: "Qwen 3.8 Flash", monthlyCap: 500_000_000, rollingWindowCap: 0, rollingWindowHours: 0, premium: false },