Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,20 @@ All notable changes to MeMesh are documented here.

### Removed

- **`embedder.model` is removed from the config.** It was settable
(`memesh config set embedder.model …`), documented in three READMEs, and it
never reached the embedding call — `embedText` built its provider config with
`model: undefined`, so the only models ever used were each provider's default.
It is removed rather than wired through, because honouring it would have
introduced the exact fault the rest of this release closes: a vector index is
fixed at one width and the width is resolved from the *provider*, so a model of
a different width could never be rebuilt against, and one of the *same* width
would have put vectors from a second embedding space into the index with no
width signal to catch it. Each provider pins its own model and dimension
(`ollama` → nomic-embed-text at 768, `openai` → text-embedding-3-small at
1536). Setting the key now reports an unknown key instead of printing "✅ Set"
and doing nothing.

- **`memesh reindex --vectors` is removed. Scripts that pass it will fail**
with `error: unknown option '--vectors'` and exit 1. The flag existed only to
grant consent for dropping every stored embedding before the refill began,
Expand All @@ -158,6 +172,53 @@ All notable changes to MeMesh are documented here.

### Fixed

- **A resumed rebuild no longer promotes a vector for text that has since
changed.** It skipped an entity whenever a row for it was already staged, on
presence alone — so an entity edited between an interrupted run and its resume
kept the vector built from its old text, and nothing downstream could detect it
because the row *was* there (the missing-vector count only asks whether a row
exists). Each staged row now records a hash of what it was embedded from, and a
resume reuses it only while that still matches. An unchanged entity is still
never bought twice, and `already_staged` is now its own count instead of being
folded into `stored` — a resumed run used to report "900/900 entities embedded"
after issuing one request.
- **A half-built index whose marker cannot be read is no longer thrown away
silently.** "The marker is unreadable" and "there is no rebuild in progress"
were the same answer, and the caller treats the second as licence to delete —
so a marker that failed to parse discarded every embedding a previous run had
produced. Neither choice is safe to make silently (resuming could merge two
embedding spaces; discarding destroys work), so it now refuses, says which it
is, and points at `memesh reindex --discard-generation`.
- **A rebuild against a broken provider stops instead of grinding through the
whole graph.** There was no circuit breaker: a provider that stopped answering
at entity 50 of 20,000 was still asked about the remaining 19,950 — up to ~91.5
seconds each — printing one identical failure per entity, even for a 401, where
the code's own reasoning is that retrying "spends the rate budget on a
certainty". Five consecutive failures now end the run; everything already
embedded is kept and the next run resumes. Provider backoff is also exponential
rather than linear, so a rate limit is backed away from instead of re-arrived
at.
- **A provider that sends headers and then stalls the body is now retried.** The
response was parsed by the caller, outside the retry, so an abort during the
body read arrived as an indistinguishable `null` with no attempt counter and no
message naming it a timeout — which is precisely the failure the 30-second
budget was added to catch.
- **A whitespace-only memory no longer holds the reindex flag open forever.**
SQLite's `TRIM` strips spaces only while JavaScript's `.trim()` also strips tabs
and newlines, so the rebuild loop and the database disagreed about the same
entity: permanently "nothing to embed" to one and permanently owed a vector to
the other. Every full reindex reported "1 active memory still has no vector"
and could never resolve it.
- **`memesh reindex` no longer prints a tick when the new index was refused.**
The verdict was built from a count taken against whatever index is *live*, so
when a rebuild was withheld that was the old, complete-by-construction index —
the run exited 0 while its own output said the new index was not switched in.
The result now carries `generationSwapped` and `abortedAfter`, and `--json`
publishes both.
- **`memesh doctor` reports a half-built index.** An interrupted rebuild leaves a
full second copy of the vectors on disk; nothing reclaimed it and no diagnostic
mentioned it, so it could sit there indefinitely. Doctor now shows its size,
width, provider and age, with the two ways out.
- **A rebuild no longer discards a memory captured while it ran.** The swap
installed exactly the staging index, and every writer other than the rebuild
itself — the seven capture hooks, `remember`, the dreamer, the MCP server —
Expand Down
5 changes: 3 additions & 2 deletions README.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,10 +447,11 @@ Standardmäßig macht MeMesh reines Keyword-Recall (FTS5) — kein API-Schlüsse

```bash
memesh config set embedder.provider openai # or: ollama
memesh config set embedder.model text-embedding-3-small
```

Der Embedder wird **unabhängig vom Chat-LLM** konfiguriert — `llm.provider` zu ändern ändert nie stillschweigend deine Embeddings. Wechselst du zu einer anderen Dimension (z. B. 768 → 1536), baut MeMesh den Vektorindex beim nächsten Schreibvorgang automatisch neu auf. Unterstützte `embedder.provider`-Werte: `ollama` (lokal), `openai` (gehostet). Ohne Einstellung bleibt das Recall bei der Keyword-Suche.
Der Embedder wird **unabhängig vom Chat-LLM** konfiguriert — `llm.provider` zu ändern ändert nie stillschweigend deine Embeddings. Jeder Anbieter legt sein Modell und seine Dimension selbst fest (`ollama` → nomic-embed-text mit 768, `openai` → text-embedding-3-small mit 1536); das Modell ist nicht separat wählbar, weil ein Vektorindex auf eine Dimension festgelegt ist und ein zweites Modell Vektoren aus einem anderen Embedding-Raum hineinschreiben würde.

