Skip to content

refactor(phase-9): isolate Ray into services/workers, delete legacy indexer components - #419

Merged
EnjoyBacon7 merged 169 commits into
mainfrom
refactor/phase-9-worker-stages
May 26, 2026
Merged

refactor(phase-9): isolate Ray into services/workers, delete legacy indexer components#419
EnjoyBacon7 merged 169 commits into
mainfrom
refactor/phase-9-worker-stages

Conversation

@hedhoud

@hedhoud hedhoud commented May 20, 2026

Copy link
Copy Markdown
Collaborator

Context
Phase 9 needs the indexing flow split into explicit worker stages before the Ray actor can become a thin runtime wrapper. This PR starts that path while keeping the current production indexing path untouched.

Problem
The worker package had Ray helpers and parser workers, but no concrete stage functions for parse through store. That made the next pipeline-builder step impossible to review independently.

Expected behavior
The stage slice is testable as normal async Python. Each stage mutates a row consistently, marks failures, and removes credentials after use. The obsolete local copilot HTML harness is also removed.

Validation

  • .venv/bin/ruff check openrag/services/workers/stages
  • .venv/bin/pytest openrag/services/workers/stages/test_parse.py openrag/services/workers/stages/test_pipeline_stages.py

Summary by CodeRabbit

  • New Features

    • End-to-end indexing pipeline with configurable per-stage and per-item timeouts, optional captioning/contextualization, and a batch ingest API that continues on per-row failures.
    • Ray-backed task state management and a worker entrypoint for processing files and reporting task progress.
    • Result aggregation summary reporting totals, successes, failures, and stored counts.
    • Automatic scrubbing of credential/sensitive fields from processing payloads.
  • Tests

    • Extensive async/sync test coverage for stages, pipeline orchestration, batch ingest, aggregation, task worker, and failure paths.

Review Change Stack

EnjoyBacon7 and others added 30 commits April 29, 2026 14:39
Type-safe registry for pluggable components. Each domain (embedder,
reranker, llm, vlm, chunking, parser) will have its own Registry
instance. Implementations register via @registry.register("name")
decorator and are instantiated via registry.create("name", **kwargs).

Includes RegistryError with helpful message listing available
implementations when a lookup fails.
Consolidates all exception classes into core/utils/exceptions.py.
Preserves the existing OpenRAGError API (message, code, status_code,
to_dict()) and all existing VDB/Embedding subclasses for backward
compatibility.

Adds new exception categories for the hexagonal architecture:
- ConfigError, RegistryError, PipelineError
- AuthError, AuthenticationError (401)
- ValidationError (422), NotFoundError (404) with domain subtypes
- QuotaExceededError (429)
- ServiceUnavailableError (503), CircuitBreakerOpenError
- InferenceError with LLMParsingError (502), timeout (504), connection (503)
- StorageError with MilvusError, PostgresError

Status codes preserved from existing codebase for backward compat.
Will be moved to api/error_handlers.py mapping in Phase 10.
Updates utils/exceptions/{__init__,base,vectordb,embeddings}.py to
re-export from openrag.core.utils.exceptions. All existing consumer
imports continue to work unchanged.

New code should import from openrag.core.utils.exceptions directly.
These shims will be removed in Phase 12.
CatalogStore ABC will live in core/ports/catalog_store.py (alongside the
repository ABCs it composes), not in a separate core/catalog/ folder.
Decision logged with Phase 1 entries.
Pure text cleaning functions moved from
components/indexer/utils/text_sanitizer.py. No infrastructure imports —
only re and unicodedata.

Includes sanitize_text(), clean_markdown_table_spacing(), and
sanitize_extracted_text().
Pure filename functions extracted from components/indexer/utils/files.py.
Only the infrastructure-free parts: sanitize_filename() and
make_unique_filename(). The rest (save_file_to_disk, serialize_file)
stays in the old location until Phase 5+.
Pure utility for detecting when errors originate from external HTTP
resources (VLM image fetches, etc.) rather than internal failures.
Moved from utils/external_resource_errors.py.
The unit of indexable/retrievable text. Includes from_langchain()
and to_langchain() boundary converters for migration compatibility.
Imports are deferred in converter methods so core/ stays pure.
Document is the input to the indexing pipeline. ProcessedDocument is
the result after parsing (text blocks + images). Includes
DocumentType enum and from_langchain/to_langchain converters.
Domain user model with role enum (viewer/editor/owner), partition
memberships carried inline, and OIDC session model for cookie-based
auth. Derived from SQLAlchemy models in components/indexer/vectordb/.
Catalog models for tracking document lifecycle (QUEUED -> COMPLETED)
and batch indexation jobs. Derived from TaskStateManager + File table.
Query input model, per-chunk scored results (RetrievalResult,
ScoredChunk), and end-to-end retrieval output (RetrievalResponse).
Conversation + Message for chat history persistence.
ContextualizedQuery for LLM query rewriting (HyDE, multi-query).
Prompt + PromptType enum for template management.
Consumers can now import cleanly:
  from openrag.core.models import Chunk, Document, User, RetrievalQuery
