gongxiangp2/scripts/audit-ai-model-contracts.ts

27 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** Read-only inventory. Does not load keys, modify configuration or call any model. */
import { readFile, writeFile, mkdir, stat } from 'node:fs/promises'
import path from 'node:path'
import { requestAiModelCapabilityCatalog } from '../lib/ai-model-http'
import { auditAiModelContracts, renderAiModelContractAudit } from '../lib/ai-model-contract-audit'
async function main() {
const args = process.argv.slice(2)
const flag = (name: string) => { const index = args.indexOf(name); return index < 0 ? undefined : args[index + 1] }
const output = path.resolve(flag('--output') || 'output/ai-model-contract-audit')
const catalogFile = flag('--catalog'), snapshotFile = flag('--admin-state')
const catalog = catalogFile ? JSON.parse(await readFile(catalogFile, 'utf8')) : await (async () => { const response = await requestAiModelCapabilityCatalog('https://api.vip98k.com/v1'); if (!response.ok) throw new Error(`公开目录 HTTP ${response.status}`); return response.json() })()
const snapshot = snapshotFile ? JSON.parse(await readFile(snapshotFile, 'utf8')) : undefined
const report = auditAiModelContracts(catalog, snapshot)
const at = new Date().toISOString()
const catalogAt = catalogFile ? (await stat(catalogFile)).mtime.toISOString() : at
await mkdir(output, { recursive: true })
await writeFile(path.join(output, 'catalog-source.json'), JSON.stringify(catalog, null, 2))
await writeFile(path.join(output, 'model-contracts.json'), JSON.stringify({ inspectedAt: at, catalogCapturedAt: catalogAt, adminSnapshotAt: snapshot?.at || null, ...report }, null, 2))
const displayTime = (iso: string) => new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Singapore', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h23' }).format(new Date(iso)) + '(UTC+8)'
const markdown = renderAiModelContractAudit(report, `${displayTime(at)};目录抓取于 ${displayTime(catalogAt)}${snapshot?.at ? `;平台状态读取于 ${displayTime(snapshot.at)}` : ''}`)
await writeFile(path.join(output, 'model-contracts.md'), markdown)
if (flag('--markdown')) await writeFile(flag('--markdown')!, markdown)
console.log(JSON.stringify({ models: report.modelCount, nativeOnly: report.nativeOnly.length, byKind: report.models.reduce<Record<string, number>>((counts, model) => (counts[model.kind] = (counts[model.kind] || 0) + 1, counts), {}), output, paidCalls: 0, writesToProduction: 0 }))
}
main().catch(error => { console.error(error instanceof Error ? error.message : '目录核查失败'); process.exitCode = 1 })