@gtrabanco/pi-nan-provider 0.2.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/AGENTS.md +84 -0
- package/CLAUDE.md +1 -0
- package/CONTRIBUTING.md +48 -0
- package/LICENSE +21 -0
- package/README.es.md +144 -0
- package/README.md +144 -0
- package/package.json +59 -0
- package/scripts/generate-models.ts +239 -0
- package/scripts/models.generated.ts +440 -0
- package/src/fetch-models.ts +254 -0
- package/src/index.ts +96 -0
- package/src/mcp/nan-media.ts +199 -0
- package/src/mcp/nan-search.ts +220 -0
- package/src/mcp/stdio-client.ts +222 -0
- package/src/provider-factory.ts +96 -0
- package/src/providers.ts +34 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Build-time generation of the NaN-compatible fallback model catalog.
|
|
4
|
+
*
|
|
5
|
+
* Pulls base capability data (context window, max output tokens, modalities,
|
|
6
|
+
* reasoning support, cost) from models.dev for the provider "nan" and emits
|
|
7
|
+
* `scripts/models.generated.ts`, which is committed and bundled into the npm
|
|
8
|
+
* package. Run before publishing (`bun run generate-models`, wired into
|
|
9
|
+
* `prepublishOnly`).
|
|
10
|
+
*
|
|
11
|
+
* Provenance rules (enforced, not decorative):
|
|
12
|
+
* - Every emitted number must come from models.dev or an explicit
|
|
13
|
+
* MANUAL_OVERRIDES note recording where it was confirmed. Nothing invented.
|
|
14
|
+
* - The founding models (qwen3.6, gemma4, deepseek-v4-flash, mimo-v2.5) MUST
|
|
15
|
+
* exist on models.dev with complete limits, or this script exits non-zero.
|
|
16
|
+
* - Any other models.dev entry missing `limit.context`/`limit.output` is
|
|
17
|
+
* skipped and flagged "needs manual verification" — never guessed.
|
|
18
|
+
* - models.dev modalities are intersected with pi's supported input set
|
|
19
|
+
* ("text" | "image"); e.g. mimo-v2.5's audio input is not representable in
|
|
20
|
+
* pi's Model type and is dropped from `input` (noted on the entry).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { GeneratedModelEntry } from "../src/fetch-models.ts";
|
|
24
|
+
|
|
25
|
+
const MODELS_DEV_API_URL = "https://models.dev/api.json";
|
|
26
|
+
const SOURCE_PROVIDER_ID = "nan";
|
|
27
|
+
const FETCH_TIMEOUT_MS = 15_000;
|
|
28
|
+
|
|
29
|
+
/** Models the founding prompt requires in the catalog; absence is fatal. */
|
|
30
|
+
const REQUIRED_MODEL_IDS = ["qwen3.6", "gemma4", "deepseek-v4-flash", "mimo-v2.5"] as const;
|
|
31
|
+
|
|
32
|
+
/** pi's Model.input only supports these values. */
|
|
33
|
+
const PI_SUPPORTED_INPUT = new Set(["text", "image"]);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Manual corrections over models.dev, each with its confirmation source.
|
|
37
|
+
* Applied only to fields NaN's own documentation contradicts or pi cannot
|
|
38
|
+
* represent; the note is emitted onto the generated entry.
|
|
39
|
+
*/
|
|
40
|
+
const MANUAL_OVERRIDES: Record<string, { input?: ("text" | "image")[]; note: string }> = {
|
|
41
|
+
"deepseek-v4-flash": {
|
|
42
|
+
input: ["text", "image"],
|
|
43
|
+
note: "input includes image: NaN serves the Vision-Exp variant (confirmed at https://nan.builders/docs/models, 'takes images as input'); models.dev provider nan lists text only.",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Per-model provenance notes sourced from NaN's own documentation (manual —
|
|
49
|
+
* models.dev has no structured tier/quota data), each stating its source.
|
|
50
|
+
* Empty today: no model in the models.dev `nan` provider is tier-gated
|
|
51
|
+
* (the premium-tier GLM 5.3 is not listed there; only glm5.3-flash is).
|
|
52
|
+
*/
|
|
53
|
+
const MANUAL_NOTES: Record<string, string> = {};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* LiteLLM compat confirmed against the live api.nan.builders gateway by the
|
|
57
|
+
* maintainer's working ~/.pi/agent/models.json config (2026-09-04) — the
|
|
58
|
+
* config this package replaces. NaN's docs example instead sets only
|
|
59
|
+
* `supportsDeveloperRole: true`, but the battle-tested config uses `false`
|
|
60
|
+
* ("system" role; these open models sit behind vLLM/SGLang via LiteLLM, not
|
|
61
|
+
* OpenAI's developer role), plus reasoning_effort forwarding, the classic
|
|
62
|
+
* `max_tokens` field, and usage in streaming.
|
|
63
|
+
*/
|
|
64
|
+
const NAN_COMPAT = {
|
|
65
|
+
supportsDeveloperRole: false,
|
|
66
|
+
supportsReasoningEffort: true,
|
|
67
|
+
supportsUsageInStreaming: true,
|
|
68
|
+
maxTokensField: "max_tokens" as const,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const NAN_COMPAT_NOTE =
|
|
72
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested.";
|
|
73
|
+
|
|
74
|
+
interface ModelsDevModel {
|
|
75
|
+
id?: string;
|
|
76
|
+
name?: string;
|
|
77
|
+
reasoning?: boolean;
|
|
78
|
+
modalities?: { input?: string[]; output?: string[] };
|
|
79
|
+
limit?: { context?: number; output?: number };
|
|
80
|
+
cost?: { input?: number; output?: number; cache_read?: number; cache_write?: number };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface ModelsDevProvider {
|
|
84
|
+
models?: Record<string, ModelsDevModel>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type ModelsDevCatalog = Record<string, ModelsDevProvider>;
|
|
88
|
+
|
|
89
|
+
interface GeneratedModel {
|
|
90
|
+
entry: GeneratedModelEntry;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function fail(message: string): never {
|
|
94
|
+
console.error(`generate-models: ${message}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function fetchModelsDevCatalog(): Promise<ModelsDevCatalog> {
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
101
|
+
try {
|
|
102
|
+
const response = await fetch(MODELS_DEV_API_URL, {
|
|
103
|
+
headers: { Accept: "application/json" },
|
|
104
|
+
signal: controller.signal,
|
|
105
|
+
});
|
|
106
|
+
if (!response.ok) fail(`models.dev returned ${response.status} ${response.statusText}`);
|
|
107
|
+
return (await response.json()) as ModelsDevCatalog;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
fail(
|
|
110
|
+
`failed to fetch ${MODELS_DEV_API_URL}: ${error instanceof Error ? error.message : String(error)}`,
|
|
111
|
+
);
|
|
112
|
+
} finally {
|
|
113
|
+
clearTimeout(timeoutId);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function normalizeInput(modalitiesInput: string[] | undefined, modelId: string): ("text" | "image")[] {
|
|
118
|
+
const input = (modalitiesInput ?? ["text"]).filter((value): value is "text" | "image" =>
|
|
119
|
+
PI_SUPPORTED_INPUT.has(value),
|
|
120
|
+
);
|
|
121
|
+
if (!input.includes("text")) input.unshift("text");
|
|
122
|
+
if (input.length === 0) fail(`model ${modelId}: modalities.input has no pi-representable values`);
|
|
123
|
+
return input;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function convertModel(modelId: string, m: ModelsDevModel): GeneratedModel | { skip: string } {
|
|
127
|
+
const contextWindow = m.limit?.context;
|
|
128
|
+
const maxTokens = m.limit?.output;
|
|
129
|
+
if (typeof contextWindow !== "number" || contextWindow <= 0) {
|
|
130
|
+
return { skip: `needs manual verification: models.dev has no limit.context for "${modelId}"` };
|
|
131
|
+
}
|
|
132
|
+
if (typeof maxTokens !== "number" || maxTokens <= 0) {
|
|
133
|
+
return { skip: `needs manual verification: models.dev has no limit.output for "${modelId}"` };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const override = MANUAL_OVERRIDES[modelId];
|
|
137
|
+
const input = override?.input ?? normalizeInput(m.modalities?.input, modelId);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
entry: {
|
|
141
|
+
id: modelId,
|
|
142
|
+
name: m.name ?? modelId,
|
|
143
|
+
reasoning: m.reasoning === true,
|
|
144
|
+
input,
|
|
145
|
+
cost: {
|
|
146
|
+
input: m.cost?.input ?? 0,
|
|
147
|
+
output: m.cost?.output ?? 0,
|
|
148
|
+
cacheRead: m.cost?.cache_read ?? 0,
|
|
149
|
+
cacheWrite: m.cost?.cache_write ?? 0,
|
|
150
|
+
},
|
|
151
|
+
contextWindow,
|
|
152
|
+
maxTokens,
|
|
153
|
+
compat: { ...NAN_COMPAT },
|
|
154
|
+
notes: [
|
|
155
|
+
NAN_COMPAT_NOTE,
|
|
156
|
+
...(override ? [override.note] : []),
|
|
157
|
+
...(MANUAL_NOTES[modelId] ? [MANUAL_NOTES[modelId]!] : []),
|
|
158
|
+
],
|
|
159
|
+
// Preserve every property models.dev documents for this model verbatim
|
|
160
|
+
// (tier, quotas, release dates, reasoning options, attachments...).
|
|
161
|
+
extras: m as unknown as Record<string, unknown>,
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function main(): Promise<void> {
|
|
167
|
+
console.log(`Fetching models from ${MODELS_DEV_API_URL}...`);
|
|
168
|
+
const catalog = await fetchModelsDevCatalog();
|
|
169
|
+
const sourceProvider = catalog[SOURCE_PROVIDER_ID];
|
|
170
|
+
if (!sourceProvider?.models) {
|
|
171
|
+
fail(`models.dev has no provider "${SOURCE_PROVIDER_ID}" — cannot generate the catalog`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const rawModels = Object.entries(sourceProvider.models);
|
|
175
|
+
const entries: GeneratedModelEntry[] = [];
|
|
176
|
+
const skipped: string[] = [];
|
|
177
|
+
|
|
178
|
+
for (const [modelId, m] of rawModels) {
|
|
179
|
+
const result = convertModel(modelId, m);
|
|
180
|
+
if ("skip" in result) {
|
|
181
|
+
skipped.push(result.skip);
|
|
182
|
+
console.warn(`generate-models: skipped ${result.skip}`);
|
|
183
|
+
} else {
|
|
184
|
+
entries.push(result.entry);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
entries.sort((a, b) => a.id.localeCompare(b.id));
|
|
189
|
+
|
|
190
|
+
const entryIds = new Set(entries.map((entry) => entry.id));
|
|
191
|
+
const missingRequired = REQUIRED_MODEL_IDS.filter((id) => !entryIds.has(id));
|
|
192
|
+
if (missingRequired.length > 0) {
|
|
193
|
+
fail(
|
|
194
|
+
`required models missing from models.dev provider "${SOURCE_PROVIDER_ID}": ${missingRequired.join(", ")} — ` +
|
|
195
|
+
"do NOT invent their data; confirm and add them via MANUAL_OVERRIDES with a source note.",
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const fetchedAt = new Date().toISOString();
|
|
200
|
+
const allNotes = [
|
|
201
|
+
...skipped,
|
|
202
|
+
...new Set(entries.flatMap((entry) => entry.notes ?? [])),
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
const generated = `// This file is auto-generated by scripts/generate-models.ts
|
|
206
|
+
// Do not edit manually — run \`bun run generate-models\` to update.
|
|
207
|
+
//
|
|
208
|
+
// Source: ${MODELS_DEV_API_URL} (provider "${SOURCE_PROVIDER_ID}"), fetched ${fetchedAt}
|
|
209
|
+
// Provenance: every contextWindow/maxTokens/input/cost value traces to
|
|
210
|
+
// models.dev or to the per-entry notes below. Nothing is invented; entries
|
|
211
|
+
// models.dev documents incompletely are omitted and flagged instead.
|
|
212
|
+
//
|
|
213
|
+
// NaN serves these via LiteLLM behind an OpenAI-compatible API; pricing is
|
|
214
|
+
// membership-quota based, which models.dev reports as zero per-token cost.
|
|
215
|
+
|
|
216
|
+
import type { GeneratedModelEntry } from "../src/fetch-models.ts";
|
|
217
|
+
|
|
218
|
+
export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = ${JSON.stringify(entries, null, "\t")};
|
|
219
|
+
|
|
220
|
+
export const GENERATED_CATALOG_META = {
|
|
221
|
+
source: "${MODELS_DEV_API_URL}",
|
|
222
|
+
modelsDevProvider: "${SOURCE_PROVIDER_ID}",
|
|
223
|
+
fetchedAt: "${fetchedAt}",
|
|
224
|
+
modelCount: ${entries.length},
|
|
225
|
+
models: ${JSON.stringify(entries.map((entry) => entry.id))},
|
|
226
|
+
notes: ${JSON.stringify(allNotes, null, "\t")},
|
|
227
|
+
} as const;
|
|
228
|
+
`;
|
|
229
|
+
|
|
230
|
+
const outputPath = new URL("./models.generated.ts", import.meta.url).pathname;
|
|
231
|
+
await Bun.write(outputPath, generated);
|
|
232
|
+
console.log(`Wrote ${outputPath} (${entries.length} models: ${entries.map((e) => e.id).join(", ")})`);
|
|
233
|
+
if (allNotes.length > 0) {
|
|
234
|
+
console.log("Notes recorded in the generated file:");
|
|
235
|
+
for (const note of allNotes) console.log(` - ${note}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
await main();
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
// This file is auto-generated by scripts/generate-models.ts
|
|
2
|
+
// Do not edit manually — run `bun run generate-models` to update.
|
|
3
|
+
//
|
|
4
|
+
// Source: https://models.dev/api.json (provider "nan"), fetched 2026-09-04T18:57:45.925Z
|
|
5
|
+
// Provenance: every contextWindow/maxTokens/input/cost value traces to
|
|
6
|
+
// models.dev or to the per-entry notes below. Nothing is invented; entries
|
|
7
|
+
// models.dev documents incompletely are omitted and flagged instead.
|
|
8
|
+
//
|
|
9
|
+
// NaN serves these via LiteLLM behind an OpenAI-compatible API; pricing is
|
|
10
|
+
// membership-quota based, which models.dev reports as zero per-token cost.
|
|
11
|
+
|
|
12
|
+
import type { GeneratedModelEntry } from "../src/fetch-models.ts";
|
|
13
|
+
|
|
14
|
+
export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
|
|
15
|
+
{
|
|
16
|
+
"id": "deepseek-v4-flash",
|
|
17
|
+
"name": "DeepSeek V4 Flash",
|
|
18
|
+
"reasoning": true,
|
|
19
|
+
"input": [
|
|
20
|
+
"text",
|
|
21
|
+
"image"
|
|
22
|
+
],
|
|
23
|
+
"cost": {
|
|
24
|
+
"input": 0,
|
|
25
|
+
"output": 0,
|
|
26
|
+
"cacheRead": 0,
|
|
27
|
+
"cacheWrite": 0
|
|
28
|
+
},
|
|
29
|
+
"contextWindow": 1000000,
|
|
30
|
+
"maxTokens": 384000,
|
|
31
|
+
"compat": {
|
|
32
|
+
"supportsDeveloperRole": false,
|
|
33
|
+
"supportsReasoningEffort": true,
|
|
34
|
+
"supportsUsageInStreaming": true,
|
|
35
|
+
"maxTokensField": "max_tokens"
|
|
36
|
+
},
|
|
37
|
+
"notes": [
|
|
38
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested.",
|
|
39
|
+
"input includes image: NaN serves the Vision-Exp variant (confirmed at https://nan.builders/docs/models, 'takes images as input'); models.dev provider nan lists text only."
|
|
40
|
+
],
|
|
41
|
+
"extras": {
|
|
42
|
+
"id": "deepseek-v4-flash",
|
|
43
|
+
"name": "DeepSeek V4 Flash",
|
|
44
|
+
"description": "Fast DeepSeek V4 lane for economical reasoning, coding, and long-context work",
|
|
45
|
+
"family": "deepseek-flash",
|
|
46
|
+
"attachment": false,
|
|
47
|
+
"reasoning": true,
|
|
48
|
+
"reasoning_options": [],
|
|
49
|
+
"tool_call": true,
|
|
50
|
+
"structured_output": true,
|
|
51
|
+
"temperature": true,
|
|
52
|
+
"knowledge": "2025-05",
|
|
53
|
+
"release_date": "2026-04-24",
|
|
54
|
+
"last_updated": "2026-04-24",
|
|
55
|
+
"modalities": {
|
|
56
|
+
"input": [
|
|
57
|
+
"text"
|
|
58
|
+
],
|
|
59
|
+
"output": [
|
|
60
|
+
"text"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"open_weights": true,
|
|
64
|
+
"limit": {
|
|
65
|
+
"context": 1000000,
|
|
66
|
+
"output": 384000
|
|
67
|
+
},
|
|
68
|
+
"cost": {
|
|
69
|
+
"input": 0,
|
|
70
|
+
"output": 0
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "gemma4",
|
|
76
|
+
"name": "Gemma 4 26B A4B IT",
|
|
77
|
+
"reasoning": true,
|
|
78
|
+
"input": [
|
|
79
|
+
"text",
|
|
80
|
+
"image"
|
|
81
|
+
],
|
|
82
|
+
"cost": {
|
|
83
|
+
"input": 0,
|
|
84
|
+
"output": 0,
|
|
85
|
+
"cacheRead": 0,
|
|
86
|
+
"cacheWrite": 0
|
|
87
|
+
},
|
|
88
|
+
"contextWindow": 262144,
|
|
89
|
+
"maxTokens": 32768,
|
|
90
|
+
"compat": {
|
|
91
|
+
"supportsDeveloperRole": false,
|
|
92
|
+
"supportsReasoningEffort": true,
|
|
93
|
+
"supportsUsageInStreaming": true,
|
|
94
|
+
"maxTokensField": "max_tokens"
|
|
95
|
+
},
|
|
96
|
+
"notes": [
|
|
97
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested."
|
|
98
|
+
],
|
|
99
|
+
"extras": {
|
|
100
|
+
"id": "gemma4",
|
|
101
|
+
"name": "Gemma 4 26B A4B IT",
|
|
102
|
+
"description": "Open Gemma instruction model for efficient chat and self-hosted deployments",
|
|
103
|
+
"family": "gemma",
|
|
104
|
+
"attachment": true,
|
|
105
|
+
"reasoning": true,
|
|
106
|
+
"reasoning_options": [
|
|
107
|
+
{
|
|
108
|
+
"type": "toggle"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"tool_call": true,
|
|
112
|
+
"structured_output": true,
|
|
113
|
+
"temperature": true,
|
|
114
|
+
"release_date": "2026-04-02",
|
|
115
|
+
"last_updated": "2026-04-02",
|
|
116
|
+
"modalities": {
|
|
117
|
+
"input": [
|
|
118
|
+
"text",
|
|
119
|
+
"image"
|
|
120
|
+
],
|
|
121
|
+
"output": [
|
|
122
|
+
"text"
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"open_weights": true,
|
|
126
|
+
"limit": {
|
|
127
|
+
"context": 262144,
|
|
128
|
+
"output": 32768
|
|
129
|
+
},
|
|
130
|
+
"cost": {
|
|
131
|
+
"input": 0,
|
|
132
|
+
"output": 0
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"id": "glm5.2",
|
|
138
|
+
"name": "GLM-5.2",
|
|
139
|
+
"reasoning": true,
|
|
140
|
+
"input": [
|
|
141
|
+
"text"
|
|
142
|
+
],
|
|
143
|
+
"cost": {
|
|
144
|
+
"input": 0,
|
|
145
|
+
"output": 0,
|
|
146
|
+
"cacheRead": 0,
|
|
147
|
+
"cacheWrite": 0
|
|
148
|
+
},
|
|
149
|
+
"contextWindow": 500000,
|
|
150
|
+
"maxTokens": 131072,
|
|
151
|
+
"compat": {
|
|
152
|
+
"supportsDeveloperRole": false,
|
|
153
|
+
"supportsReasoningEffort": true,
|
|
154
|
+
"supportsUsageInStreaming": true,
|
|
155
|
+
"maxTokensField": "max_tokens"
|
|
156
|
+
},
|
|
157
|
+
"notes": [
|
|
158
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested."
|
|
159
|
+
],
|
|
160
|
+
"extras": {
|
|
161
|
+
"id": "glm5.2",
|
|
162
|
+
"name": "GLM-5.2",
|
|
163
|
+
"description": "Open flagship GLM for long-horizon coding agents, served at 500K context on the NaN premium tier",
|
|
164
|
+
"family": "glm",
|
|
165
|
+
"attachment": false,
|
|
166
|
+
"reasoning": true,
|
|
167
|
+
"reasoning_options": [],
|
|
168
|
+
"tool_call": true,
|
|
169
|
+
"structured_output": true,
|
|
170
|
+
"temperature": true,
|
|
171
|
+
"release_date": "2026-06-13",
|
|
172
|
+
"last_updated": "2026-06-13",
|
|
173
|
+
"modalities": {
|
|
174
|
+
"input": [
|
|
175
|
+
"text"
|
|
176
|
+
],
|
|
177
|
+
"output": [
|
|
178
|
+
"text"
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
"open_weights": true,
|
|
182
|
+
"limit": {
|
|
183
|
+
"context": 500000,
|
|
184
|
+
"output": 131072
|
|
185
|
+
},
|
|
186
|
+
"cost": {
|
|
187
|
+
"input": 0,
|
|
188
|
+
"output": 0
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"id": "glm5.3-flash",
|
|
194
|
+
"name": "GLM-5.3-Flash",
|
|
195
|
+
"reasoning": true,
|
|
196
|
+
"input": [
|
|
197
|
+
"text",
|
|
198
|
+
"image"
|
|
199
|
+
],
|
|
200
|
+
"cost": {
|
|
201
|
+
"input": 0,
|
|
202
|
+
"output": 0,
|
|
203
|
+
"cacheRead": 0,
|
|
204
|
+
"cacheWrite": 0
|
|
205
|
+
},
|
|
206
|
+
"contextWindow": 1000000,
|
|
207
|
+
"maxTokens": 131072,
|
|
208
|
+
"compat": {
|
|
209
|
+
"supportsDeveloperRole": false,
|
|
210
|
+
"supportsReasoningEffort": true,
|
|
211
|
+
"supportsUsageInStreaming": true,
|
|
212
|
+
"maxTokensField": "max_tokens"
|
|
213
|
+
},
|
|
214
|
+
"notes": [
|
|
215
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested."
|
|
216
|
+
],
|
|
217
|
+
"extras": {
|
|
218
|
+
"id": "glm5.3-flash",
|
|
219
|
+
"name": "GLM-5.3-Flash",
|
|
220
|
+
"description": "Native multimodal GLM model for efficient coding and long-horizon agent tasks",
|
|
221
|
+
"family": "glm",
|
|
222
|
+
"attachment": true,
|
|
223
|
+
"reasoning": true,
|
|
224
|
+
"reasoning_options": [],
|
|
225
|
+
"tool_call": true,
|
|
226
|
+
"structured_output": true,
|
|
227
|
+
"temperature": true,
|
|
228
|
+
"release_date": "2026-08-26",
|
|
229
|
+
"last_updated": "2026-08-26",
|
|
230
|
+
"modalities": {
|
|
231
|
+
"input": [
|
|
232
|
+
"text",
|
|
233
|
+
"image"
|
|
234
|
+
],
|
|
235
|
+
"output": [
|
|
236
|
+
"text"
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
"open_weights": false,
|
|
240
|
+
"limit": {
|
|
241
|
+
"context": 1000000,
|
|
242
|
+
"output": 131072
|
|
243
|
+
},
|
|
244
|
+
"cost": {
|
|
245
|
+
"input": 0,
|
|
246
|
+
"output": 0
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"id": "mimo-v2.5",
|
|
252
|
+
"name": "MiMo-V2.5",
|
|
253
|
+
"reasoning": true,
|
|
254
|
+
"input": [
|
|
255
|
+
"text",
|
|
256
|
+
"image"
|
|
257
|
+
],
|
|
258
|
+
"cost": {
|
|
259
|
+
"input": 0,
|
|
260
|
+
"output": 0,
|
|
261
|
+
"cacheRead": 0,
|
|
262
|
+
"cacheWrite": 0
|
|
263
|
+
},
|
|
264
|
+
"contextWindow": 1048576,
|
|
265
|
+
"maxTokens": 131072,
|
|
266
|
+
"compat": {
|
|
267
|
+
"supportsDeveloperRole": false,
|
|
268
|
+
"supportsReasoningEffort": true,
|
|
269
|
+
"supportsUsageInStreaming": true,
|
|
270
|
+
"maxTokensField": "max_tokens"
|
|
271
|
+
},
|
|
272
|
+
"notes": [
|
|
273
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested."
|
|
274
|
+
],
|
|
275
|
+
"extras": {
|
|
276
|
+
"id": "mimo-v2.5",
|
|
277
|
+
"name": "MiMo-V2.5",
|
|
278
|
+
"description": "Open MiMo model for multimodal coding agents and long-context automation",
|
|
279
|
+
"family": "mimo",
|
|
280
|
+
"attachment": true,
|
|
281
|
+
"reasoning": true,
|
|
282
|
+
"reasoning_options": [],
|
|
283
|
+
"tool_call": true,
|
|
284
|
+
"temperature": true,
|
|
285
|
+
"knowledge": "2024-12",
|
|
286
|
+
"release_date": "2026-04-22",
|
|
287
|
+
"last_updated": "2026-04-22",
|
|
288
|
+
"modalities": {
|
|
289
|
+
"input": [
|
|
290
|
+
"text",
|
|
291
|
+
"image",
|
|
292
|
+
"audio"
|
|
293
|
+
],
|
|
294
|
+
"output": [
|
|
295
|
+
"text"
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
"open_weights": true,
|
|
299
|
+
"limit": {
|
|
300
|
+
"context": 1048576,
|
|
301
|
+
"output": 131072
|
|
302
|
+
},
|
|
303
|
+
"cost": {
|
|
304
|
+
"input": 0,
|
|
305
|
+
"output": 0
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"id": "qwen3.6",
|
|
311
|
+
"name": "Qwen3.6 35B-A3B",
|
|
312
|
+
"reasoning": true,
|
|
313
|
+
"input": [
|
|
314
|
+
"text",
|
|
315
|
+
"image"
|
|
316
|
+
],
|
|
317
|
+
"cost": {
|
|
318
|
+
"input": 0,
|
|
319
|
+
"output": 0,
|
|
320
|
+
"cacheRead": 0,
|
|
321
|
+
"cacheWrite": 0
|
|
322
|
+
},
|
|
323
|
+
"contextWindow": 262144,
|
|
324
|
+
"maxTokens": 65536,
|
|
325
|
+
"compat": {
|
|
326
|
+
"supportsDeveloperRole": false,
|
|
327
|
+
"supportsReasoningEffort": true,
|
|
328
|
+
"supportsUsageInStreaming": true,
|
|
329
|
+
"maxTokensField": "max_tokens"
|
|
330
|
+
},
|
|
331
|
+
"notes": [
|
|
332
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested."
|
|
333
|
+
],
|
|
334
|
+
"extras": {
|
|
335
|
+
"id": "qwen3.6",
|
|
336
|
+
"name": "Qwen3.6 35B-A3B",
|
|
337
|
+
"description": "Open multimodal Qwen MoE for local agents that need vision, audio, and code",
|
|
338
|
+
"family": "qwen",
|
|
339
|
+
"attachment": true,
|
|
340
|
+
"reasoning": true,
|
|
341
|
+
"reasoning_options": [
|
|
342
|
+
{
|
|
343
|
+
"type": "toggle"
|
|
344
|
+
}
|
|
345
|
+
],
|
|
346
|
+
"tool_call": true,
|
|
347
|
+
"structured_output": true,
|
|
348
|
+
"temperature": true,
|
|
349
|
+
"release_date": "2026-04-17",
|
|
350
|
+
"last_updated": "2026-04-17",
|
|
351
|
+
"modalities": {
|
|
352
|
+
"input": [
|
|
353
|
+
"text",
|
|
354
|
+
"image"
|
|
355
|
+
],
|
|
356
|
+
"output": [
|
|
357
|
+
"text"
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
"open_weights": true,
|
|
361
|
+
"limit": {
|
|
362
|
+
"context": 262144,
|
|
363
|
+
"output": 65536
|
|
364
|
+
},
|
|
365
|
+
"cost": {
|
|
366
|
+
"input": 0,
|
|
367
|
+
"output": 0
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"id": "qwen3.8-flash",
|
|
373
|
+
"name": "Qwen3.8 Flash",
|
|
374
|
+
"reasoning": true,
|
|
375
|
+
"input": [
|
|
376
|
+
"text",
|
|
377
|
+
"image"
|
|
378
|
+
],
|
|
379
|
+
"cost": {
|
|
380
|
+
"input": 0,
|
|
381
|
+
"output": 0,
|
|
382
|
+
"cacheRead": 0,
|
|
383
|
+
"cacheWrite": 0
|
|
384
|
+
},
|
|
385
|
+
"contextWindow": 262144,
|
|
386
|
+
"maxTokens": 131072,
|
|
387
|
+
"compat": {
|
|
388
|
+
"supportsDeveloperRole": false,
|
|
389
|
+
"supportsReasoningEffort": true,
|
|
390
|
+
"supportsUsageInStreaming": true,
|
|
391
|
+
"maxTokensField": "max_tokens"
|
|
392
|
+
},
|
|
393
|
+
"notes": [
|
|
394
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested."
|
|
395
|
+
],
|
|
396
|
+
"extras": {
|
|
397
|
+
"id": "qwen3.8-flash",
|
|
398
|
+
"name": "Qwen3.8 Flash",
|
|
399
|
+
"description": "Qwen vision-language model for visual reasoning, documents, and agent tasks",
|
|
400
|
+
"family": "qwen",
|
|
401
|
+
"attachment": true,
|
|
402
|
+
"reasoning": true,
|
|
403
|
+
"reasoning_options": [],
|
|
404
|
+
"tool_call": true,
|
|
405
|
+
"structured_output": true,
|
|
406
|
+
"release_date": "2026-08-26",
|
|
407
|
+
"last_updated": "2026-08-26",
|
|
408
|
+
"modalities": {
|
|
409
|
+
"input": [
|
|
410
|
+
"text",
|
|
411
|
+
"image"
|
|
412
|
+
],
|
|
413
|
+
"output": [
|
|
414
|
+
"text"
|
|
415
|
+
]
|
|
416
|
+
},
|
|
417
|
+
"open_weights": false,
|
|
418
|
+
"limit": {
|
|
419
|
+
"context": 262144,
|
|
420
|
+
"output": 131072
|
|
421
|
+
},
|
|
422
|
+
"cost": {
|
|
423
|
+
"input": 0,
|
|
424
|
+
"output": 0
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
];
|
|
429
|
+
|
|
430
|
+
export const GENERATED_CATALOG_META = {
|
|
431
|
+
source: "https://models.dev/api.json",
|
|
432
|
+
modelsDevProvider: "nan",
|
|
433
|
+
fetchedAt: "2026-09-04T18:57:45.925Z",
|
|
434
|
+
modelCount: 7,
|
|
435
|
+
models: ["deepseek-v4-flash","gemma4","glm5.2","glm5.3-flash","mimo-v2.5","qwen3.6","qwen3.8-flash"],
|
|
436
|
+
notes: [
|
|
437
|
+
"compat matches the maintainer's working ~/.pi/agent/models.json LiteLLM config for api.nan.builders (2026-09-04): supportsDeveloperRole false, supportsReasoningEffort true, supportsUsageInStreaming true, maxTokensField max_tokens. NaN's docs example sets only supportsDeveloperRole: true and is not battle-tested.",
|
|
438
|
+
"input includes image: NaN serves the Vision-Exp variant (confirmed at https://nan.builders/docs/models, 'takes images as input'); models.dev provider nan lists text only."
|
|
439
|
+
],
|
|
440
|
+
} as const;
|