@aliou/pi-synthetic 0.17.3 → 0.18.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliou/pi-synthetic",
3
- "version": "0.17.3",
3
+ "version": "0.18.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "private": false,
@@ -29,7 +29,8 @@
29
29
  },
30
30
  "files": [
31
31
  "src",
32
- "README.md"
32
+ "README.md",
33
+ "!src/**/*.test.ts"
33
34
  ],
34
35
  "peerDependencies": {
35
36
  "@earendil-works/pi-coding-agent": "0.74.0",
@@ -40,8 +41,8 @@
40
41
  "@aliou/pi-utils-ui": "^0.4.0"
41
42
  },
42
43
  "devDependencies": {
43
- "@aliou/biome-plugins": "^0.7.0",
44
- "@biomejs/biome": "^2.4.2",
44
+ "@aliou/biome-plugins": "^0.8.1",
45
+ "@biomejs/biome": "^2.4.15",
45
46
  "@changesets/cli": "^2.27.11",
46
47
  "@earendil-works/pi-coding-agent": "0.74.0",
47
48
  "typebox": "^1.1.37",
@@ -91,38 +91,6 @@ export const SYNTHETIC_MODELS: SyntheticModelConfig[] = [
91
91
  contextWindow: 196608,
92
92
  maxTokens: 65536,
93
93
  },
94
- // models.dev: synthetic/hf:meta-llama/Llama-3.3-70B-Instruct → ctx=128000, out=32768
95
- {
96
- id: "hf:meta-llama/Llama-3.3-70B-Instruct",
97
- name: "meta-llama/Llama-3.3-70B-Instruct",
98
- provider: "together",
99
- reasoning: false,
100
- input: ["text"],
101
- cost: {
102
- input: 0.88,
103
- output: 0.88,
104
- cacheRead: 0.88,
105
- cacheWrite: 0,
106
- },
107
- contextWindow: 131072,
108
- maxTokens: 32768,
109
- },
110
- // models.dev: synthetic/hf:deepseek-ai/DeepSeek-R1-0528 → ctx=128000, out=128000
111
- {
112
- id: "hf:deepseek-ai/DeepSeek-R1-0528",
113
- name: "deepseek-ai/DeepSeek-R1-0528",
114
- provider: "together",
115
- reasoning: true,
116
- input: ["text"],
117
- cost: {
118
- input: 3,
119
- output: 8,
120
- cacheRead: 3,
121
- cacheWrite: 0,
122
- },
123
- contextWindow: 131072,
124
- maxTokens: 128000,
125
- },
126
94
  // models.dev: synthetic/hf:deepseek-ai/DeepSeek-V3.2 → ctx=162816, out=8000
127
95
  {
128
96
  id: "hf:deepseek-ai/DeepSeek-V3.2",
@@ -191,122 +159,139 @@ export const SYNTHETIC_MODELS: SyntheticModelConfig[] = [
191
159
  contextWindow: 262144,
192
160
  maxTokens: 65536,
193
161
  },
194
- // API: hf:moonshotai/Kimi-K2.5 → ctx=262144, out=65536
162
+ // API: hf:Qwen/Qwen3.5-397B-A17B → ctx=262144, out=65536
195
163
  {
196
- id: "hf:moonshotai/Kimi-K2.5",
197
- name: "moonshotai/Kimi-K2.5",
164
+ id: "hf:Qwen/Qwen3.5-397B-A17B",
165
+ name: "Qwen/Qwen3.5-397B-A17B",
198
166
  provider: "together",
199
167
  reasoning: true,
200
168
  input: ["text", "image"],
201
169
  cost: {
202
- input: 0.5,
203
- output: 2.8,
204
- cacheRead: 0.5,
170
+ input: 0.6,
171
+ output: 3.6,
172
+ cacheRead: 0.6,
205
173
  cacheWrite: 0,
206
174
  },
207
175
  contextWindow: 262144,
208
176
  maxTokens: 65536,
209
177
  },
210
- // API: hf:nvidia/Kimi-K2.5-NVFP4 → ctx=262144; models.dev: out=65536 (NVFP4 quantized)
178
+ // API: hf:MiniMaxAI/MiniMax-M2.5 → ctx=191488, out=65536
211
179
  {
212
- id: "hf:nvidia/Kimi-K2.5-NVFP4",
213
- name: "nvidia/Kimi-K2.5-NVFP4",
214
- provider: "together",
180
+ id: "hf:MiniMaxAI/MiniMax-M2.5",
181
+ name: "MiniMaxAI/MiniMax-M2.5",
182
+ provider: "synthetic",
215
183
  reasoning: true,
216
- input: ["text", "image"],
184
+ thinkingLevelMap: { off: null, minimal: null, low: null, xhigh: null },
185
+ input: ["text"],
217
186
  cost: {
218
- input: 0.5,
219
- output: 2.8,
220
- cacheRead: 0.5,
187
+ input: 0.4,
188
+ output: 2,
189
+ cacheRead: 0.4,
221
190
  cacheWrite: 0,
222
191
  },
223
- contextWindow: 262144,
192
+ contextWindow: 191488,
224
193
  maxTokens: 65536,
194
+ compat: {
195
+ supportsReasoningEffort: true,
196
+ maxTokensField: "max_completion_tokens",
197
+ },
225
198
  },
226
- // models.dev: synthetic/hf:deepseek-ai/DeepSeek-V3 → ctx=128000, out=128000
199
+ // API: hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4 → ctx=262144, out=65536
227
200
  {
228
- id: "hf:deepseek-ai/DeepSeek-V3",
229
- name: "deepseek-ai/DeepSeek-V3",
230
- provider: "together",
201
+ id: "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4",
202
+ name: "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4",
203
+ provider: "synthetic",
231
204
  reasoning: true,
205
+ thinkingLevelMap: { minimal: null, low: null, xhigh: null },
206
+ compat: {
207
+ supportsReasoningEffort: true,
208
+ },
232
209
  input: ["text"],
233
210
  cost: {
234
- input: 1.25,
235
- output: 1.25,
236
- cacheRead: 1.25,
211
+ input: 0.3,
212
+ output: 1,
213
+ cacheRead: 0.3,
237
214
  cacheWrite: 0,
238
215
  },
239
- contextWindow: 131072,
240
- maxTokens: 128000,
216
+ contextWindow: 262144,
217
+ maxTokens: 65536,
241
218
  },
242
- // models.dev: synthetic/hf:Qwen/Qwen3-235B-A22B-Thinking-2507 → ctx=256000, out=32000
219
+ // API: syn:large:text → alias for hf:zai-org/GLM-5.1 → ctx=196608, out=65536
243
220
  {
244
- id: "hf:Qwen/Qwen3-235B-A22B-Thinking-2507",
245
- name: "Qwen/Qwen3-235B-A22B-Thinking-2507",
246
- provider: "together",
221
+ id: "syn:large:text",
222
+ name: "syn:large:text",
223
+ provider: "synthetic",
247
224
  reasoning: true,
225
+ thinkingLevelMap: { minimal: null, xhigh: null },
226
+ compat: {
227
+ supportsReasoningEffort: true,
228
+ supportsDeveloperRole: false,
229
+ },
248
230
  input: ["text"],
249
231
  cost: {
250
- input: 0.65,
232
+ input: 1,
251
233
  output: 3,
252
- cacheRead: 0.65,
234
+ cacheRead: 1,
253
235
  cacheWrite: 0,
254
236
  },
255
- contextWindow: 262144,
256
- maxTokens: 32000,
237
+ contextWindow: 196608,
238
+ maxTokens: 65536,
257
239
  },
258
- // API: hf:Qwen/Qwen3.5-397B-A17B → ctx=262144, out=65536
240
+ // API: syn:small:text → alias for hf:zai-org/GLM-4.7-Flash → ctx=196608, out=65536
259
241
  {
260
- id: "hf:Qwen/Qwen3.5-397B-A17B",
261
- name: "Qwen/Qwen3.5-397B-A17B",
262
- provider: "together",
242
+ id: "syn:small:text",
243
+ name: "syn:small:text",
244
+ provider: "synthetic",
263
245
  reasoning: true,
264
- input: ["text", "image"],
246
+ thinkingLevelMap: { minimal: null, xhigh: null },
247
+ compat: {
248
+ supportsReasoningEffort: true,
249
+ },
250
+ input: ["text"],
265
251
  cost: {
266
- input: 0.6,
267
- output: 3.6,
268
- cacheRead: 0.6,
252
+ input: 0.1,
253
+ output: 0.5,
254
+ cacheRead: 0.1,
269
255
  cacheWrite: 0,
270
256
  },
271
- contextWindow: 262144,
257
+ contextWindow: 196608,
272
258
  maxTokens: 65536,
273
259
  },
274
- // API: hf:MiniMaxAI/MiniMax-M2.5 → ctx=191488, out=65536
260
+ // API: syn:large:vision → alias for hf:moonshotai/Kimi-K2.6 → ctx=262144, out=65536
275
261
  {
276
- id: "hf:MiniMaxAI/MiniMax-M2.5",
277
- name: "MiniMaxAI/MiniMax-M2.5",
262
+ id: "syn:large:vision",
263
+ name: "syn:large:vision",
278
264
  provider: "synthetic",
279
265
  reasoning: true,
280
- thinkingLevelMap: { off: null, minimal: null, low: null, xhigh: null },
281
- input: ["text"],
266
+ thinkingLevelMap: { minimal: null, low: null, xhigh: null },
267
+ compat: {
268
+ supportsReasoningEffort: true,
269
+ },
270
+ input: ["text", "image"],
282
271
  cost: {
283
- input: 0.4,
284
- output: 2,
285
- cacheRead: 0.4,
272
+ input: 0.95,
273
+ output: 4,
274
+ cacheRead: 0.95,
286
275
  cacheWrite: 0,
287
276
  },
288
- contextWindow: 191488,
277
+ contextWindow: 262144,
289
278
  maxTokens: 65536,
290
- compat: {
291
- supportsReasoningEffort: true,
292
- maxTokensField: "max_completion_tokens",
293
- },
294
279
  },
295
- // API: hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4 → ctx=262144, out=65536
280
+ // API: syn:small:vision → alias for hf:moonshotai/Kimi-K2.6 → ctx=262144, out=65536
296
281
  {
297
- id: "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4",
298
- name: "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4",
282
+ id: "syn:small:vision",
283
+ name: "syn:small:vision",
299
284
  provider: "synthetic",
300
285
  reasoning: true,
301
286
  thinkingLevelMap: { minimal: null, low: null, xhigh: null },
302
287
  compat: {
303
288
  supportsReasoningEffort: true,
304
289
  },
305
- input: ["text"],
290
+ input: ["text", "image"],
306
291
  cost: {
307
- input: 0.3,
308
- output: 1,
309
- cacheRead: 0.3,
292
+ input: 0.95,
293
+ output: 4,
294
+ cacheRead: 0.95,
310
295
  cacheWrite: 0,
311
296
  },
312
297
  contextWindow: 262144,
@@ -1,46 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { buildSyntheticProviderModels } from "./index";
3
- import { SYNTHETIC_MODELS } from "./models";
4
-
5
- describe("buildSyntheticProviderModels", () => {
6
- it("excludes proxied models when includeProxiedModels is false", () => {
7
- const models = buildSyntheticProviderModels(false);
8
- for (const model of models) {
9
- const source = SYNTHETIC_MODELS.find((m) => m.id === model.id);
10
- expect(source).toBeDefined();
11
- expect(source?.provider).toBe("synthetic");
12
- }
13
- });
14
-
15
- it("includes all models when includeProxiedModels is true", () => {
16
- const models = buildSyntheticProviderModels(true);
17
- expect(models).toHaveLength(SYNTHETIC_MODELS.length);
18
- });
19
-
20
- it("does not expose the internal provider field", () => {
21
- const models = buildSyntheticProviderModels(true);
22
- for (const model of models) {
23
- expect(model).not.toHaveProperty("provider");
24
- }
25
- });
26
-
27
- it("sets default compat fields on every model", () => {
28
- const models = buildSyntheticProviderModels(true);
29
- for (const model of models) {
30
- expect(model.compat).toMatchObject({
31
- supportsDeveloperRole: false,
32
- });
33
- expect(model.compat).toHaveProperty("maxTokensField");
34
- }
35
- });
36
-
37
- it("preserves model-specific compat overrides", () => {
38
- const models = buildSyntheticProviderModels(true);
39
- const miniMax = models.find((m) => m.id === "hf:MiniMaxAI/MiniMax-M2.5");
40
- expect(miniMax).toBeDefined();
41
- expect(miniMax?.compat).toMatchObject({
42
- supportsDeveloperRole: false,
43
- maxTokensField: "max_completion_tokens",
44
- });
45
- });
46
- });
@@ -1,217 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { SYNTHETIC_MODELS } from "./models";
3
-
4
- interface ApiModel {
5
- id: string;
6
- name: string;
7
- provider: string | null;
8
- input_modalities: string[];
9
- output_modalities: string[];
10
- context_length: number;
11
- max_output_length: number;
12
- pricing: {
13
- prompt: string;
14
- completion: string;
15
- input_cache_reads: string;
16
- input_cache_writes: string;
17
- };
18
- supported_features?: string[];
19
- }
20
-
21
- interface ApiResponse {
22
- data: ApiModel[];
23
- }
24
-
25
- interface Discrepancy {
26
- model: string;
27
- field: string;
28
- hardcoded: unknown;
29
- api: unknown;
30
- }
31
-
32
- async function fetchApiModels(): Promise<ApiModel[]> {
33
- // Making ourselves known
34
- const response = await fetch("https://api.synthetic.new/openai/v1/models", {
35
- headers: {
36
- Referer: "https://github.com/aliou/pi-synthetic",
37
- },
38
- });
39
-
40
- if (!response.ok) {
41
- throw new Error(
42
- `API request failed: ${response.status} ${response.statusText}`,
43
- );
44
- }
45
-
46
- const data: ApiResponse = await response.json();
47
- return data.data;
48
- }
49
-
50
- function parsePrice(priceStr: string): number {
51
- // Convert "$0.0000006" to 0.6 (dollars per million tokens)
52
- const match = priceStr.match(/\$?(\d+\.?\d*)/);
53
- if (!match) return 0;
54
- const pricePerToken = Number.parseFloat(match[1]);
55
- // API prices are per token, hardcoded prices are per million tokens
56
- return pricePerToken * 1_000_000;
57
- }
58
-
59
- function compareModels(
60
- apiModels: ApiModel[],
61
- hardcodedModels: typeof SYNTHETIC_MODELS,
62
- ): Discrepancy[] {
63
- const discrepancies: Discrepancy[] = [];
64
-
65
- for (const hardcoded of hardcodedModels) {
66
- const apiModel = apiModels.find((m) => m.id === hardcoded.id);
67
-
68
- if (!apiModel) {
69
- discrepancies.push({
70
- model: hardcoded.id,
71
- field: "exists",
72
- hardcoded: true,
73
- api: false,
74
- });
75
- continue;
76
- }
77
-
78
- // Check input modalities (text vs image support)
79
- const apiInputs = apiModel.input_modalities.sort();
80
- const hardcodedInputs = [...hardcoded.input].sort();
81
- if (JSON.stringify(apiInputs) !== JSON.stringify(hardcodedInputs)) {
82
- discrepancies.push({
83
- model: hardcoded.id,
84
- field: "input",
85
- hardcoded: hardcodedInputs,
86
- api: apiInputs,
87
- });
88
- }
89
-
90
- // Check context window
91
- if (apiModel.context_length !== hardcoded.contextWindow) {
92
- discrepancies.push({
93
- model: hardcoded.id,
94
- field: "contextWindow",
95
- hardcoded: hardcoded.contextWindow,
96
- api: apiModel.context_length,
97
- });
98
- }
99
-
100
- // Check max output tokens (skip if API doesn't provide it)
101
- if (
102
- apiModel.max_output_length !== undefined &&
103
- apiModel.max_output_length !== hardcoded.maxTokens
104
- ) {
105
- discrepancies.push({
106
- model: hardcoded.id,
107
- field: "maxTokens",
108
- hardcoded: hardcoded.maxTokens,
109
- api: apiModel.max_output_length,
110
- });
111
- }
112
-
113
- // Check input cost (convert API price to per-million rate)
114
- const apiInputCost = parsePrice(apiModel.pricing.prompt);
115
- const epsilon = 0.001; // Small tolerance for floating point
116
- if (Math.abs(apiInputCost - hardcoded.cost.input) > epsilon) {
117
- discrepancies.push({
118
- model: hardcoded.id,
119
- field: "cost.input",
120
- hardcoded: hardcoded.cost.input,
121
- api: apiInputCost,
122
- });
123
- }
124
-
125
- // Check output cost
126
- const apiOutputCost = parsePrice(apiModel.pricing.completion);
127
- if (Math.abs(apiOutputCost - hardcoded.cost.output) > epsilon) {
128
- discrepancies.push({
129
- model: hardcoded.id,
130
- field: "cost.output",
131
- hardcoded: hardcoded.cost.output,
132
- api: apiOutputCost,
133
- });
134
- }
135
-
136
- // Check cache read cost
137
- const apiCacheReadCost = parsePrice(apiModel.pricing.input_cache_reads);
138
- if (Math.abs(apiCacheReadCost - hardcoded.cost.cacheRead) > epsilon) {
139
- discrepancies.push({
140
- model: hardcoded.id,
141
- field: "cost.cacheRead",
142
- hardcoded: hardcoded.cost.cacheRead,
143
- api: apiCacheReadCost,
144
- });
145
- }
146
-
147
- // Check reasoning capability from supported_features (skip if API doesn't provide it)
148
- if (apiModel.supported_features !== undefined) {
149
- const apiSupportsReasoning =
150
- apiModel.supported_features.includes("reasoning");
151
- if (apiSupportsReasoning !== hardcoded.reasoning) {
152
- discrepancies.push({
153
- model: hardcoded.id,
154
- field: "reasoning",
155
- hardcoded: hardcoded.reasoning,
156
- api: apiSupportsReasoning,
157
- });
158
- }
159
- }
160
-
161
- // Check provider
162
- if (
163
- apiModel.provider !== null &&
164
- apiModel.provider !== hardcoded.provider
165
- ) {
166
- discrepancies.push({
167
- model: hardcoded.id,
168
- field: "provider",
169
- hardcoded: hardcoded.provider,
170
- api: apiModel.provider,
171
- });
172
- }
173
- }
174
-
175
- // Check for API models not in hardcoded list
176
- for (const apiModel of apiModels) {
177
- const hardcoded = hardcodedModels.find((m) => m.id === apiModel.id);
178
- if (!hardcoded) {
179
- discrepancies.push({
180
- model: apiModel.id,
181
- field: "exists",
182
- hardcoded: false,
183
- api: true,
184
- });
185
- }
186
- }
187
-
188
- return discrepancies;
189
- }
190
-
191
- describe("Synthetic models", () => {
192
- it("should match API model definitions", { timeout: 30000 }, async () => {
193
- const apiModels = await fetchApiModels();
194
- const discrepancies = compareModels(apiModels, SYNTHETIC_MODELS);
195
-
196
- if (discrepancies.length > 0) {
197
- console.error("\nModel discrepancies found:");
198
- console.error("==========================");
199
- for (const d of discrepancies) {
200
- if (d.field === "exists") {
201
- if (d.hardcoded) {
202
- console.error(` ${d.model}: Missing from API`);
203
- } else {
204
- console.error(` ${d.model}: Missing from hardcoded models (NEW)`);
205
- }
206
- } else {
207
- console.error(` ${d.model}.${d.field}:`);
208
- console.error(` hardcoded: ${JSON.stringify(d.hardcoded)}`);
209
- console.error(` api: ${JSON.stringify(d.api)}`);
210
- }
211
- }
212
- console.error("==========================\n");
213
- }
214
-
215
- expect(discrepancies).toHaveLength(0);
216
- });
217
- });