Skip to content

fix(vectorstore): stop retrying Ollama 4xx and size chunks to the server's num_ctx - #487

Merged
Leo310 merged 3 commits into
mainfrom
fix/ollama-embed-context-retries
Sep 13, 2026
Merged

Leo310 merged 3 commits into
mainfrom
fix/ollama-embed-context-retries

Conversation

@Leo310

@Leo310 Leo310 commented Sep 13, 2026

Copy link
Copy Markdown
Member

Fixes #485.

What was wrong

Ollama embedding runs stalled 10–30 minutes per file with the GPU idle. Two independent causes:

  1. HTTP 400 retried six times. LangChain's default retry handler skips 4xx, but it reads the status from status / statusCode / response.status. The Ollama client's ResponseError exposes it as status_code, so "the input length exceeds the context length" was treated as transient. Reproduced against a mock server that always answers 400: 7 requests, 84.5 s for one embed call. The bulk indexer's per-entry fallback then repeats that for every chunk in the failed batch, which is where the multi-minute stalls come from.
  2. Chunks sized to the wrong limit. The budget came from /api/show's architecture context (40960 tokens for qwen3-embedding) × 4 chars/token, but Ollama's server enforces min(num_ctx, model context), and its num_ctx default is VRAM-chosen (4k+) and not readable from the client. Every long note overshot by 5×.

What changed

  • createTransportedOllamaEmbeddings installs an onFailedAttempt that rethrows on 4xx status_code and on cancellation, leaving the default retry budget for transport failures and 5xx.
  • The Ollama template pins num_ctx on embed requests to OLLAMA_EMBED_NUM_CTX (8192), and hydrateEmbeddingModel caps Ollama input budgets to the same number, so client and server agree on the limit.
  • truncate: true on Ollama embeddings as a backstop: a truncated vector still finds the note by its opening, a rejected one drops it from the index.
  • CHARS_PER_TOKEN 4 → 3 so dense content (JSON, code, tables, non-Latin) stops overshooting in general. Affects chunk sizing only on (re)index; existing indexes are untouched.

Tests

  • test/providers/ollamaEmbeddings.test.ts: handler rules, and an end-to-end mock-400 that asserts exactly one request (fails by timeout before the fix), plus the template's num_ctx/truncate wiring.
  • test/lib/modelMetadataNormalizer.test.ts: the Ollama cap, smaller contexts left alone, other providers uncapped.

bun run check, format, lint, test (1682 passing) and a dev build are clean.

…ver's num_ctx

Ollama embedding runs stalled for 10–30 minutes per file on large vaults
with the GPU idle (#485). Two causes:

1. A permanent HTTP 400 ("the input length exceeds the context length")
   was retried six times with exponential backoff. LangChain's default
   handler skips retries for 4xx, but reads the status from `status` /
   `statusCode` / `response.status`, while the Ollama client's
   `ResponseError` carries `status_code`. Measured: 7 requests, ~85 s per
   call — and the bulk indexer then repeats that per entry of the failed
   batch. `OllamaEmbeddings` now gets an `onFailedAttempt` that rethrows
   on 4xx (and cancellations), keeping the default budget for the rest.

2. Chunks were sized to the architecture context `/api/show` reports
   (40960 for qwen3-embedding) × 4 chars/token, but Ollama enforces
   min(num_ctx, model context) and its num_ctx default is a VRAM-chosen
   4k+. Every long note overshot by 5×+. The embedding client now pins
   `num_ctx` to OLLAMA_EMBED_NUM_CTX (8192) and `hydrateEmbeddingModel`
   caps Ollama budgets to the same number, so client and server agree.
   `truncate: true` backstops dense content, and CHARS_PER_TOKEN drops
   4 → 3 so JSON/code/non-Latin chunks stop overshooting in general.

Fixes #485
@greptile-apps

greptile-apps Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR aligns Ollama embedding chunk budgets with the num_ctx sent to the server and prevents permanent 4xx responses or cancellations from being retried. It also makes bulk indexing preserve chunk-0 completion semantics when any chunk from a note fails.

  • Pins Ollama embedding requests and metadata budgets to an 8192-token context.
  • Uses a more conservative character-to-token estimate and enables truncation as a fallback.
  • Stops retries for Ollama 4xx responses and canceled requests while retaining retries for transient failures.
  • Prevents failed notes from receiving the chunk-0 completion marker.
  • Adds regression coverage for request behavior, metadata normalization, and Ollama configuration.

Confidence Score: 5/5

The PR appears safe to merge; no outstanding correctness, security, or repository-rule issues remain.

The prior partial-index concern was manually resolved after the current failure tracking preserved chunk-0 as a completion marker, and the present code consistently aligns Ollama request limits, chunk sizing, retry behavior, and recovery semantics.

Important Files Changed

Filename Overview
src/providers/chatProviders.ts Adds an Ollama-specific failed-attempt handler that immediately stops retries for permanent client errors and cancellation.
src/providers/ollama.ts Pins embedding request context and enables truncation as a final safeguard against oversized inputs.
src/lib/modelMetadataNormalizer.ts Caps Ollama's advertised embedding input budget to the context configured on requests.
src/providers/ollamaModels.ts Defines the shared Ollama embedding context constant used by request construction and chunk budgeting.
src/vectorstore/VectorStoreService.ts Uses more conservative chunk sizing and prevents a failed note from receiving its chunk-0 completion marker.
test/providers/ollamaEmbeddings.test.ts Covers retry classification, single-request behavior for HTTP 400, and Ollama request options.
test/lib/modelMetadataNormalizer.test.ts Covers Ollama context capping, smaller model contexts, defaults, and unaffected non-Ollama providers.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Read Ollama model metadata] --> B[Cap input budget at 8192 tokens]
  B --> C[Split note using conservative token estimate]
  C --> D[Send embedding request with num_ctx 8192]
  D --> E{Response}
  E -->|Success| F[Write chunks with chunk 0 last]
  E -->|4xx or cancellation| G[Stop retrying]
  E -->|Transport failure or 5xx| H[Use retry budget]
  G --> I[Mark note failed and suppress remaining chunks]
  I --> J[Leave chunk 0 absent for later reindex]
Loading

Reviews (3): Last reviewed commit: "Merge branch 'main' into fix/ollama-embe..." | Re-trigger Greptile

Comment thread src/lib/modelMetadataNormalizer.ts
Leo310 and others added 2 commits September 13, 2026 15:43
…ailed

The bulk fallback kept embedding a note's remaining chunks after one
failed, so chunk 0 — the 'note is indexed' marker written last — could
still land and startup validation then read the partial note as
complete. Drop the note's remaining chunks instead; without #0 the next
validation pass re-indexes it whole.
@Leo310
Leo310 merged commit 87e595e into main Sep 13, 2026
3 checks passed
@Leo310
Leo310 deleted the fix/ollama-embed-context-retries branch September 13, 2026 15:07
Leo310 added a commit that referenced this pull request Sep 14, 2026
Stability release for the Obsidian 1.13 installer (Electron 43 / Node 24):
the renderer crash on the first chat send (#492) and the transport
connection error (#501), plus the streaming render coalescing (#484), the
checkpoint callbacks strip (#483), and the indexing fixes since 2.1.0
(#486, #487, #490, #491, #493, #495, #497, #498, #499, #500).

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Search embedding indexing is too slow/stuck + context limit errors

1 participant