Frozen Pydantic model with dict-like backward compatibility.
Existing code using config.section.get(), config.section["key"],
dict(config.section), and **config.section keeps working.
endpoints.py — LLMConfig, VLMConfig, EmbedderConfig, SemaphoreConfig,
LLMContextConfig (all model endpoint settings in one file).
chunking.py — ChunkerConfig (strategy name, chunk size, overlap, etc.).
Discriminated unions for reranker (infinity/openai) and retriever
(single/multiQuery/hyde). Plus RAGConfig, MapReduceConfig,
and WebSearchConfig (staan provider).
LoaderConfig with all nested configs: FileLoadersConfig, MimetypesConfig,
TranscriberConfig, OpenAILoaderConfig, LocalWhisperConfig, plus
Marker and Docling settings.
infrastructure.py — VectorDBConfig, RDBConfig, RayConfig (with indexer
concurrency groups, serve, semaphore), PathsConfig, ServerConfig,
VerboseConfig, PromptsConfig.
auth.py — OIDCConfig for Keycloak/external IdP integration.
root.py — Settings class composing all sub-models.
loader.py — load_config() with YAML defaults + env var overrides +
Pydantic validation. Exact same logic as config/loader.py.

The old config/ package will be updated to re-export from here
for backward compatibility.
Updates config/__init__.py to import Settings and load_config from
openrag.core.config. All existing consumer imports (from config import
load_config) continue to work unchanged.
Async-native embedder interface: embed(), embed_single(), dimension property.
Registry for pluggable implementations via @embedder_registry.register().
Async reranker interface: rerank(query, documents, top_k) returning
(original_index, score) pairs sorted by relevance.
@hedhoud hedhoud changed the title refactor(workers): add Phase 9 indexing stages refactor(phase-9): isolate Ray into services/workers, delete legacy indexer components May 23, 2026
@hedhoud
hedhoud changed the base branch from refactor/hexagonal to main May 23, 2026 10:10
The dispatcher is the production replacement for the old
indexer_ray_shim — it routes new indexing jobs to IndexerPool via
.remote() and bookkeeps task state on the TaskStateManager actor. That
makes services/workers/ its natural home; living under services/storage/
violated the "Ray only in services/workers/ + startup" rule from the
phase-9 plan.

No behavioural change: same class, same factory, only the import path
changes. di.container and the dispatcher test suite are updated to the
new location.
components/indexer/loaders/audio/openai.py was the last non-worker
loader making raw .remote() calls — it grabbed the singleton
WhisperActor and invoked detect_language for the language-detection
hook the OpenAI audio client uses when use_whisper_lang_detector=true.

Move the actor lookup + .remote() call into a new helper
``detect_language_via_actor`` in services/workers/parsers/whisper_workers.py.
The loader now imports the helper and wraps it in the existing
language_detector callable, so the components/ side is fully Ray-free.
utils/dependencies.py was the last non-worker, non-startup module
holding ``import ray`` + module-level ``ray.get_actor`` / .remote()
calls. The bootstrap belongs under services/workers/ alongside the
worker actors it creates.

- New: services/workers/bootstrap.py — same actor-creation helpers and
  module-level bootstrap calls, no behaviour change.
- Deleted: utils/dependencies.py (only consumers were routers/actors.py
  and di/container.py; both updated to the new path).
- main.py now explicitly imports services.workers.bootstrap so the
  worker actors come up at startup without relying on a transitive
  import through routers/actors.
- Test stubs (test_auth_router, test_utils_partition_access) re-pointed
  at the new module name; the stubs no longer fake the whole ``services``
  package, which had been masking the real services.auth subpackage.
- Loader-shim docstrings updated to cite the new path.

9F verification now shows zero Ray imports outside services/workers/,
main.py, and the two explicit carve-outs (distributed_semaphore,
routers/actors).
The auth_service.py imports ``mask_email`` from ``utils.logger`` (added
for PII masking in OIDC logs); the OIDC lifecycle test stubs
``utils.logger`` in ``sys.modules`` but only exposed ``get_logger`` and
``escape_markup``, so collecting the test crashed at import time. Mirror
the same helper that the other router-test stubs already define.
hedhoud added a commit that referenced this pull request May 26, 2026
@EnjoyBacon7
EnjoyBacon7 merged commit 67faa64 into main May 26, 2026
9 of 10 checks passed
@EnjoyBacon7
EnjoyBacon7 deleted the refactor/phase-9-worker-stages branch May 26, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants