mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
142 lines
6.8 KiB
TypeScript
142 lines
6.8 KiB
TypeScript
/**
|
|
* WHAT BACKS ENSO, ZEN AND KAI, AS THE BYTES WOULD SAY IT.
|
|
*
|
|
* A reader of the export names Hanzo's models by their own names. The models
|
|
* from other labs stay named where they are competitors on a board or entries
|
|
* in the gateway's catalog; what is never shipped is a model or vendor said to
|
|
* be inside one of ours, in any form a browser receives: a sentence on a page,
|
|
* a string in a script, a value in flight data or a JSON file, a line of the
|
|
* markdown twins.
|
|
*
|
|
* `scan` reads one file's text as the units it is made of — the string
|
|
* literals of a script (and the strings inside a literal that holds a JSON or
|
|
* flight document), the text runs and attribute values of a page, the lines of
|
|
* anything else — and reports each place one of the rules below holds.
|
|
*/
|
|
import { UPSTREAM } from '../../lib/upstream'
|
|
|
|
/** A Hanzo or Zoo model family as a unit names it, in any case. Fonts named Zen are a typeface, not the model. */
|
|
const FAMILY = /\b(?:enso|kai)\b|\benso-[a-z]+|\bzen(?![ -]?(?:variable|mono|sans|serif|display|kaku|maru|old|antique|kurenaido|dots|tokyo|loop))(?:\b|-?\d|-[a-z]+)/i
|
|
|
|
/**
|
|
* What sits inside them: the names a Zen checkpoint is built from (lib/upstream,
|
|
* the one list the Zen pages are held to), the open-model labs Enso routes to,
|
|
* and the vendor routers whose ids stand in for ours.
|
|
*/
|
|
export const BACKING = new RegExp(
|
|
[
|
|
...UPSTREAM.map((n) => `\\b${n.replace(/[.-]/g, '\\$&')}`),
|
|
'kimi',
|
|
'moonshot',
|
|
'\\bglm\\b',
|
|
'\\bglm-\\d',
|
|
'zhipu',
|
|
'\\bz\\.ai\\b',
|
|
'\\bmimo\\b',
|
|
'xiaomi',
|
|
'hunyuan',
|
|
'\\bhy3\\b',
|
|
'tencent',
|
|
'alibaba',
|
|
'openrouter\\/(?:auto|free)\\b',
|
|
].join('|'),
|
|
'i',
|
|
)
|
|
|
|
/** How a record or a sentence says what answered a router's request, or what a model was made from. */
|
|
const ROUTING = /routed arm per item|upstream calls by model id|routed to \$\{|arms? its notes|first rung|\b(?:repackag\w*|re-?host(?:ed)?) of\b/i
|
|
|
|
/** A run's version as enso-bench files it: the lab and the model joined, which only a run record writes. */
|
|
const VERSION = /\b(?:anthropic-claude|openai-gpt|google-gemini|x-ai-grok|deepseek-deepseek|z-ai-glm|moonshotai-kimi)-[a-z0-9]/i
|
|
|
|
/** A list of the labs written as alternatives: a filter that names them ships the names. */
|
|
const ALTERNATION = /\b(?:qwen|kimi|glm|alibaba|moonshot|zhipu|minimax|deepseek)\b\s*\|\s*\b(?:qwen|kimi|glm|alibaba|moonshot|zhipu|minimax|deepseek)\b/i
|
|
|
|
/** A vendor router's id read as one of ours: `openrouter/free` labelled Enso Free, `openrouter/auto` mapped to enso-auto. */
|
|
const ALIAS = /openrouter\/(?:auto|free)[\s\S]{0,120}\b[Ee]nso\b|openrouter\/(?:auto|free)[\s\S]{0,120}\benso-|\b[Ee]nso[- ][\s\S]{0,120}openrouter\/(?:auto|free)/
|
|
|
|
/** Kai's base, which no file names at all. */
|
|
const BASE = /\blaya\b/i
|
|
|
|
export type Finding = { rule: string; at: string }
|
|
|
|
/** The string literals of a script or a JSON document, unescaped as far as a reader needs. */
|
|
function* literals(src: string): Generator<string> {
|
|
const re = /"((?:[^"\\\n]|\\.)*)"|'((?:[^'\\\n]|\\.)*)'|`((?:[^`\\]|\\.)*)`/g
|
|
for (const m of src.matchAll(re)) {
|
|
const body = m[1] ?? m[2] ?? m[3] ?? ''
|
|
yield body.replace(/\\u([0-9a-fA-F]{4})/g, (_, h) => String.fromCharCode(parseInt(h, 16))).replace(/\\(["'`\\/])/g, '$1').replace(/\\n/g, '\n')
|
|
}
|
|
}
|
|
|
|
/** A literal long enough to be a document is read as the strings inside it, two levels down at most. */
|
|
function* units(src: string, depth = 0): Generator<string> {
|
|
for (const l of literals(src)) {
|
|
if (depth < 2 && l.length > 200 && l.includes('"')) yield* units(l, depth + 1)
|
|
else yield l
|
|
}
|
|
}
|
|
|
|
/** Words that sit inside a sentence: a sentence is read across them. */
|
|
const INLINE = /<\/?(?:a|abbr|b|cite|code|em|i|kbd|mark|q|s|small|span|strong|sub|sup|time|u)\b[^>]*>/gi
|
|
|
|
/** A page's text by block (React's `<!-- -->` separators and inline markup joined) and its attribute values; its scripts are read as scripts. */
|
|
function* page(html: string): Generator<string> {
|
|
for (const m of html.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script>/gi)) yield* units(m[1])
|
|
const body = html.replace(/<script\b[^>]*>[\s\S]*?<\/script>|<style\b[^>]*>[\s\S]*?<\/style>/gi, ' ').replace(/<!--\s*-->/g, '')
|
|
for (const m of body.matchAll(/\s[a-z-]+="([^"]*)"/gi)) yield m[1]
|
|
for (const run of body.replace(INLINE, '').split(/<[^>]+>/)) if (run.trim()) yield run.replace(/'|'/g, "'").replace(/&/g, '&')
|
|
}
|
|
|
|
/** A JSON document's strings, and each object's own strings read together, so a record split across fields reads as one. */
|
|
function* json(text: string): Generator<string> {
|
|
yield* units(text)
|
|
let doc: unknown
|
|
try {
|
|
doc = JSON.parse(text)
|
|
} catch {
|
|
return
|
|
}
|
|
const walk = function* (v: unknown): Generator<string> {
|
|
if (Array.isArray(v)) for (const x of v) yield* walk(x)
|
|
else if (v && typeof v === 'object') {
|
|
const own = Object.values(v).flatMap((x) => (typeof x === 'string' ? [x] : Array.isArray(x) ? x.filter((y): y is string => typeof y === 'string') : []))
|
|
if (own.length > 1) yield own.join(' · ')
|
|
for (const x of Object.values(v)) yield* walk(x)
|
|
}
|
|
}
|
|
yield* walk(doc)
|
|
}
|
|
|
|
const around = (text: string, at: number) => text.slice(Math.max(0, at - 70), at + 70).replace(/\s+/g, ' ')
|
|
|
|
/** Every place in one file's text where a rule holds. `kind` is the file's extension. */
|
|
export function scan(text: string, kind: string): Finding[] {
|
|
const found: Finding[] = []
|
|
for (const [rule, re] of [['routing', ROUTING], ['version', VERSION], ['alternation', ALTERNATION], ['alias', ALIAS], ['base', BASE]] as const) {
|
|
for (const m of text.matchAll(new RegExp(re.source, `${re.flags}g`))) found.push({ rule, at: around(text, m.index) })
|
|
}
|
|
// A `.txt` is flight data when it opens on a `<row>:` (a hint row's id is empty: `:HL[…]`), else it is prose (llms.txt) and read by the line.
|
|
const flight = kind === '.txt' && /^[0-9a-f]*:/.test(text)
|
|
const parts = kind === '.html' ? page(text) : kind === '.json' ? json(text) : kind === '.js' || kind === '.mjs' || flight ? units(text) : text.split('\n')
|
|
const seen = new Set<string>()
|
|
for (const u of parts) {
|
|
// Explicit contrasts distinguishing third-party models from first-party ones for billing
|
|
if (/\b(?:third-party|3rd-party)\s+models\b/i.test(u) && /\bfirst-party\s+models\b/i.test(u)) continue
|
|
const f = FAMILY.exec(u)
|
|
const b = f && BACKING.exec(u)
|
|
if (b && !seen.has(u)) {
|
|
seen.add(u)
|
|
found.push({ rule: 'inside', at: around(u, b.index) })
|
|
}
|
|
}
|
|
// A link whose address names a model inside ours and whose words name ours says the same thing in two parts.
|
|
if (kind === '.html') {
|
|
for (const m of text.matchAll(/<a\b[^>]*\bhref="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi)) {
|
|
const words = m[2].replace(/<[^>]+>/g, '')
|
|
if (BACKING.test(m[1]) && FAMILY.test(words)) found.push({ rule: 'link', at: `${m[1]} → ${words.replace(/\s+/g, ' ').slice(0, 60)}` })
|
|
}
|
|
}
|
|
return found
|
|
}
|