| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- /**
- * 模型目录种子脚本 - 从 dashscope-models-20260808.json 导入 236 个模型到 DB
- * 幂等:使用 upsert,可安全重复执行
- * 用法:node scripts/seed-models.mjs
- */
- import { PrismaClient } from '@prisma/client';
- import { readFileSync } from 'node:fs';
- import { join, dirname } from 'node:path';
- import { fileURLToPath } from 'node:url';
- const __dirname = dirname(fileURLToPath(import.meta.url));
- const DATA_PATH = join(__dirname, '..', '..', '..', 'docs', 'models', 'dashscope-models-20260808.json');
- // ---- 分类逻辑:按优先级 speech > image > vision > chat ----
- function classifyCategory(publicId) {
- const lower = publicId.toLowerCase();
- // speech: TTS / ASR / audio / realtime / livetranslate / s2s
- if (/tts|asr|audio|speech|realtime|livetranslate|s2s/.test(lower)) return 'speech';
- // image
- if (/image|wan2/i.test(lower)) return 'image';
- // vision: vl / omni / ocr
- if (/vl|omni|ocr/.test(lower)) return 'vision';
- return 'chat';
- }
- function classifyProvider(publicId) {
- const lower = publicId.toLowerCase();
- if (lower.startsWith('glm') || lower.includes('zhipu/')) return 'Zhipu';
- if (lower.includes('deepseek')) return 'DeepSeek';
- if (lower.includes('minimax')) return 'MiniMax';
- if (lower.includes('kimi')) return 'Kimi';
- if (lower.includes('siliconflow')) return 'SiliconFlow';
- if (lower.includes('vanchin')) return 'Vanchin';
- if (lower.includes('xiaomi')) return 'Xiaomi';
- // Alibaba: qwen, qvq, qwq, tongyi, codeqwen, fun-, sre, test, wan, z-image, gui
- if (
- lower.startsWith('qwen') || lower.startsWith('qvq') || lower.startsWith('qwq') ||
- lower.startsWith('tongyi') || lower.startsWith('codeqwen') ||
- lower.startsWith('fun-') || lower.startsWith('sre-') || lower.startsWith('test-') ||
- lower.startsWith('wan') || lower.startsWith('z-image') || lower.startsWith('gui')
- ) {
- return 'Alibaba';
- }
- return 'Other';
- }
- function makeName(publicId) {
- // 尝试提取一个适合展示的名字
- const parts = publicId.split('/');
- return parts[parts.length - 1] ?? publicId;
- }
- function makeDescription(category, provider, publicId) {
- switch (category) {
- case 'speech': return `${provider} 语音模型: ${publicId}`;
- case 'image': return `${provider} 图像模型: ${publicId}`;
- case 'vision': return `${provider} 视觉模型: ${publicId}`;
- default: return `${provider} 对话模型: ${publicId}`;
- }
- }
- const RECOMMENDED = new Set([
- 'glm-5.2',
- 'glm-5.2-fast-preview',
- 'deepseek-v4-flash',
- 'deepseek-v4-pro',
- 'qwen-max',
- 'qwen-plus',
- 'qwen-turbo',
- 'qwen-flash',
- 'qwen3-max',
- 'qwen3-coder-plus',
- 'kimi-k2.6',
- 'MiniMax-M3',
- ]);
- async function main() {
- const prisma = new PrismaClient();
- try {
- const raw = readFileSync(DATA_PATH, 'utf-8');
- const data = JSON.parse(raw);
- const models = /** @type {string[]} */ (data.models);
- if (!models || models.length === 0) {
- console.error('No models found in JSON file');
- process.exit(1);
- }
- console.log(`Seeding ${models.length} models...`);
- let upserted = 0;
- for (let i = 0; i < models.length; i++) {
- const publicId = models[i];
- const category = classifyCategory(publicId);
- const provider = classifyProvider(publicId);
- const name = makeName(publicId);
- const description = makeDescription(category, provider, publicId);
- const isRecommended = RECOMMENDED.has(publicId);
- try {
- await prisma.modelCatalog.upsert({
- where: { publicId },
- update: {
- upstreamId: publicId,
- name,
- provider,
- category,
- description,
- isRecommended,
- status: 'active',
- sortOrder: i,
- },
- create: {
- publicId,
- upstreamId: publicId,
- name,
- provider,
- category,
- description,
- // TODO: 价格待定,后续通过管理后台设置
- // 定价规则:DashScope 价格 × 0.7
- inputPriceCents: 0,
- outputPriceCents: 0,
- isRecommended,
- status: 'active',
- sortOrder: i,
- },
- });
- upserted++;
- } catch (err) {
- console.error(`Failed to upsert ${publicId}: ${err.message}`);
- }
- }
- console.log(`Done. Upserted ${upserted}/${models.length} models.`);
- // 打印分类统计
- const counts = {};
- for (const m of models) {
- const cat = classifyCategory(m);
- counts[cat] = (counts[cat] || 0) + 1;
- }
- console.log('Category counts:', counts);
- await prisma.$disconnect();
- } catch (err) {
- console.error('Seed failed:', err);
- process.exit(1);
- }
- }
- main();
|