Skip to content

refactor: migrate to httpx and remove RAGAnything/LightRAG pipeline#52

Merged
Kaiohz merged 11 commits into
mainfrom
refactor/httpx-remove-raganything
Jul 8, 2026
Merged

refactor: migrate to httpx and remove RAGAnything/LightRAG pipeline#52
Kaiohz merged 11 commits into
mainfrom
refactor/httpx-remove-raganything

Conversation

@Kaiohz

@Kaiohz Kaiohz commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three-part refactor of the mcp-raganything service:

1. Migrate urllibhttpx in BricksApiAdapter

  • Replace urllib.request with httpx.AsyncClient (persistent client created at __init__, closed via aclose() in FastAPI lifespan).
  • Use raise_for_status() for idiomatic HTTP error handling (HTTPStatusError, ConnectError, TimeoutException).
  • Drop asyncio.to_thread (native async calls).

2. Remove RAGAnything / LightRAG (HKUDS) pipeline

  • Delete 28 source files: lightrag_adapter, kreuzberg_raganything_parser, pg_textsearch_adapter, indexing_routes, query_routes, mcp_query_tools, 4 RAGAnything use cases, DTOs, rag_engine port, rag domain errors, alembic/ migrations, rag_storage/.
  • Introduce KreuzbergConfig (extracted from RAGConfig) for shared Kreuzberg extraction settings used by Files / Bricks / Classical.
  • Rename MCP servers: RAGAnythingFiles -> Files, RAGAnythingBricks -> Bricks, RAGAnythingClassical -> ClassicalRAG. Drop /rag/mcp endpoint.
  • Remove dependencies from pyproject.toml: raganything, lightrag-hku, mineru[core], torch, torchvision, alembic, mako, sqlalchemy[asyncio], aiohttp, openai, pgvector.
  • Dockerfile: drop libgomp1 + UV_TORCH_BACKEND=cpu. Dockerfile.db: drop Apache AGE build. .env.example: drop LightRAG variables.

3. Rewrite tests in Arrange/Act/Assert style

  • 32 test files restructured with inline # Arrange / # Act / # Assert comments. Logic and assertions preserved.

SonarQube / Trivy fixes (pre-existing issues also addressed)

  • Fix BLOCKER S8410: FastAPI Annotated dependency injection across all surviving routes.
  • Fix CRITICAL S3776: cognitive complexity in classical_query_use_case, kreuzberg_adapter, bricks_api_adapter.
  • Fix CRITICAL S1186: empty methods, unused variables.
  • Fix MINOR S7503: 24 async handlers without await converted to sync.
  • Bump cryptography>=48.0.1, langsmith>=0.8.18, pydantic-settings>=2.14.2 to fix Trivy HIGH/MEDIUM CVEs.

QA tests updated

  • test_mcp_endpoints.py: removed /rag/mcp/ tests, added /rag/mcp 404 check, renamed server assertions.
  • test_mcp_bricks_endpoints.py: updated server name assertion + session isolation tests.

Verification

Check Result
pytest tests/ 334 passed
ruff check + format All checks passed
SonarQube OPEN issues 0
Trivy vulnerabilities 0
QA Docker tests 26 passed (4 pre-existing failures on publish_section_version schema mismatch, unrelated to this refactor)

Breaking changes

  • /rag/mcp endpoint removed (RAGAnythingQuery MCP server).
  • MCP server names changed: RAGAnythingFiles -> Files, RAGAnythingBricks -> Bricks, RAGAnythingClassical -> ClassicalRAG.
  • Environment variables removed: RAG_STORAGE_TYPE, COSINE_THRESHOLD, MAX_CONCURRENT_FILES, MAX_WORKERS, DOCUMENT_PARSER, ENABLE_IMAGE_PROCESSING, ENABLE_TABLE_PROCESSING, ENABLE_EQUATION_PROCESSING.

Kaiohz added 10 commits June 18, 2026 13:26
- Add BRICKS_HTTP_TIMEOUT and Kreuzberg extraction/quality config
- Extract FastAPI error handlers into dedicated module
- Handle ExtractionTimeoutError in Kreuzberg adapter
- Use structured error messages in MCP tools
Replace the hardcoded VLM timeout value with the new
KREUZBERG_VLM_TIMEOUT config setting and refactor the adapter
to read the overall extraction timeout from the config object
instead of instantiating RAGConfig again.
Upgrade kreuzberg dependency to >=4.9.9 and log extraction errors
before raising domain exceptions.
- Replace urllib with httpx.AsyncClient in BricksApiAdapter (persistent
  client with aclose in lifespan, raise_for_status, idiomatic error
  handling via HTTPStatusError/ConnectError/TimeoutException).
