Spaces:
Sleeping
Sleeping
File size: 5,165 Bytes
ed57015 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | /**
* export-catalog — build the signed catalog product from the live freeapi DB.
*
* This is the single source of truth pipeline: every model and its resolved
* quirks come straight out of the same SQLite DB the router uses, so the
* published catalog can never drift from what the gateway actually serves.
* The output JSON is what the standalone catalog server (../../catalog) hands
* to clients — fresh ("live") to Premium, a monthly snapshot to free users.
*
* Usage:
* tsx src/scripts/export-catalog.ts [--db <path>] [--out <path>]
* [--version <YYYY.MM.DD>] [--tier live|monthly]
* [--stdout]
*
* Defaults: reads the server's data/freeapi.db, writes the suite's
* catalog/data/catalog.live.json. The catalog server signs it separately
* (catalog/src/scripts/sign-catalog.ts) so the signature covers the exact
* bytes served.
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { initDb, getDb } from '../db/index.js';
import { getAllProviders } from '../providers/index.js';
import { resolveQuirksByModel, quirkKey, listQuirkDefinitions } from '../services/quirks.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SUITE_ROOT = path.resolve(__dirname, '../../../..'); // freeapi/server/src/scripts -> suite root
function arg(name: string): string | undefined {
const i = process.argv.indexOf(`--${name}`);
return i !== -1 ? process.argv[i + 1] : undefined;
}
function flag(name: string): boolean {
return process.argv.includes(`--${name}`);
}
function dateVersion(d = new Date()): string {
const yyyy = d.getUTCFullYear();
const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
const dd = String(d.getUTCDate()).padStart(2, '0');
return `${yyyy}.${mm}.${dd}`;
}
interface ModelRow {
platform: string;
model_id: string;
display_name: string;
intelligence_rank: number;
speed_rank: number;
size_label: string;
rpm_limit: number | null;
rpd_limit: number | null;
tpm_limit: number | null;
tpd_limit: number | null;
monthly_token_budget: string;
context_window: number | null;
enabled: number;
supports_vision: number;
supports_tools: number;
}
function main() {
const dbPath = arg('db');
const tier = (arg('tier') as 'live' | 'monthly' | undefined) ?? 'live';
const version = arg('version') ?? dateVersion();
// In --stdout mode stdout must carry ONLY the JSON document; incidental
// logs (e.g. initDb's "Database initialized at ...") go to stderr instead.
if (flag('stdout')) console.log = console.error;
initDb(dbPath);
const db = getDb();
const models = db
.prepare(
`SELECT platform, model_id, display_name, intelligence_rank, speed_rank, size_label,
rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window,
enabled, supports_vision, supports_tools
FROM models
ORDER BY intelligence_rank ASC, platform ASC, model_id ASC`,
)
.all() as ModelRow[];
const quirksByModel = resolveQuirksByModel(db);
const quirkDefs = listQuirkDefinitions(db);
// Only platforms that actually have a registered provider AND a model in the
// catalog — skip 'custom' (user-supplied endpoint, nothing to publish).
const livePlatforms = new Set(models.map((m) => m.platform));
const platforms = getAllProviders()
.filter((p) => p.platform !== 'custom' && livePlatforms.has(p.platform))
.map((p) => ({ id: p.platform, name: p.name }))
.sort((a, b) => a.id.localeCompare(b.id));
const catalogModels = models.map((m) => ({
platform: m.platform,
modelId: m.model_id,
displayName: m.display_name,
intelligenceRank: m.intelligence_rank,
speedRank: m.speed_rank,
sizeLabel: m.size_label,
limits: {
rpm: m.rpm_limit,
rpd: m.rpd_limit,
tpm: m.tpm_limit,
tpd: m.tpd_limit,
},
monthlyTokenBudget: m.monthly_token_budget,
contextWindow: m.context_window,
enabled: m.enabled === 1,
supportsVision: m.supports_vision === 1,
supportsTools: m.supports_tools === 1,
quirks: quirksByModel.get(quirkKey(m.platform, m.model_id)) ?? [],
}));
const catalog = {
version,
generatedAt: new Date().toISOString(),
tier,
counts: {
platforms: platforms.length,
models: catalogModels.length,
enabledModels: catalogModels.filter((m) => m.enabled).length,
quirks: quirkDefs.length,
},
platforms,
models: catalogModels,
quirks: quirkDefs,
};
const json = JSON.stringify(catalog, null, 2) + '\n';
if (flag('stdout')) {
process.stdout.write(json);
return;
}
const outPath = arg('out') ?? path.join(SUITE_ROOT, 'catalog', 'data', `catalog.${tier}.json`);
fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, json);
console.log(
`Wrote ${tier} catalog ${version}: ${catalog.counts.models} models ` +
`(${catalog.counts.enabledModels} enabled), ${catalog.counts.platforms} platforms, ` +
`${catalog.counts.quirks} quirks -> ${outPath}`,
);
}
main();
|