Skip to content

Commit 18056dd

Browse files
Merge pull request #298 from techdeveloper-org/fix/code-review-round-9
fix(review): resolve regressions from #147-#240 batch, harden auth + prompt-injection guards
2 parents 1a7c9de + c2f23df commit 18056dd

36 files changed

Lines changed: 248 additions & 89 deletions

‎.github/workflows/ci.yml‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ jobs:
4545
test:
4646
timeout-minutes: 30
4747
runs-on: ubuntu-latest
48+
# db/tests/test_schema.py::test_forward_then_rollback_restores_baseline runs
49+
# a live migration round-trip against DATABASE_URL instead of skipping, so
50+
# this job needs an actual Postgres server for that DSN to resolve.
51+
services:
52+
postgres:
53+
image: postgres:16
54+
env:
55+
POSTGRES_USER: test
56+
POSTGRES_PASSWORD: test
57+
POSTGRES_DB: test
58+
ports:
59+
- 5432:5432
60+
options: >-
61+
--health-cmd="pg_isready -U test"
62+
--health-interval=5s
63+
--health-timeout=5s
64+
--health-retries=10
4865
env:
4966
DATABASE_URL: "postgresql+asyncpg://test:test@localhost:5432/test"
5067
QDRANT_URL: "http://localhost:6333"

‎.secrets.baseline‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎backend/app/adapters/__init__.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ def __getattr__(name: str) -> object:
4444
AttributeError: When ``name`` is not a known adapter.
4545
"""
4646
_lazy_map = {
47-
"SqlAlchemyDocumentStore": ("backend.app.adapters.document_store", "SqlAlchemyDocumentStore"),
47+
"SqlAlchemyDocumentStore": (
48+
"backend.app.adapters.document_store",
49+
"SqlAlchemyDocumentStore",
50+
),
4851
"ClaudeGenerationLLM": ("backend.app.adapters.generation", "ClaudeGenerationLLM"),
4952
"PipelineIngestor": ("backend.app.adapters.ingestor", "PipelineIngestor"),
5053
"RouterModuleAdapter": ("backend.app.adapters.router", "RouterModuleAdapter"),

‎backend/app/adapters/generation.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
from __future__ import annotations
1919

2020
import asyncio
21+
import logging
2122
import os
2223
from collections.abc import AsyncIterator
2324

2425
from backend.app.api.interfaces import DependencyUnavailable, RoutedSection
2526
from backend.app.settings import get_settings
2627

28+
_logger = logging.getLogger(__name__)
29+
2730
DEFAULT_GENERATION_MODEL = os.environ.get("GENERATION_MODEL", "claude-opus-4-8")
2831
"""Answer-synthesis model id (overridable via the GENERATION_MODEL env var)."""
2932

@@ -49,6 +52,10 @@ def _default_max_tokens() -> int:
4952
try:
5053
return get_settings().generation_max_tokens
5154
except Exception:
55+
_logger.warning(
56+
"Failed to read generation_max_tokens from settings; using fallback",
57+
exc_info=True,
58+
)
5259
return 16000
5360

5461

@@ -61,6 +68,10 @@ def _default_thinking_budget() -> int:
6168
try:
6269
return get_settings().generation_thinking_budget_tokens
6370
except Exception:
71+
_logger.warning(
72+
"Failed to read generation_thinking_budget_tokens from settings; using fallback",
73+
exc_info=True,
74+
)
6475
return 5000
6576

6677

‎backend/app/adapters/ingestor.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Ingestor adapter binding ``ingestion.ingest_document`` to the backend Protocol.
1+
"""Ingestor adapter binding ``ingestion.ingest_document`` to the backend Protocol.
22
33
The backend :class:`Ingestor` Protocol is ``ingest_document(tenant_id, content,
44
filename, title, domain, no_retention, residency_region, ocr) -> IngestOutcome``.
@@ -247,6 +247,7 @@ async def ingest_document(
247247
toc = list(result.get("toc") or [])
248248
fallback_only = bool(result.get("fallback_only", False))
249249
extracted_title = result.get("title") or title
250+
250251
return IngestOutcome(
251252
doc_id=doc_id_str,
252253
title=extracted_title,

‎backend/app/api/answer.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ async def answer_query(
218218
raise document_not_found()
219219

220220
if document.fallback_only:
221-
# TODO: product owner to confirm — Option B (whole-document RAG) may replace this
221+
# TODO: product owner to confirm - Option B (whole-document RAG) may replace this
222222
raise validation_error(
223223
detail=(
224224
"This document does not support section-level routing."

‎backend/app/api/dependencies.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from __future__ import annotations
2424

25+
import logging
2526
import threading
2627

2728
from backend.app.api.interfaces import (
@@ -33,6 +34,8 @@
3334
from backend.app.errors import service_unavailable
3435
from backend.app.settings import get_settings
3536

37+
logger = logging.getLogger(__name__)
38+
3639
_singleton_lock = threading.RLock()
3740
"""Single re-entrant process-wide lock for all singleton builders (issue #222).
3841
@@ -146,10 +149,10 @@ def _generation_llm_singleton() -> GenerationLLM:
146149
from backend.app.settings import get_settings
147150

148151
settings = get_settings()
149-
_generation_llm_cache = ClaudeGenerationLLM( # type: ignore[assignment]
152+
_generation_llm_cache = ClaudeGenerationLLM(
150153
thinking_budget_tokens=settings.generation_thinking_budget_tokens
151154
)
152-
return _generation_llm_cache # type: ignore[return-value]
155+
return _generation_llm_cache
153156

154157

155158
def get_document_store() -> DocumentStore:
@@ -207,4 +210,4 @@ async def _dispose_engines() -> None:
207210
if engine is not None:
208211
await engine.dispose()
209212
except Exception:
210-
pass
213+
logger.warning("Error disposing document store engine on shutdown", exc_info=True)

‎backend/app/api/interfaces.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from __future__ import annotations
2020

21-
from collections.abc import AsyncGenerator, AsyncIterator
21+
from collections.abc import AsyncIterator
2222
from dataclasses import dataclass, field
2323
from typing import Protocol, runtime_checkable
2424

@@ -214,7 +214,8 @@ async def stream_answer(
214214
sections: list[RoutedSection],
215215
) -> AsyncIterator[str]:
216216
"""Yield answer token fragments for the routed sections."""
217-
...
217+
raise NotImplementedError
218+
yield # pragma: no cover - unreachable, marks this an async generator
218219

219220

220221
class DependencyUnavailable(Exception): # noqa: N818 - maps to SERVICE_UNAVAILABLE, not an *Error

‎backend/app/api/observability.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Observability surface: the ``/metrics`` scrape endpoint (PRD §21, NFR-009).
1+
"""Observability surface: the ``/metrics`` scrape endpoint (PRD Sec. 21, NFR-009).
22
33
Exposes the four product KPIs (token reduction %, answer accuracy, routing
44
latency, fallback rate) in Prometheus text exposition format. LangSmith

‎backend/app/api/routing.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ async def route_query(
8787
"""
8888
document_ids = list(dict.fromkeys(_target_document_ids(body)))
8989

90-
# Fix #262: rerank=True is not supported — reject with 422 instead of
90+
# Fix #262: rerank=True is not supported - reject with 422 instead of
9191
# silently downgrading and returning HTTP 200.
9292
if body.rerank:
9393
raise validation_error(

0 commit comments

Comments
 (0)