You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(embedder)!: swap embedding model gte-modernbert-base → F2LLM-v2-80M (320-dim)
BREAKING CHANGE: the local ONNX embedder is now codefuse-ai/F2LLM-v2-80M
(320-dim, was gte-modernbert-base 768-dim). Existing indexes MUST be
rebuilt with `codehub analyze --embeddings` — 320-dim query vectors cannot
be compared against stored 768-dim vectors. The embedder-fingerprint guard
(ADR 0014) refuses queries against a stale store until re-analyze.
What changed:
- onnx-embedder.ts requests the in-graph `embedding` output (shape [B,320],
last-token pooling + L2 norm baked into the graph) instead of pooling /
normalizing in JS — clsPool + l2NormalizeInPlace are removed. Qwen2 pad id.
- New Embedder.embedQuery() applies F2LLM's query-only `Instruct:` prefix;
documents embed raw. Wired at the search/hybrid.ts query seam. New
query-prefix.ts holds the instruction string.
- Dimension parameterized 768→320 across embedder, search (NullEmbedder),
storage (DEFAULT_DIM), ingestion pool, and HTTP/SageMaker defaults.
- model-pins.ts: GTE_MODERNBERT_BASE_PINS → F2LLM_V2_80M_PINS; weights are a
custom ONNX export hosted as the GitHub release asset `embed-v1`,
SHA256-pinned. 3-file manifest (model + tokenizer.json + tokenizer_config.json).
- Migration guard: analyze suppresses the content-hash cache on a model-id
change so a swap forces a full re-embed (no mixed-dimension store).
- Docs, CHANGELOGs, skills swept to F2LLM/320-dim. Collapsed the stale
storage ADR chain (0001/0011/0013/0016 → 0019) and fixed the README's
pre-ADR-0019 storage narrative. Fixed lefthook verdict guard to check
store.sqlite (was the removed graph.lbug).
Verified: pnpm -r build + test green (~2400 tests); tokenizer parity with
Python AutoTokenizer (byte-identical IDs); production embedder reproduces the
POC 4/4 top-1 ranking + byte-deterministic output; end-to-end analyze writes
320-dim rows; migration guard confirmed firing on a stale stamp.
Copy file name to clipboardExpand all lines: .claude/skills/opencodehub-guide/SKILL.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,7 +15,7 @@ For any task that touches code understanding, debugging, impact analysis, refact
15
15
2. Read `codehub://repo/{name}/context` — codebase stats and a staleness envelope.
16
16
3. Match the task to a skill below and follow that skill's checklist.
17
17
18
-
> If the context envelope reports the index is stale, run `codehub analyze` in the terminal first. If it says weights are missing, run `codehub setup --embeddings` to fetch the 768d gte-modernbert-base ONNX weights.
18
+
> If the context envelope reports the index is stale, run `codehub analyze` in the terminal first. If it says weights are missing, run `codehub setup --embeddings` to fetch the 320d F2LLM-v2-80M ONNX weights.
***embedder:** local embedding model swapped to `codefuse-ai/F2LLM-v2-80M` (320-dim, was gte-modernbert-base 768-dim). Existing stores must be rebuilt with `codehub analyze --embeddings`; queries against a stale-dim store are refused by the fingerprint guard.
Copy file name to clipboardExpand all lines: README.md
+53-49Lines changed: 53 additions & 49 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -78,31 +78,31 @@ flowchart LR
78
78
|**Local-first, offline-capable**|`codehub analyze --offline` opens zero sockets. Your code never leaves your machine. No telemetry. |
79
79
|**Deterministic indexing**| Identical inputs produce a byte-identical graph hash. Reproducible. Auditable. Cacheable in CI. |
80
80
|**MCP-native**| Works out-of-the-box with Claude Code, Cursor, Codex, Windsurf, OpenCode. The MCP server is the primary interface; CLI exists for scripts and CI. |
81
-
|**Embedded storage, two-tier**|`@ladybugdb/core`holds the structural store: symbols, edges, embeddings, BM25 + HNSW. A dedicated DuckDB sibling holds the temporal views: cochanges and summaries. Embedded files. No daemon. No database to operate. Both tiers are always present, with no backend knob (ADR 0016). |
81
+
|**Single-file embedded storage**|One `store.sqlite` file holds everything — symbols, edges, embeddings, BM25 (FTS5) + HNSW traversal, and the temporal views (cochanges, summaries) — via Node's built-in `node:sqlite`. No daemon, no database to operate, and **zero native storage bindings** (ADR 0019 removed both `@ladybugdb/core` and `@duckdb/node-api`). |
82
82
|**15 languages at GA**| TypeScript, JavaScript, Python, Go, Rust, Java, C#, C, C++, Ruby, Kotlin, Swift, PHP, Dart, COBOL — tree-sitter for the first 14 plus a regex provider for fixed-format COBOL. |
83
-
|**WASM-only parse runtime**|`web-tree-sitter` WASM is the only parse runtime. The 15 grammar `.wasm` blobs are vendored at `packages/ingestion/vendor/wasms/`, so parsing does **zero grammar/native builds and zero GitHub fetches** at install time — there is no native parser opt-in. Storage and embeddings still load prebuilt native bindings (see Platform support). |
83
+
|**WASM-only parse runtime**|`web-tree-sitter` WASM is the only parse runtime. The 15 grammar `.wasm` blobs are vendored at `packages/ingestion/vendor/wasms/`, so parsing does **zero grammar/native builds and zero GitHub fetches** at install time — there is no native parser opt-in. Storage is pure `node:sqlite`; the only optional native dep is the local embedder (see Platform support). |
84
84
85
85
## Platform support
86
86
87
-
Parsing is WASM and runs anywhere Node does. The storage and embedding
88
-
tiers, however, depend on **prebuilt native bindings** — `@ladybugdb/core`
89
-
(graph store), `@duckdb/node-api` (temporal store), and `onnxruntime-node`
90
-
(local embeddings) — so OpenCodeHub runs on the platforms those bindings
91
-
ship a prebuild for:
87
+
Parsing is WASM and storage is pure `node:sqlite`, so the core runs anywhere
88
+
Node ≥ 24.15 does — no prebuilt native storage bindings, no Docker, no
89
+
postinstall compile (ADR 0019). There is exactly **one** optional native
90
+
dependency: `onnxruntime-web`, the WASM ONNX runtime that powers
91
+
`--embeddings`. It ships prebuilt WebAssembly (no node-gyp, no native
92
+
binding) and runs single-threaded under Node, so it too is platform-agnostic;
|`storage`|One `SqliteStore` (`node:sqlite`) implementing both `IGraphStore`+ `ITemporalStore`over a single `store.sqlite`; deterministic `graphHash`|
191
191
|`summarizer`| Process + cluster summaries for MCP responses |
192
192
|`wiki`| LLM-narrated module pages emitted by `codehub wiki --llm`|
193
193
@@ -199,63 +199,67 @@ production package set ships free of test-time dependencies.
199
199
## Embedding backends
200
200
201
201
OpenCodeHub ships with three embedding backends — all serve the same
202
-
`gte-modernbert-base` 768-dim space, all use CLS pooling + L2 norm — and
203
-
picks one at runtime based on environment variables:
202
+
`codefuse-ai/F2LLM-v2-80M` 320-dim space (last-token pooling + L2 norm
203
+
baked into the ONNX graph) — and picks one at runtime based on
204
+
environment variables:
204
205
205
206
| Precedence | Env | Backend |
206
207
|---|---|---|
207
-
| 1 |`CODEHUB_EMBEDDING_SAGEMAKER_ENDPOINT`|**SageMaker** — invokes an AWS SageMaker Runtime endpoint (e.g. a TEI-served `gte-modernbert-embed`). Auth via the default AWS credential chain (profile, env vars, IMDS). No local weights needed. |
208
+
| 1 |`CODEHUB_EMBEDDING_SAGEMAKER_ENDPOINT`|**SageMaker** — invokes an AWS SageMaker Runtime endpoint (e.g. a TEI-served `F2LLM-v2-80M`). Auth via the default AWS credential chain (profile, env vars, IMDS). No local weights needed. |
208
209
| 2 |`CODEHUB_EMBEDDING_URL` + `CODEHUB_EMBEDDING_MODEL`|**HTTP (OpenAI-compatible)** — POSTs to a `/v1/embeddings` server (Infinity, vLLM, TEI, Ollama, LM Studio, OpenAI). Bearer auth optional via `CODEHUB_EMBEDDING_API_KEY`. |
209
210
| 3 |*(nothing set)*|**Local ONNX** — deterministic, offline-safe. Requires `codehub setup --embeddings` to download the weights. |
210
211
211
212
**SageMaker-specific vars**:
212
213
213
214
| Var | Default | Purpose |
214
215
|---|---|---|
215
-
|`CODEHUB_EMBEDDING_SAGEMAKER_ENDPOINT`|*(required to select)*| Endpoint name (e.g. `gte-modernbert-embed`). |
216
+
|`CODEHUB_EMBEDDING_SAGEMAKER_ENDPOINT`|*(required to select)*| Endpoint name (e.g. `F2LLM-v2-80M`). |
|`CODEHUB_EMBEDDING_DIMS`|`768`| Expected vector dimension — asserted on every response to catch model-swap drift. |
218
-
|`CODEHUB_EMBEDDING_MODEL`|`gte-modernbert-base/sagemaker:<endpoint-name>`| Stable modelId stamp recorded in index metadata. Override only when bridging a non-gte endpoint. |
218
+
|`CODEHUB_EMBEDDING_DIMS`|`320`| Expected vector dimension — asserted on every response to catch model-swap drift. |
219
+
|`CODEHUB_EMBEDDING_MODEL`|`F2LLM-v2-80M/sagemaker:<endpoint-name>`| Stable modelId stamp recorded in index metadata. Override only when bridging a non-F2LLM endpoint. |
219
220
220
221
IAM: the caller needs `sagemaker:InvokeEndpoint` on the endpoint ARN —
221
-
e.g. `arn:aws:sagemaker:us-east-1:<account>:endpoint/gte-modernbert-embed`.
222
+
e.g. `arn:aws:sagemaker:us-east-1:<account>:endpoint/F2LLM-v2-80M`.
222
223
223
224
**Do not mix backends against the same index.** Backends are pinned to a
224
225
single model identity via the `modelId` stamp in the `embeddings` table;
> Note (2026-06-26): the embedder-fingerprint mechanism this ADR introduced
9
+
> — persist `embedder_model_id`, refuse mismatched queries via
10
+
> `assertEmbedderCompatible` — is unchanged and is precisely what guards the
11
+
> later embedding-model swap from `gte-modernbert-base` (768-dim) to
12
+
> `F2LLM-v2-80M` (320-dim). The `gte-modernbert-base` / `768` references
13
+
> below are the contemporaneous examples; the dim/model are now 320 /
14
+
> `f2llm-v2-80m/*` but the decision and the comparator are identical. The
15
+
> `store_meta` storage substrate referenced here (DuckDB) was later replaced
16
+
> per [ADR 0019](./0019-single-file-sqlite-storage.md); the column and
17
+
> semantics carried over to `store.sqlite` verbatim.
18
+
8
19
## Context
9
20
10
21
Two unrelated holes in v1.0 finalize, both routing through a shared one-time graphHash content delta. They land in a single ADR per spec.md§Q7 because the fixture-regeneration cost is paid once.
0 commit comments