pi-umans-provider 1.0.2

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/models.json ADDED
@@ -0,0 +1,102 @@
1
+ [
2
+ {
3
+ "id": "umans-coder",
4
+ "name": "Umans Coder",
5
+ "reasoning": true,
6
+ "input": [
7
+ "text",
8
+ "image"
9
+ ],
10
+ "cost": {
11
+ "input": 0,
12
+ "output": 0,
13
+ "cacheRead": 0,
14
+ "cacheWrite": 0
15
+ },
16
+ "contextWindow": 262144,
17
+ "maxTokens": 32768
18
+ },
19
+ {
20
+ "id": "umans-flash",
21
+ "name": "Umans Flash",
22
+ "reasoning": true,
23
+ "input": [
24
+ "text",
25
+ "image"
26
+ ],
27
+ "cost": {
28
+ "input": 0,
29
+ "output": 0,
30
+ "cacheRead": 0,
31
+ "cacheWrite": 0
32
+ },
33
+ "contextWindow": 262144,
34
+ "maxTokens": 32768
35
+ },
36
+ {
37
+ "id": "umans-glm-5.2",
38
+ "name": "Umans GLM 5.2",
39
+ "reasoning": true,
40
+ "input": [
41
+ "text"
42
+ ],
43
+ "cost": {
44
+ "input": 0,
45
+ "output": 0,
46
+ "cacheRead": 0,
47
+ "cacheWrite": 0
48
+ },
49
+ "contextWindow": 405504,
50
+ "maxTokens": 131071
51
+ },
52
+ {
53
+ "id": "umans-glm-5.2-nvfp4",
54
+ "name": "Umans GLM 5.2 NVFP4 (experimental, short test from Jun 29)",
55
+ "reasoning": true,
56
+ "input": [
57
+ "text"
58
+ ],
59
+ "cost": {
60
+ "input": 0,
61
+ "output": 0,
62
+ "cacheRead": 0,
63
+ "cacheWrite": 0
64
+ },
65
+ "contextWindow": 405504,
66
+ "maxTokens": 131071
67
+ },
68
+ {
69
+ "id": "umans-kimi-k2.7",
70
+ "name": "Umans Kimi K2.7 Code",
71
+ "reasoning": true,
72
+ "input": [
73
+ "text",
74
+ "image"
75
+ ],
76
+ "cost": {
77
+ "input": 0,
78
+ "output": 0,
79
+ "cacheRead": 0,
80
+ "cacheWrite": 0
81
+ },
82
+ "contextWindow": 262144,
83
+ "maxTokens": 32768
84
+ },
85
+ {
86
+ "id": "umans-qwen3.6-35b-a3b",
87
+ "name": "Umans Qwen3.6 35B A3B",
88
+ "reasoning": true,
89
+ "input": [
90
+ "text",
91
+ "image"
92
+ ],
93
+ "cost": {
94
+ "input": 0,
95
+ "output": 0,
96
+ "cacheRead": 0,
97
+ "cacheWrite": 0
98
+ },
99
+ "contextWindow": 262144,
100
+ "maxTokens": 32768
101
+ }
102
+ ]
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "pi-umans-provider",
3
+ "version": "1.0.2",
4
+ "description": "UMANS provider extension for pi - Access Kimi K2.6, GLM 5.1, Qwen 3.6, and coding-optimized models through the UMANS API",
5
+ "type": "module",
6
+ "main": "index.ts",
7
+ "keywords": [
8
+ "pi",
9
+ "extension",
10
+ "provider",
11
+ "umans",
12
+ "ai",
13
+ "llm",
14
+ "kimi",
15
+ "moonshot",
16
+ "glm",
17
+ "qwen",
18
+ "coding"
19
+ ],
20
+ "author": "",
21
+ "license": "MIT",
22
+ "pi": {
23
+ "extensions": [
24
+ "./index.ts"
25
+ ]
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^25.9.2",
29
+ "typescript": "^6.0.3"
30
+ },
31
+ "scripts": {
32
+ "clean": "echo 'nothing to clean'",
33
+ "build": "echo 'nothing to build'",
34
+ "check": "echo 'nothing to check'",
35
+ "update-models": "node scripts/update-models.js"
36
+ }
37
+ }
package/patch.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "umans-coder": {
3
+ "thinkingLevelMap": { "off": "none" },
4
+ "compat": {
5
+ "supportsDeveloperRole": false,
6
+ "supportsReasoningEffort": true,
7
+ "requiresReasoningContentOnAssistantMessages": true
8
+ }
9
+ },
10
+ "umans-flash": {
11
+ "thinkingLevelMap": { "off": "none" },
12
+ "compat": {
13
+ "supportsDeveloperRole": false,
14
+ "supportsReasoningEffort": true,
15
+ "requiresReasoningContentOnAssistantMessages": true
16
+ }
17
+ },
18
+ "umans-kimi-k2.6": {
19
+ "thinkingLevelMap": { "off": "none" },
20
+ "compat": {
21
+ "supportsDeveloperRole": false,
22
+ "supportsReasoningEffort": true,
23
+ "requiresReasoningContentOnAssistantMessages": true
24
+ }
25
+ },
26
+ "umans-kimi-k2.7": {
27
+ "thinkingLevelMap": { "off": "none" },
28
+ "compat": {
29
+ "supportsDeveloperRole": false,
30
+ "supportsReasoningEffort": true,
31
+ "requiresReasoningContentOnAssistantMessages": true
32
+ }
33
+ },
34
+ "umans-glm-5.1": {
35
+ "thinkingLevelMap": { "off": "none" },
36
+ "compat": {
37
+ "supportsDeveloperRole": false,
38
+ "supportsReasoningEffort": true,
39
+ "requiresReasoningContentOnAssistantMessages": true
40
+ }
41
+ },
42
+ "umans-glm-5.2": {
43
+ "thinkingLevelMap": { "off": "none" },
44
+ "compat": {
45
+ "supportsDeveloperRole": false,
46
+ "supportsReasoningEffort": true,
47
+ "requiresReasoningContentOnAssistantMessages": true
48
+ }
49
+ },
50
+ "umans-glm-5.2-nvfp4": {
51
+ "thinkingLevelMap": { "off": "none" },
52
+ "compat": {
53
+ "supportsDeveloperRole": false,
54
+ "supportsReasoningEffort": true,
55
+ "requiresReasoningContentOnAssistantMessages": true
56
+ }
57
+ },
58
+ "umans-qwen3.6-35b-a3b": {
59
+ "thinkingLevelMap": { "off": "none" },
60
+ "compat": {
61
+ "supportsDeveloperRole": false,
62
+ "supportsReasoningEffort": true,
63
+ "requiresReasoningContentOnAssistantMessages": true
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Update UMANS models from API
4
+ *
5
+ * Fetches models from https://api.code.umans.ai/v1/models/info and updates:
6
+ * - models.json: Pure API model definitions (no patches baked in)
7
+ * - README.md: Model table with patch.json overrides applied
8
+ *
9
+ * models.json reflects the raw API data as-is. patch.json corrects
10
+ * compatibility details at runtime (index.ts) and is also applied when
11
+ * generating the README table so the docs reflect reality.
12
+ *
13
+ * Idempotent: re-running produces the same result if the API hasn't changed.
14
+ */
15
+
16
+ import fs from "fs";
17
+ import path from "path";
18
+ import { fileURLToPath } from "url";
19
+
20
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+
22
+ const MODELS_INFO_API_URL = "https://api.code.umans.ai/v1/models/info";
23
+ const MODELS_JSON_PATH = path.join(__dirname, "..", "models.json");
24
+ const PATCH_JSON_PATH = path.join(__dirname, "..", "patch.json");
25
+ const CUSTOM_MODELS_JSON_PATH = path.join(__dirname, "..", "custom-models.json");
26
+ const README_PATH = path.join(__dirname, "..", "README.md");
27
+
28
+ // Skip deprecated aliases
29
+ const DEPRECATED_MODELS = new Set(["umans-flash-beta"]);
30
+
31
+ function transformApiModel(id, info) {
32
+ if (info.deprecation || DEPRECATED_MODELS.has(id)) return null;
33
+
34
+ const caps = info.capabilities || {};
35
+ const hasVision = caps.supports_vision === true;
36
+
37
+ return {
38
+ id,
39
+ name: info.display_name || info.name || id,
40
+ reasoning: true, // All UMANS models are reasoning-capable
41
+ input: hasVision ? ["text", "image"] : ["text"],
42
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, // Subscription-based
43
+ contextWindow: caps.context_window || 131072,
44
+ maxTokens: caps.recommended_max_tokens || caps.max_completion_tokens || 131072,
45
+ };
46
+ }
47
+
48
+ function applyPatch(model, patch) {
49
+ const result = { ...model };
50
+ if (patch.name !== undefined) result.name = patch.name;
51
+ if (patch.reasoning !== undefined) result.reasoning = patch.reasoning;
52
+ if (patch.input !== undefined) result.input = patch.input;
53
+ if (patch.contextWindow !== undefined) result.contextWindow = patch.contextWindow;
54
+ if (patch.maxTokens !== undefined) result.maxTokens = patch.maxTokens;
55
+ if (patch.cost) {
56
+ result.cost = {
57
+ input: patch.cost.input ?? result.cost.input,
58
+ output: patch.cost.output ?? result.cost.output,
59
+ cacheRead: patch.cost.cacheRead ?? result.cost.cacheRead,
60
+ cacheWrite: patch.cost.cacheWrite ?? result.cost.cacheWrite,
61
+ };
62
+ }
63
+ if (patch.compat) {
64
+ result.compat = { ...(result.compat || {}), ...patch.compat };
65
+ }
66
+ if (!result.reasoning && result.compat?.thinkingFormat) {
67
+ delete result.compat.thinkingFormat;
68
+ }
69
+ if (result.compat && Object.keys(result.compat).length === 0) {
70
+ delete result.compat;
71
+ }
72
+ return result;
73
+ }
74
+
75
+ function buildModels(baseModels, customModels, patchData) {
76
+ const modelMap = new Map();
77
+ for (const model of baseModels) {
78
+ modelMap.set(model.id, model);
79
+ }
80
+ for (const [id, patchEntry] of Object.entries(patchData)) {
81
+ const existing = modelMap.get(id);
82
+ if (existing) {
83
+ modelMap.set(id, applyPatch(existing, patchEntry));
84
+ }
85
+ }
86
+ for (const model of customModels) {
87
+ const existing = modelMap.get(model.id);
88
+ const patchEntry = patchData[model.id];
89
+ if (existing && patchEntry) {
90
+ modelMap.set(model.id, applyPatch(model, patchEntry));
91
+ } else if (existing) {
92
+ modelMap.set(model.id, model);
93
+ } else if (patchEntry) {
94
+ modelMap.set(model.id, applyPatch(model, patchEntry));
95
+ } else {
96
+ modelMap.set(model.id, model);
97
+ }
98
+ }
99
+ return Array.from(modelMap.values());
100
+ }
101
+
102
+ function loadJson(filePath) {
103
+ try {
104
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
105
+ } catch {
106
+ return Array.isArray(filePath) ? [] : {};
107
+ }
108
+ }
109
+
110
+ function formatContextWindow(n) {
111
+ if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`;
112
+ if (n >= 1000) return `${Math.round(n / 1000)}K`;
113
+ return n.toString();
114
+ }
115
+
116
+ function generateReadmeTable(models) {
117
+ const lines = [
118
+ "| Model | Base | Context | Vision | Reasoning | Max Output |",
119
+ "|-------|------|---------|--------|-----------|------------|",
120
+ ];
121
+
122
+ for (const model of models) {
123
+ const name = model.name.replace(/^Umans\s+/, "");
124
+ const base = model._meta?.baseModel || "—";
125
+ const context = formatContextWindow(model.contextWindow);
126
+ const vision = model.input.includes("image") ? "✅" : "❌";
127
+ const reasoning = model.reasoning ? "✅" : "❌";
128
+ const maxTokens = formatContextWindow(model.maxTokens);
129
+
130
+ lines.push(`| ${name} | ${base} | ${context} | ${vision} | ${reasoning} | ${maxTokens} |`);
131
+ }
132
+
133
+ return lines.join("\n");
134
+ }
135
+
136
+ function updateReadme(models) {
137
+ let readme = fs.readFileSync(README_PATH, "utf8");
138
+ const newTable = generateReadmeTable(models);
139
+
140
+ const tableRegex =
141
+ /(## Available Models\n\n)\| Model \| Base \| Context \| Vision \| Reasoning \| Max Output \|\n\|[-| ]+\|(\n\|[^\n]+\|)*\n*/;
142
+
143
+ if (tableRegex.test(readme)) {
144
+ readme = readme.replace(tableRegex, (match, header) => `${header}${newTable}\n\n`);
145
+ fs.writeFileSync(README_PATH, readme);
146
+ console.log("✓ Updated README.md");
147
+ } else {
148
+ console.warn('⚠ Could not find model table in "## Available Models" section');
149
+ }
150
+ }
151
+
152
+ function cleanModelForJson(model) {
153
+ const { _meta, ...cleanModel } = model;
154
+ return cleanModel;
155
+ }
156
+
157
+ async function main() {
158
+ console.log(`Fetching models from ${MODELS_INFO_API_URL}...`);
159
+
160
+ try {
161
+ const response = await fetch(MODELS_INFO_API_URL);
162
+ if (!response.ok) {
163
+ throw new Error(`HTTP error! status: ${response.status}`);
164
+ }
165
+
166
+ const apiData = await response.json();
167
+
168
+ if (!apiData || typeof apiData !== "object") {
169
+ throw new Error("API response is not a valid object");
170
+ }
171
+
172
+ // Load existing models.json — source of truth for curated specs
173
+ let existingModels = [];
174
+ try {
175
+ existingModels = JSON.parse(fs.readFileSync(MODELS_JSON_PATH, "utf8"));
176
+ } catch {}
177
+ const existingModelsMap = {};
178
+ for (const m of existingModels) {
179
+ existingModelsMap[m.id] = m;
180
+ }
181
+
182
+ // Transform models from API
183
+ let apiTransformed = Object.entries(apiData)
184
+ .map(([id, info]) => transformApiModel(id, info))
185
+ .filter((m) => m !== null);
186
+
187
+ // Enrich with base model info for README
188
+ apiTransformed = apiTransformed.map((model) => {
189
+ const info = apiData[model.id];
190
+ const baseModel = info?.base_model?.oss_base || info?.base_model?.name || "—";
191
+ return { ...model, _meta: { baseModel } };
192
+ });
193
+
194
+ // Sort models alphabetically by name
195
+ apiTransformed.sort((a, b) => a.name.localeCompare(b.name));
196
+
197
+ console.log(`✓ Fetched and transformed ${apiTransformed.length} models from API`);
198
+
199
+ // Preserve existing curated data (compat, reasoning flags, etc.)
200
+ apiTransformed = apiTransformed.map((model) => {
201
+ const existing = existingModelsMap[model.id];
202
+ if (existing) {
203
+ return {
204
+ ...model,
205
+ // Preserve curated fields that the API doesn't provide
206
+ reasoning: typeof existing.reasoning === "boolean" ? existing.reasoning : model.reasoning,
207
+ input: model.input,
208
+ maxTokens: existing.maxTokens || model.maxTokens,
209
+ _meta: model._meta, // Keep API-derived _meta for README
210
+ };
211
+ }
212
+ return model;
213
+ });
214
+
215
+ // Update models.json — pure API data, no patches baked in
216
+ const cleanModels = apiTransformed.map(cleanModelForJson);
217
+ fs.writeFileSync(MODELS_JSON_PATH, JSON.stringify(cleanModels, null, 2) + "\n");
218
+ console.log("✓ Updated models.json (pure API data)");
219
+
220
+ // Load patch.json and apply for README generation
221
+ let patch = {};
222
+ try {
223
+ patch = JSON.parse(fs.readFileSync(PATCH_JSON_PATH, "utf8"));
224
+ console.log(`✓ Loaded ${Object.keys(patch).length} patch overrides from patch.json`);
225
+ } catch {
226
+ console.warn("⚠ Could not load patch.json, README may show incorrect compat flags");
227
+ }
228
+
229
+ // Build full model list for README: base → patch → custom
230
+ const customModels = loadJson(CUSTOM_MODELS_JSON_PATH);
231
+ const readmeModels = buildModels(apiTransformed, Array.isArray(customModels) ? customModels : [], patch);
232
+ readmeModels.sort((a, b) => a.name.localeCompare(b.name));
233
+ console.log("✓ Built model list (base → patch → custom) for README");
234
+
235
+ // Update README.md with patched data
236
+ updateReadme(readmeModels);
237
+
238
+ // Summary
239
+ console.log("\n--- Summary ---");
240
+ console.log(`Total models: ${readmeModels.length}`);
241
+ console.log(`Reasoning models: ${readmeModels.filter((m) => m.reasoning).length}`);
242
+ console.log(`Vision models: ${readmeModels.filter((m) => m.input.includes("image")).length}`);
243
+ console.log(`Subscription-based: All models $0/M (included in plan)`);
244
+
245
+ const oldIds = new Set(existingModels.map((m) => m.id));
246
+ const newIds = new Set(apiTransformed.map((m) => m.id));
247
+
248
+ const added = [...newIds].filter((id) => !oldIds.has(id));
249
+ const removed = [...oldIds].filter((id) => !newIds.has(id));
250
+
251
+ if (added.length > 0) {
252
+ console.log(`\nNew models: ${added.join(", ")}`);
253
+ }
254
+ if (removed.length > 0) {
255
+ console.log(`\nRemoved models: ${removed.join(", ")}`);
256
+ }
257
+ } catch (error) {
258
+ console.error("Error:", error.message);
259
+ process.exit(1);
260
+ }
261
+ }
262
+
263
+ main();
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "skipLibCheck": true,
10
+ },
11
+ "files": ["index.ts"]
12
+ }