mirror of
https://git.openapi.site/https://github.com/desirecore/config-center.git
synced 2026-10-21 05:24:25 +08:00
fix(compute): verify official model data and document sources (#112)
* fix(compute): verify official data and document model sources * test(docs): guard provider metadata source freshness
This commit is contained in:
66
__tests__/validate-sources.test.mjs
Normal file
66
__tests__/validate-sources.test.mjs
Normal file
@@ -0,0 +1,66 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { modelFingerprint, configFingerprint, sourceUrlError, validateSources } from '../scripts/validate-sources.mjs'
|
||||
|
||||
function fixture(callback) {
|
||||
const root = mkdtempSync(join(tmpdir(), 'model-sources-'))
|
||||
const model = { modelName: 'gpt-test', contextWindow: 1000 }
|
||||
const provider = { priceCurrency: 'USD', models: [model] }
|
||||
for (const folder of ['providers', 'coding-plans', 'model-specs']) {
|
||||
mkdirSync(join(root, 'compute', folder), { recursive: true })
|
||||
writeFileSync(join(root, 'compute', folder, '_index.json'), JSON.stringify({ order: folder === 'providers' ? ['openai'] : [] }))
|
||||
}
|
||||
mkdirSync(join(root, 'docs/model-sources/providers'), { recursive: true })
|
||||
const dataPath = join(root, 'compute/providers/openai.json')
|
||||
const docPath = join(root, 'docs/model-sources/providers/openai.md')
|
||||
writeFileSync(dataPath, JSON.stringify(provider))
|
||||
const source = { id: 'official', url: 'https://developers.openai.com/api/docs/models', kind: 'official-doc', scope: 'OpenAI 测试接入', retrieval: 'fetched', checkedAt: '2026-10-03' }
|
||||
const metadata = { formatVersion: 1, supplier: 'openai', checkedAt: '2026-10-03', sources: [source] }
|
||||
const page = (status = 'partial', src = metadata) => `# OpenAI\n\n### official\n\n<!-- source-config: compute/providers/openai.json -->\n<!-- source-config-fingerprint: ${configFingerprint(provider)} -->\n\n| \`gpt-test\` | 1000 / 未声明 | USD:未声明 / 未声明 | \`contextWindow\`→[official](#official) | [official](#official) | ${status} | \`${modelFingerprint(model)}\` |\n\n<!-- source-metadata:start -->\n\`\`\`json\n${JSON.stringify(src)}\n\`\`\`\n<!-- source-metadata:end -->\n`
|
||||
writeFileSync(docPath, page())
|
||||
try { callback({ root, model, provider, dataPath, docPath, page, metadata }) }
|
||||
finally { rmSync(root, { recursive: true, force: true }) }
|
||||
}
|
||||
|
||||
test('配置已变而来源记录未复核时必须失败', () => fixture(({ root, provider, dataPath }) => {
|
||||
assert.equal(validateSources(root).errors.length, 0)
|
||||
provider.models[0].contextWindow = 2000
|
||||
writeFileSync(dataPath, JSON.stringify(provider))
|
||||
assert.ok(validateSources(root).errors.some((message) => message.includes('来源记录过期')))
|
||||
}))
|
||||
|
||||
test('来源覆盖遗漏及重复配置 ID 都会失败', () => fixture(({ root, provider, dataPath }) => {
|
||||
provider.models.push({ modelName: 'undocumented' })
|
||||
writeFileSync(dataPath, JSON.stringify(provider))
|
||||
assert.ok(validateSources(root).errors.some((message) => message.includes('undocumented') && message.includes('缺少 Markdown')))
|
||||
provider.models.push({ ...provider.models[0] })
|
||||
writeFileSync(dataPath, JSON.stringify(provider))
|
||||
assert.ok(validateSources(root).errors.some((message) => message.includes('配置中存在重复 ID')))
|
||||
}))
|
||||
|
||||
test('页面壳不能支持已核字段,整条全部核实标记被拒绝', () => fixture(({ root, metadata, page, docPath }) => {
|
||||
metadata.sources[0].retrieval = 'shell'
|
||||
writeFileSync(docPath, page('partial', metadata))
|
||||
assert.ok(validateSources(root).errors.some((message) => message.includes('不能以页面壳')))
|
||||
metadata.sources[0].retrieval = 'fetched'
|
||||
writeFileSync(docPath, page('verified', metadata))
|
||||
assert.ok(validateSources(root).errors.some((message) => message.includes('禁止将整条模型标为全部核实')))
|
||||
}))
|
||||
|
||||
test('拒绝第三方、社区、伪官方仓库和带凭据的来源 URL', () => {
|
||||
assert.equal(sourceUrlError('openai', 'https://developers.openai.com/api/docs/models'), null)
|
||||
assert.ok(sourceUrlError('openai', 'https://example.com/openai/models'))
|
||||
assert.ok(sourceUrlError('openai', 'https://github.com/unrelated/models'))
|
||||
assert.ok(sourceUrlError('volcengine', 'https://www.volcengine.com/article/123'))
|
||||
assert.ok(sourceUrlError('openai', 'https://developers.openai.com/docs?api_key=secret'))
|
||||
})
|
||||
|
||||
test('端点变化也必须复核来源,即使模型参数完全未变', () => fixture(({ root, provider, dataPath }) => {
|
||||
assert.equal(validateSources(root).errors.length, 0)
|
||||
provider.baseUrl = 'https://api.openai.com/v1'
|
||||
writeFileSync(dataPath, JSON.stringify(provider))
|
||||
assert.ok(validateSources(root).errors.some((message) => message.includes('平台元数据来源记录过期')))
|
||||
}))
|
||||
@@ -446,16 +446,17 @@ describe('真实数据全量校验', () => {
|
||||
assert.equal(Number(watermark.trim().split(/\s+/).at(-1)), 1784554658)
|
||||
})
|
||||
|
||||
it('Ox Alpha 应提供与 OpenRouter 一致的多模态推理规格', () => {
|
||||
it('OpenRouter 不再预置当前官网目录未列出的 Ox Alpha,历史兼容规格保留', () => {
|
||||
const provider = JSON.parse(readFileSync(join(ROOT, 'compute', 'providers', 'openrouter.json'), 'utf8'))
|
||||
const specFile = JSON.parse(readFileSync(join(ROOT, 'compute', 'model-specs', 'stealth.json'), 'utf8'))
|
||||
const model = provider.models.find((item) => item.modelName === 'stealth/ox-alpha')
|
||||
const modelSpec = specFile.specs.find((item) => item.id === 'ox-alpha')
|
||||
|
||||
assert.ok(model, 'OpenRouter provider 缺少 stealth/ox-alpha')
|
||||
assert.equal(model, undefined)
|
||||
assert.ok(provider.tombstones.includes('stealth/ox-alpha'))
|
||||
assert.ok(modelSpec, 'model-specs 缺少 ox-alpha')
|
||||
|
||||
for (const item of [model, modelSpec.spec]) {
|
||||
for (const item of [modelSpec.spec]) {
|
||||
assert.equal(item.contextWindow, 1048576)
|
||||
assert.equal(item.maxOutputTokens, 131072)
|
||||
assert.equal(item.defaultTemperature, 1)
|
||||
@@ -465,11 +466,6 @@ describe('真实数据全量校验', () => {
|
||||
assert.ok(item.capabilities.includes('video_understanding'))
|
||||
}
|
||||
|
||||
assert.deepEqual(model.extra.reasoning.supportedEfforts, ['low', 'high', 'max'])
|
||||
assert.equal(model.extra.reasoning.defaultEffort, 'max')
|
||||
assert.equal(model.extra.thinking.disableSupported, false)
|
||||
assert.equal(model.inputPrice, 0)
|
||||
assert.equal(model.outputPrice, 0)
|
||||
assert.deepEqual(modelSpec.match.exact, ['ox-alpha', 'stealth/ox-alpha'])
|
||||
assert.equal(modelSpec.spec.supportsReasoning, true)
|
||||
assert.equal(modelSpec.spec.releasedAt, '2026-08-21')
|
||||
@@ -948,7 +944,9 @@ describe('真实数据全量校验', () => {
|
||||
assert.equal(preview.extra.reasoning.defaultEffort, 'max')
|
||||
assert.equal(read('providers', 'minimax').models.some((item) => item.modelName === previewId), false)
|
||||
const embed = findModel('providers', 'cohere', 'embed-v5.0-pro')
|
||||
assert.deepEqual(embed.extra.dimensions, [256, 512, 768, 1024, 1536, 2048])
|
||||
assert.equal(embed.extra.dimensions, undefined, '当前兼容 API 不支持 dimensions 参数')
|
||||
const intrinsicEmbed = read('model-specs', 'cohere').specs.find((item) => item.id === 'embed-v5.0-pro')
|
||||
assert.deepEqual(intrinsicEmbed.spec.extra.dimensions, [256, 512, 768, 1024, 1536, 2048])
|
||||
assert.equal(embed.inputPrice, undefined)
|
||||
assert.equal(read('providers', 'cohere').models.some((item) => item.modelName === 'rerank-v4.0-fast'), false, '不向 OpenAI 兼容端点声明原生 Rerank 接入')
|
||||
assert.ok(read('model-specs', 'cohere').specs.some((item) => item.id === 'rerank-v4.0-fast'))
|
||||
@@ -993,6 +991,30 @@ describe('真实数据全量校验', () => {
|
||||
assert.equal((command.match.exact ?? []).map(normalize).includes('command-a-plus'), false)
|
||||
})
|
||||
|
||||
it('官网复核修正 Qwen 重复规格、Fast 大小写别名与 OpenRouter 动态价格', () => {
|
||||
const read = (folder, name) => JSON.parse(readFileSync(join(ROOT, 'compute', folder, `${name}.json`), 'utf8'))
|
||||
const qwen = read('model-specs', 'qwen').specs.filter((item) => item.id === 'qwen3.7-max')
|
||||
assert.equal(qwen.length, 1)
|
||||
assert.equal(qwen[0].spec.contextWindow, 1000000)
|
||||
assert.equal(qwen[0].spec.maxOutputTokens, 131072)
|
||||
assert.equal(qwen[0].spec.capabilities.includes('vision'), false)
|
||||
for (const [folder, name] of [['providers', 'dashscope'], ['coding-plans', 'dashscope-token-plan']]) {
|
||||
assert.equal(read(folder, name).models.find((item) => item.modelName === 'qwen3.7-max').maxOutputTokens, 131072)
|
||||
}
|
||||
const fast = read('model-specs', 'minimax').specs.filter((item) => item.id.toLowerCase() === 'minimax-hailuo-2.3-fast')
|
||||
assert.equal(fast.length, 1)
|
||||
assert.deepEqual(fast[0].match.exact, ['MiniMax-Hailuo-2.3-Fast', 'MiniMax-Hailuo-2.3-fast'])
|
||||
assert.equal(fast[0].routing.eligibleForAgent, false)
|
||||
const router = read('providers', 'openrouter')
|
||||
const auto = router.models.find((item) => item.modelName === 'openrouter/auto')
|
||||
assert.equal(auto.inputPrice, undefined)
|
||||
assert.equal(auto.outputPrice, undefined)
|
||||
for (const id of ['stealth/ox-alpha', 'openai/gpt-oss-120b:free', 'qwen/qwen3-coder:free']) {
|
||||
assert.equal(router.models.some((item) => item.modelName === id), false)
|
||||
assert.ok(router.tombstones.includes(id))
|
||||
}
|
||||
})
|
||||
|
||||
it('所有 Provider 应按供应商归属计价,模型来源不覆盖供应商币种', () => {
|
||||
const expectedCurrencies = {
|
||||
anthropic: 'USD',
|
||||
|
||||
Reference in New Issue
Block a user