- Remove RAGAnything/LightRAG (HKUDS) pipeline entirely: lightrag_adapter,
  kreuzberg_raganything_parser, pg_textsearch_adapter, indexing/query
  routes, mcp_query_tools, RAGAnything use cases, rag_engine port, rag
  domain errors, alembic migrations, rag_storage working dir.
- Introduce KreuzbergConfig (extracted from RAGConfig) for shared
  Kreuzberg extraction settings used by Files/Bricks/Classical.
- Rename MCP servers: RAGAnythingFiles -> Files, RAGAnythingBricks ->
  Bricks, RAGAnythingClassical -> ClassicalRAG. Drop /rag/mcp endpoint.
- Drop alembic + mako + sqlalchemy[asyncio] (migrations handled by
  langchain-postgres for Classical). Drop raganything, lightrag-hku,
  mineru[core], torch, torchvision, aiohttp, openai, pgvector from
  pyproject. Remove Apache AGE build from Dockerfile.db and libgomp1
  from Dockerfile.
- Rewrite all tests in Arrange/Act/Assert style with inline comments.
- Fix SonarQube blockers: Annotated FastAPI dependency injection,
  cognitive complexity, empty methods, unused variables, async handlers
  without await.
- Bump cryptography>=48.0.1, langsmith>=0.8.18, pydantic-settings>=2.14.2
  to fix Trivy HIGH/MEDIUM CVEs.

Tests: 334 passed. SonarQube: 0 OPEN issues. Trivy: 0 vulnerabilities.
@Kaiohz Kaiohz marked this pull request as ready for review July 8, 2026 14:26
Resolved conflicts keeping our (httpx refactor) version for:
- bricks_api_adapter.py (httpx + X-API-Key already integrated)
- kreuzberg_adapter.py (helpers + timing logs already integrated)
- messages.py (KREUZBERG_EXTRACTION/SERIALIZATION_DURATION already added)
- test_bricks_api_adapter.py (httpx mocks + X-API-Key assertion)
- pyproject.toml (kreuzberg>=4.9.9 already bumped; drop explicit idna
  as it stays transitive via httpx/httpcore)
- uv.lock regenerated via `uv lock` (126 packages resolved)

Deleted by us (kept deletion): src/infrastructure/rag/lightrag_adapter.py,
tests/unit/test_lightrag_adapter.py (RAGAnything pipeline removed).

