diff --git a/README.md b/README.md index b3f19b9..a9916de 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,14 @@ PR #1 曾把 reasoning 模型的 `defaultTemperature` / `defaultTopP` 写为 `nu { "modelName": "deepseek-reasoner", "displayName": "DeepSeek Reasoner", ... } ``` +### 关键规则:计价币种按 Provider 归属决定 + +- 国内 Provider 必须使用 `priceCurrency: "CNY"`,并写入国内平台人民币价格。 +- 国外 Provider 必须使用 `priceCurrency: "USD"`,并写入美元价格。 +- Provider 归属与其托管模型的产地冲突时,以 Provider 为准。例如,OpenRouter + 托管的 Qwen 模型仍按 USD 计价;国内 Provider 托管的国外模型仍按 CNY 计价。 +- 新增 Provider 时必须同步更新全量币种归属测试,避免未分类数据进入预置。 + ### 关键规则:新增字段需先升级老客户端 schema `provider` 和 `model` 顶层均启用 `additionalProperties: false`。如需新增字段: diff --git a/__tests__/validate.test.mjs b/__tests__/validate.test.mjs index 1452f4b..924b010 100644 --- a/__tests__/validate.test.mjs +++ b/__tests__/validate.test.mjs @@ -134,6 +134,80 @@ describe('真实数据全量校验', () => { } } }) + + it('所有 Provider 应按供应商归属计价,模型来源不覆盖供应商币种', () => { + const expectedCurrencies = { + anthropic: 'USD', + baichuan: 'CNY', + baidu: 'CNY', + cohere: 'USD', + dashscope: 'CNY', + deepseek: 'CNY', + google: 'USD', + kling: 'CNY', + lingyiwanwu: 'CNY', + 'local-whisper': 'USD', + minimax: 'CNY', + mistral: 'USD', + moonshot: 'CNY', + ollama: 'USD', + openai: 'USD', + 'openai-codex': 'USD', + openrouter: 'USD', + perplexity: 'USD', + siliconflow: 'CNY', + stability: 'USD', + tencent: 'CNY', + volcengine: 'CNY', + xai: 'USD', + xiaomi: 'CNY', + xunfei: 'CNY', + zhipu: 'CNY', + 'zhipu-embedding': 'CNY', + } + const dir = join(ROOT, 'compute', 'providers') + const actualProviders = [] + + for (const file of readdirSync(dir)) { + if (!file.endsWith('.json') || file === '_index.json') continue + const data = JSON.parse(readFileSync(join(dir, file), 'utf8')) + actualProviders.push(data.provider) + assert.ok( + Object.hasOwn(expectedCurrencies, data.provider), + `未给 Provider ${data.provider} 声明计价归属`, + ) + assert.equal( + data.priceCurrency, + expectedCurrencies[data.provider], + `${data.label} 应按供应商归属使用 ${expectedCurrencies[data.provider]} 计价`, + ) + } + + assert.deepEqual(actualProviders.sort(), Object.keys(expectedCurrencies).sort()) + }) + + it('MiniMax 应使用国内开放平台人民币价', () => { + const provider = JSON.parse(readFileSync(join(ROOT, 'compute', 'providers', 'minimax.json'), 'utf8')) + const model = (modelName) => provider.models.find((item) => item.modelName === modelName) + + assert.equal(provider.priceCurrency, 'CNY') + assert.deepEqual( + [model('MiniMax-M3').inputPrice, model('MiniMax-M3').outputPrice], + [2.1, 8.4], + ) + assert.deepEqual( + [model('MiniMax-M2.7').inputPrice, model('MiniMax-M2.7').outputPrice], + [2.1, 8.4], + ) + assert.deepEqual( + [model('MiniMax-M2.7-highspeed').inputPrice, model('MiniMax-M2.7-highspeed').outputPrice], + [4.2, 16.8], + ) + assert.equal(model('image-01').extra.pricePerImage, 0.025) + assert.equal(model('speech-2.8-hd').extra.pricePerMillionCharacters, 350) + assert.equal(model('speech-2.8-turbo').extra.pricePerMillionCharacters, 200) + assert.equal(model('music-2.6').extra.pricePerSongUpToFiveMinutes, 1) + }) }) // ==================== Runtime 清单反例 ==================== diff --git a/compute/providers/minimax.json b/compute/providers/minimax.json index 3b4ad00..5b3fddb 100644 --- a/compute/providers/minimax.json +++ b/compute/providers/minimax.json @@ -9,7 +9,7 @@ "apiKeyVerified": false, "enabled": false, "status": "unconfigured", - "priceCurrency": "USD", + "priceCurrency": "CNY", "services": [ "chat", "fast", @@ -41,40 +41,40 @@ "vision", "long_context" ], - "inputPrice": 0.3, - "outputPrice": 1.2, + "inputPrice": 2.1, + "outputPrice": 8.4, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { "pricingTiers": [ { "maxInputTokens": 512000, - "inputPrice": 0.3, - "outputPrice": 1.2, - "cacheReadPrice": 0.06 + "inputPrice": 2.1, + "outputPrice": 8.4, + "cacheReadPrice": 0.42 }, { "maxInputTokens": 1000000, - "inputPrice": 0.6, - "outputPrice": 2.4, - "cacheReadPrice": 0.12 + "inputPrice": 4.2, + "outputPrice": 16.8, + "cacheReadPrice": 0.84 } ], "priorityPricingTiers": [ { "maxInputTokens": 512000, - "inputPrice": 0.45, - "outputPrice": 1.8, - "cacheReadPrice": 0.09 + "inputPrice": 3.15, + "outputPrice": 12.6, + "cacheReadPrice": 0.63 }, { "maxInputTokens": 1000000, - "inputPrice": 0.9, - "outputPrice": 3.6, - "cacheReadPrice": 0.18 + "inputPrice": 6.3, + "outputPrice": 25.2, + "cacheReadPrice": 1.26 } ], - "pricingNotes": "Permanent 50% off prices from MiniMax Pay as You Go page." + "pricingNotes": "MiniMax 国内开放平台按量计费永久五折价格。" } }, { @@ -94,13 +94,13 @@ "tool_use", "vision" ], - "inputPrice": 0.3, - "outputPrice": 1.2, + "inputPrice": 2.1, + "outputPrice": 8.4, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { - "cacheReadPrice": 0.06, - "cacheWritePrice": 0.375, + "cacheReadPrice": 0.42, + "cacheWritePrice": 2.625, "outputSpeedTps": 60 } }, @@ -121,13 +121,13 @@ "fast", "vision" ], - "inputPrice": 0.6, - "outputPrice": 2.4, + "inputPrice": 4.2, + "outputPrice": 16.8, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { - "cacheReadPrice": 0.06, - "cacheWritePrice": 0.375, + "cacheReadPrice": 0.42, + "cacheWritePrice": 2.625, "outputSpeedTps": 100 } }, @@ -148,13 +148,13 @@ "tool_use", "long_context" ], - "inputPrice": 0.3, - "outputPrice": 1.2, + "inputPrice": 2.1, + "outputPrice": 8.4, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { - "cacheReadPrice": 0.03, - "cacheWritePrice": 0.375, + "cacheReadPrice": 0.21, + "cacheWritePrice": 2.625, "outputSpeedTps": 60 } }, @@ -174,13 +174,13 @@ "tool_use", "fast" ], - "inputPrice": 0.6, - "outputPrice": 2.4, + "inputPrice": 4.2, + "outputPrice": 16.8, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { - "cacheReadPrice": 0.03, - "cacheWritePrice": 0.375, + "cacheReadPrice": 0.21, + "cacheWritePrice": 2.625, "outputSpeedTps": 100 } }, @@ -199,13 +199,13 @@ "tool_use", "reasoning" ], - "inputPrice": 0.3, - "outputPrice": 1.2, + "inputPrice": 2.1, + "outputPrice": 8.4, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { - "cacheReadPrice": 0.03, - "cacheWritePrice": 0.375, + "cacheReadPrice": 0.21, + "cacheWritePrice": 2.625, "outputSpeedTps": 60 } }, @@ -225,13 +225,13 @@ "tool_use", "fast" ], - "inputPrice": 0.6, - "outputPrice": 2.4, + "inputPrice": 4.2, + "outputPrice": 16.8, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { - "cacheReadPrice": 0.03, - "cacheWritePrice": 0.375, + "cacheReadPrice": 0.21, + "cacheWritePrice": 2.625, "outputSpeedTps": 100 } }, @@ -249,14 +249,14 @@ "code", "tool_use" ], - "inputPrice": 0.3, - "outputPrice": 1.2, + "inputPrice": 2.1, + "outputPrice": 8.4, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { "legacy": true, - "cacheReadPrice": 0.03, - "cacheWritePrice": 0.375 + "cacheReadPrice": 0.21, + "cacheWritePrice": 2.625 } }, { @@ -272,13 +272,13 @@ "chat", "roleplay" ], - "inputPrice": 0.3, - "outputPrice": 1.2, + "inputPrice": 2.1, + "outputPrice": 8.4, "defaultTemperature": 1, "defaultTopP": 0.95, "extra": { - "cacheReadPrice": 0.03, - "cacheWritePrice": 0.375 + "cacheReadPrice": 0.21, + "cacheWritePrice": 2.625 } }, { @@ -311,7 +311,7 @@ "base64" ], "urlExpiry": "24h", - "pricePerImage": 0.0035 + "pricePerImage": 0.025 } }, { @@ -376,7 +376,7 @@ "aac", "ogg" ], - "pricePerMillionCharacters": 100 + "pricePerMillionCharacters": 350 } }, { @@ -403,7 +403,7 @@ "aac", "ogg" ], - "pricePerMillionCharacters": 60 + "pricePerMillionCharacters": 200 } }, { @@ -430,7 +430,7 @@ "aac", "ogg" ], - "pricePerMillionCharacters": 100 + "pricePerMillionCharacters": 350 } }, { @@ -457,7 +457,7 @@ "aac", "ogg" ], - "pricePerMillionCharacters": 60 + "pricePerMillionCharacters": 200 } }, { @@ -484,7 +484,7 @@ "ogg" ], "legacy": true, - "pricePerMillionCharacters": 100 + "pricePerMillionCharacters": 350 } }, { @@ -511,7 +511,7 @@ "ogg" ], "legacy": true, - "pricePerMillionCharacters": 60 + "pricePerMillionCharacters": 200 } }, { @@ -656,7 +656,7 @@ "[Solo]", "[Inst]" ], - "pricePerSongUpToFiveMinutes": 0.15 + "pricePerSongUpToFiveMinutes": 1 } }, { @@ -677,7 +677,7 @@ "hex", "url" ], - "pricePerSongUpToFiveMinutes": 0.15 + "pricePerSongUpToFiveMinutes": 1 } }, { @@ -698,7 +698,7 @@ "hex", "url" ], - "pricePerSongUpToFiveMinutes": 0.15 + "pricePerSongUpToFiveMinutes": 1 } }, { diff --git a/manifest.json b/manifest.json index 0be9748..8a19fca 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "presetDataVersion": 72, - "updatedAt": "2026-07-10", + "presetDataVersion": 73, + "updatedAt": "2026-07-13", "description": "DesireCore 官方配置中心" }