File size: 2,477 Bytes
c126239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**
 * Model adapter registry. The app is not coupled to a specific model; any
 * Transformers.js-compatible ONNX decoder can be plugged in here.
 */

export interface ModelSpec {
  id: string;
  label: string;
  repo: string; // HF hub repo with ONNX weights
  dtype: 'q4f16' | 'q4' | 'int8' | 'fp16' | 'fp32';
  approxSizeMB: number;
  /** Extra options passed to tokenizer.apply_chat_template. */
  chatTemplateOptions?: Record<string, unknown>;
  notes?: string;
}

export const MODELS: ModelSpec[] = [
  {
    id: 'qwen3-0.6b',
    label: 'Qwen3-0.6B (q4f16, ~570MB)',
    repo: 'onnx-community/Qwen3-0.6B-ONNX',
    dtype: 'q4f16',
    approxSizeMB: 570,
    // Qwen3 has a thinking mode; disable it for this demo.
    chatTemplateOptions: { enable_thinking: false },
  },
  {
    id: 'qwen2.5-0.5b',
    label: 'Qwen2.5-0.5B-Instruct (q4f16, ~483MB)',
    repo: 'onnx-community/Qwen2.5-0.5B-Instruct',
    dtype: 'q4f16',
    approxSizeMB: 483,
  },
  {
    id: 'smollm2-360m',
    label: 'SmolLM2-360M-Instruct (q4f16, ~273MB)',
    repo: 'HuggingFaceTB/SmolLM2-360M-Instruct',
    dtype: 'q4f16',
    approxSizeMB: 273,
    notes: 'English-centric; lightest reasonable option.',
  },
];

export const DEFAULT_MODEL_ID = 'qwen3-0.6b';

/**
 * Sentence encoder for k-SemStamp. EmbeddingGemma is multilingual, so the
 * semantic clusters mean something in any language the demo model can write.
 *
 * Its ONNX graph already contains the pooling and the dense projection and
 * exposes `sentence_embedding` directly, so we call the model rather than the
 * feature-extraction pipeline - the pipeline would mean-pool the hidden states
 * and silently skip the projection, giving a different space.
 */
export const EMBEDDING_MODEL = {
  repo: 'onnx-community/embeddinggemma-300m-ONNX',
  dim: 768,
  approxSizeMB: 167,
  /**
   * EmbeddingGemma is trained with task prefixes. Clustering has its own, and
   * centroid computation and runtime assignment MUST use the same one or the
   * two embedding spaces do not line up.
   */
  clusteringPrefix: 'task: clustering | query: ',
  /**
   * Quantization is part of the embedding geometry, not just a size knob:
   * q4 and fp32 embeddings of one sentence sit at cosine ~0.98. The centroid
   * script pins the same value.
   */
  dtype: 'q4' as const,
};

export function getModelSpec(id: string): ModelSpec {
  const m = MODELS.find((m) => m.id === id);
  if (!m) throw new Error(`Unknown model id: ${id}`);
  return m;
}