Changes from main (#50 bump kreuzberg to 4.9.9 + add timing logs,
#51 switch auth header to X-API-Key) were already present in this
refactor branch, so the merge tree is identical to our HEAD.

Verified: ruff check passed, mypy 48 pre-existing errors (no regression),
334 pytest passed.
@Kaiohz

Kaiohz commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

PR Review — refactor: migrate to httpx and remove RAGAnything/LightRAG pipeline

Score: 7/10 — refactor solide et complet, mais quelques points à clarifier avant de merger.

J'ai analysé les 87 fichiers du diff (1855+ / 8125-). Le scope est énorme et la grande majorité des changements est cohérente. Résumé ci-dessous, puis points bloquants / suggestions.

✅ Points forts

  • Migration urllibhttpx propre et idiomatique : httpx.AsyncClient persistant avec aclose() câblé dans le lifespan FastAPI, raise_for_status(), helpers _raise_for_status_error / _raise_for_connection_error / _raise_for_timeout pour DRY les 3 callers (list_project_documents, download_document, publish_section_version). C'est la bonne façon de faire.
  • Suppression RAGAnything/LightRAG : 28 fichiers supprimés, l'isolation du domaine est respectée (domain/ports/rag_engine.py et domain/errors/rag.py out), MCP server renommé (RAGAnythingFiles/Bricks/ClassicalFiles/Bricks/ClassicalRAG).
  • SonarQube / Trivy fixes vraiment appliqués : Annotated pour l'injection FastAPI, complexité cognitive extraite en helpers privés dans classical_query_use_case et bricks_api_adapter, 24 handlers async sync-ifiés, deps bumped (cryptography>=48.0.1, langsmith>=0.8.18, pydantic-settings>=2.14.2).
  • Tests réécrits en Arrange/Act/Assert : 302 # Arrange / 272 # Act / 251 # Assert ajoutés. Les helpers de mock (_mock_response, _http_status_error) et l'usage de AsyncMock(spec=httpx.AsyncClient) sont propres.
  • 334 tests verts + ruff + mypy clean + SonarQube 0 OPEN + Trivy 0 vulns — niveau de confiance élevé sur la non-régression.

🔴 Points bloquants (à fixer avant merge)

1. Comportement d'erreur plus strict sur l'adapter HTTP (risque latent)
Le helper _get attrape httpx.HTTPStatusError (status >= 400) et httpx.RequestError (mère de ConnectError, ReadError, RemoteProtocolError, etc.). Mais le caller ne catch QUE HTTPStatusError, ConnectError, TimeoutException. Conséquence : un RemoteProtocolError (réponse tronquée mid-stream) ou ReadError remonte désormais brut au lieu d'être converti en BricksApiError — alors qu'avant, le except Exception les transformait.

Suggestion : ajouter httpx.RequestError (ou un httpx.NetworkError parent) dans le except de chaque caller, avec un mapping BricksConnectionError ou BricksApiError selon le cas. C'est un changement de comportement silencieux qui peut faire surface en production sous mauvaise connectivité.

2. KREUZBERG_VLM_TIMEOUT n'apparaît pas dans .env.example
Vous le référencez dans kreuzberg_adapter.py (timeout_secs=cfg.KREUZBERG_VLM_TIMEOUT) et dans le body de la PR vous mentionnez KREUZBERG_VLM_TIMEOUT comme config setting ajouté. Mais .env.example ne le liste pas (seulement KREUZBERG_OCR_MODE, KREUZBERG_EXTRACTION_TIMEOUT, KREUZBERG_EXTRACT_IMAGES). Soit c'est un oubli (ajouter la ligne), soit retirez la mention du body pour cohérence.

3. Test test_uses_http_timeout_from_config re-crée l'adapter manuellement
Le test mute bricks_config.BRICKS_HTTP_TIMEOUT = 42 puis instancie un nouvel adapter au lieu d'utiliser la fixture adapter (qui a déjà un AsyncMock pour _client). Conséquence : le test ferme le client via await instance._client.aclose() sur un mock — opération no-op mais qui révèle un couplage fragile. Le test du mock factory httpx.Timeout(42) est correct, mais c'est un test d'implémentation (lit _client.timeout.read) plutôt qu'un test de comportement. Préférable d'assert via une variable d'observation : passer par un spy.

🟡 Suggestions (non bloquantes)

4. kreuzberg_adapter._coerce_page_dict — silencieux sur les shapes inattendues
return {} si la page n'est ni dict ni objet avec __dict__. C'est défensif mais ça peut masquer un changement de format Kreuzberg (renvoyerait des pages vides silencieusement). Préférable : logger un warning quand on tombe sur un type inconnu, ou laisser remonter l'erreur.

5. RAGConfigKreuzbergConfig — la docstring est minimale
Vous avez extrait la partie Kreuzberg, c'est bien. Mais le reste de RAGConfig (chunking, OCR mode) est mélangé avec des params de plus haut niveau. À terme, envisagez de scinder KreuzbergConfig en KreuzbergOCRConfig + KreuzbergExtractionConfig si ça grossit.

6. Migration bricks_api_adapter : retirer asyncio.to_thread
OK, mais c'est aussi la fin de l'éventuelle parallélisation "gratuitement" via thread pool. Le commentaire dans le body mentionne que c'est natif async maintenant — confirmez qu'il n'y a pas de blocking call caché dans le reste du flow (FastAPI handlers, autres adapters).

7. Tests AAA — 2-3 mal placés
Dans tests/unit/test_kreuzberg_adapter.py, j'ai vu deux # Arrange qui apparaissent à l'intérieur d'un literal d'assertion (entre le dict d'assertion) et un # Act & Assert suivi d'un # Act puis # Assert qui crée de la confusion. Cf. la version finale du fichier pour aligner tout le monde sur le pattern AAA strict.

8. pyproject.toml — fin de fichier sans newline
\ No newline at end of file dans la diff. Pas un blocker mais ruff format peut le signaler.

9. Dockerfile.db — fin de fichier sans newline
Même remarque.

10. Suppression de tests/fixtures/external.py
Pas de remplacement explicite. Si quelque chose ailleurs dépendait de ce fixture, ça va casser à l'import. À vérifier.

📊 Synthèse

Critère Note
Cohérence migration httpx 9/10
Suppression RAGAnything 9/10
SonarQube / Trivy fixes 9/10
Tests AAA + coverage 7/10 (placement de commentaires à corriger)
Gestion d'erreurs httpx 6/10 (le point #1 est important)
Documentation (body, .env) 7/10 (#2 à clarifier)

Verdict : request changes sur les points #1 et #2. Une fois corrigés + #3/#7 adressés, c'est mergeable.

@Kaiohz Kaiohz merged commit 4e9db15 into main Jul 8, 2026
1 check passed
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.

1 participant