Wechselst du zu einer anderen Dimension (z. B. 768 → 1536), wird **nichts gelöscht**. MeMesh behält den bestehenden Index und weist beim Öffnen darauf hin, `memesh reindex` auszuführen: der neue Index wird neben dem alten aufgebaut und erst übernommen, wenn jede Erinnerung einen Vektor hat — ein abgebrochener Neuaufbau kostet dich also nichts und wird an der Abbruchstelle fortgesetzt. In diesem Zeitraum ist die semantische Suche aus und das Recall läuft nur über die Keyword-Suche; `recall` meldet das als `degraded`, statt eine Suche vorzugeben. Unterstützte `embedder.provider`-Werte: `ollama` (lokal), `openai` (gehostet). Ohne Einstellung bleibt das Recall bei der Keyword-Suche.

| | Stufe 0 (Standard) | Stufe 1 (Smart Mode) |
|---|---|---|
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,13 +512,13 @@ By default MeMesh does **keyword-only** recall (FTS5) — no API key, no model d

```bash
memesh config set embedder.provider ollama # local, needs `ollama serve`
memesh config set embedder.model nomic-embed-text
# or, for a hosted embedder:
memesh config set embedder.provider openai
memesh config set embedder.model text-embedding-3-small
```

The embedder is configured **independently of the chat LLM** — changing `llm.provider` never silently changes your embeddings. If you switch to an embedder with a different dimension (e.g. 768 → 1536), MeMesh rebuilds the vector index automatically on the next write. Supported `embedder.provider` values: `ollama` (local), `openai` (hosted). With none set, recall stays on keyword search.
The embedder is configured **independently of the chat LLM** — changing `llm.provider` never silently changes your embeddings. Each provider pins its own model and width (`ollama` → nomic-embed-text at 768, `openai` → text-embedding-3-small at 1536); the model is not separately selectable, because a vector index is fixed at one width and a second model would put vectors from a different embedding space into it.

If you switch to an embedder with a different dimension (e.g. 768 → 1536), **nothing is deleted**. MeMesh keeps the existing index and tells you on open to run `memesh reindex`, which builds the new index beside the old one and switches over only once every memory has a vector — so an interrupted rebuild costs you nothing and resumes where it stopped. During that window semantic search is off and recall runs on keyword search alone; `recall` reports this as `degraded` rather than implying it searched. Supported `embedder.provider` values: `ollama` (local), `openai` (hosted). With none set, recall stays on keyword search.

| | Level 0 (default) | Level 1 (Smart Mode) |
|---|---|---|
Expand Down
5 changes: 3 additions & 2 deletions README.zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,10 +456,11 @@ memesh serve # 開啟儀表板 → Settings 分頁

```bash
memesh config set embedder.provider openai # or: ollama
memesh config set embedder.model text-embedding-3-small
```

嵌入器**獨立於對話 LLM** 設定 —— 更改 `llm.provider` 絕不會悄悄改變你的嵌入。如果切換到不同維度(如 768 → 1536),MeMesh 會在下次寫入時自動重建向量索引。支援的 `embedder.provider` 取值:`ollama`(本地)、`openai`(託管)。兩者都不設定時,召回保持關鍵字搜尋。
嵌入器**獨立於對話 LLM** 設定 —— 更改 `llm.provider` 絕不會悄悄改變你的嵌入。每個 provider 自己固定模型與維度(`ollama` → nomic-embed-text 768 維、`openai` → text-embedding-3-small 1536 維);模型不另外提供選項,因為一個向量索引的維度是固定的,換第二個模型會把另一個嵌入空間的向量寫進同一個索引。

如果切換到不同維度(如 768 → 1536),**不會刪掉任何東西**。MeMesh 保留現有索引,並在開啟時提示你執行 `memesh reindex`:新索引會建在舊索引旁邊,等到每一筆記憶都有向量才切換過去 —— 所以重建中途被打斷不會損失任何東西,下次會從斷點繼續。這段期間語意搜尋是關閉的,召回只走關鍵字搜尋;`recall` 會回報 `degraded`,不會假裝搜過了。支援的 `embedder.provider` 取值:`ollama`(本地)、`openai`(託管)。兩者都不設定時,召回保持關鍵字搜尋。

| | 等級 0(預設) | 等級 1(智慧模式) |
|---|---|---|
Expand Down
10 changes: 5 additions & 5 deletions dashboard/dist/index.html

Large diffs are not rendered by default.

Loading
Loading