phase: 02-operations-maintenance
plan: "04"
subsystem: ollama-client
tags: [resilience, retry, error-handling, ollama, embedding]
dependency_graph:
requires: []
provides: [ollama-retry-logic]
affects: [query.py, ingest.py, api.py]
tech_stack:
added: []
patterns: [retry-loop, custom-exception, german-user-messages]
key_files:
created: []
modified:
- query.py
- ingest.py
decisions:
- "OLLAMA_TIMEOUT reduced to 30s (from 60s): fail faster, better UX than waiting a full minute"
- "OllamaError as separate exception class: allows api.py and callers to distinguish Ollama failures from other errors"
- "Separate copies in query.py and ingest.py (not shared module): both scripts run standalone, shared module would require import restructuring"
metrics:
duration_seconds: 51
completed_date: "2026-03-24"
tasks_completed: 2
tasks_total: 2
files_changed: 2
requirements_addressed: [QRY-04]


Phase 02 Plan 04: Ollama Timeout and Retry Logic Summary

Retry loop with German error messages for all Ollama embedding HTTP calls in query.py and ingest.py, replacing the silent 60s raw-500 failure path.

What Was Built

Both query.py and ingest.py now have a hardened get_embedding() function that:
- Attempts the Ollama HTTP call up to 2 times (configurable via OLLAMA_MAX_RETRIES)
- Waits 2 seconds between attempts (OLLAMA_RETRY_DELAY)
- Uses a 30-second per-attempt timeout (OLLAMA_TIMEOUT, down from 60s)
- Catches httpx.TimeoutException, httpx.ConnectError, httpx.HTTPStatusError separately
- Returns a clear German error message for each case (e.g., "Ollama antwortet nicht (Timeout nach 30s, Versuch 1/2)")
- Raises OllamaError after all retries exhausted — propagates to api.py as HTTPException detail string

Tasks Completed

Task Description Commit Files
1 Add timeout + retry to query.py get_embedding af7d167 query.py
2 Add timeout + retry to ingest.py get_embedding 830b29e ingest.py

Decisions Made

  1. OLLAMA_TIMEOUT = 30s (was 60s): Fail faster. 30s is already generous for a local Ollama instance. The retry adds resilience without doubling the worst-case wait.
  2. OllamaError as custom exception: Allows api.py (and any future caller) to distinguish Ollama failures from DB errors or Claude API errors.
  3. Duplicate copies, not shared module: query.py and ingest.py are standalone scripts. Introducing a shared module would require packaging restructuring — deferred per plan note.

Deviations from Plan

None - plan executed exactly as written.

Known Stubs

None.

Self-Check: PASSED

  • query.py exists and contains OllamaError, OLLAMA_TIMEOUT, OLLAMA_MAX_RETRIES, TimeoutException, ConnectError
  • ingest.py exists and contains OllamaError, OLLAMA_TIMEOUT, OLLAMA_MAX_RETRIES, TimeoutException, ConnectError
  • Commits af7d167 and 830b29e exist in git log