This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
uv sync # install/update dependencies
uv run python -m rag_indexer # run the consumer
make test # unit tests only
make test-all # unit + integration (needs Docker for RabbitMQ)
make lint # ruff linter
uv run pytest tests/unit/test_processing.py -v # run a single test file
uv run pytest tests/unit/ -v -k "test_name" # run a single test by nameIntegration tests use pytest-docker to spin up RabbitMQ automatically. E2E tests live in tests/e2e/ and use a stateful RAG stub.
Async RabbitMQ consumer that indexes documents into a RAG API with retry and dead-letter handling.
Producer -> RabbitMQ (topic exchange) -> rag-indexer -> RAG API
^ |
| TTL delay queues | on transient error
+-------- retry <------------+
| on fatal / exhausted
+-> DLQ
Message flow through the code:
main.py— Entry point. Connects to RabbitMQ, starts health server, consumes messages with concurrency control viaasyncio.Semaphore. Routes errors to retry queues or DLQ.processing.py— Core business logic. Parses message headers intoIndexMessage, handles upsert (GET-then-PUT with version check) and delete paths. Classifies errors asTransientError(retryable) orFatalError(goes to DLQ).rag_client.py— HTTP client for the RAG API (GET file, DELETE, UPSERT with multipart form).transport.py— RabbitMQ topology declaration (main queue, retry queues, DLQ), publish helpers, health/metrics HTTP server, shutdown coordination.config.py— All configuration from env vars. Retry queue names are generated from TTL intervals (e.g.,rag.index.retry.30s.q).models.py— Pydantic models:IndexMessage,RagConn,ContentSpec.
Key design decisions:
- Messages carry metadata in AMQP headers and file binary in the body (not JSON-encoded).
- RAG connection info (
rag_base_url,rag_api_key) comes per-message via headers, not from global config. - Retry count is derived from
x-deathheader entries withreason=expired, not a custom counter. - Upsert is idempotent: skips indexing if remote version matches local
version/md5sum. - Quorum queues for main queue and DLQ; classic queues for TTL retry delays.
TransientError— HTTP 429, 5xx, timeouts, network errors -> retry queueFatalError— HTTP 4xx (except 429), unknown actions, unexpected errors -> DLQValidationError(Pydantic) -> DLQ directly