W
File size: 2,684 Bytes
2b64d42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env node
/**
 * Regenerate the MODELS array embedded in docs/index.html from the canonical
 * src/models.js catalog so the GitHub Pages site never drifts.
 *
 * Run:  node scripts/gen-docs-models.js
 *       — writes back to docs/index.html in place
 */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { MODELS } from '../src/models.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const docsPath = path.join(__dirname, '..', 'docs', 'index.html');

const PROVIDER_ALIAS = { alibaba: 'qwen' };

function isThinking(id, info) {
  if (id.includes('thinking')) return true;
  if (info.modelUid && /THINKING|REASONING/.test(info.modelUid)) return true;
  return false;
}

const FREE_TIER_HINTS = new Set([
  'gemini-2.5-flash', 'gemini-3.0-flash-minimal', 'glm-4.7', 'swe-1.5-fast',
]);

function buildEntries() {
  const entries = [];
  for (const [id, info] of Object.entries(MODELS)) {
    if (info.deprecated) continue;
    const provider = PROVIDER_ALIAS[info.provider] || info.provider;
    const e = { k: id, p: provider, c: info.credit ?? 0 };
    if (isThinking(id, info)) e.thinking = 1;
    if (FREE_TIER_HINTS.has(id)) e.free = 1;
    entries.push(e);
  }
  return entries;
}

function formatEntry(e) {
  const parts = [`k:'${e.k}'`, `p:'${e.p}'`, `c:${e.c}`];
  if (e.thinking) parts.push('thinking:1');
  if (e.free) parts.push('free:1');
  return `    {${parts.join(',')}}`;
}

function buildArrayLiteral(entries) {
  return [
    '  const MODELS = [',
    entries.map(formatEntry).join(',\n') + ',',
    '  ];',
  ].join('\n');
}

const html = fs.readFileSync(docsPath, 'utf8');
const start = html.indexOf('const MODELS = [');
if (start === -1) {
  console.error('Could not find "const MODELS = [" anchor in docs/index.html');
  process.exit(1);
}
const endMarker = '\n  ];';
const end = html.indexOf(endMarker, start);
if (end === -1) {
  console.error('Could not find closing "];" for MODELS array');
  process.exit(1);
}
const lineStart = html.lastIndexOf('\n', start);
const prefix = lineStart === -1 ? '' : html.slice(0, lineStart + 1);
const suffix = html.slice(end + endMarker.length);
const entries = buildEntries();
const literal = buildArrayLiteral(entries);
const next = `${prefix}${literal}${suffix}`;
fs.writeFileSync(docsPath, next, 'utf8');

const byProvider = entries.reduce((acc, e) => {
  acc[e.p] = (acc[e.p] || 0) + 1;
  return acc;
}, {});
console.log(`docs/index.html updated: ${entries.length} models`);
console.log('By provider:', byProvider);
console.log(`thinking: ${entries.filter(e => e.thinking).length}, free: ${entries.filter(e => e.free).length}`);