Skip to content

Commit d8e286e

Browse files
committed
updates after code review
1 parent a6c4e60 commit d8e286e

34 files changed

+811
-515
lines changed

.github/workflows/api-build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
branches: [main]
66
paths:
77
- 'services/query-engine/**'
8+
pull_request:
9+
branches: [main]
10+
paths:
11+
- 'services/query-engine/**'
812

913
jobs:
1014
build:

.github/workflows/api-tests.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
branches: [main]
66
paths:
77
- 'services/query-engine/**'
8+
pull_request:
9+
branches: [main]
10+
paths:
11+
- 'services/query-engine/**'
812

913
jobs:
1014
test:
@@ -25,6 +29,14 @@ jobs:
2529
working-directory: services/query-engine
2630
run: poetry install --no-root --no-interaction
2731

32+
- name: Lint
33+
working-directory: services/query-engine
34+
run: poetry run ruff check .
35+
36+
- name: Type check
37+
working-directory: services/query-engine
38+
run: poetry run mypy app tests
39+
2840
- name: Run tests
2941
working-directory: services/query-engine
3042
run: poetry run pytest tests/ -v

.github/workflows/ui-build.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ on:
55
branches: [main]
66
paths:
77
- 'ui/**'
8+
pull_request:
9+
branches: [main]
10+
paths:
11+
- 'ui/**'
812

913
jobs:
1014
build:
@@ -13,5 +17,25 @@ jobs:
1317
steps:
1418
- uses: actions/checkout@v4
1519

20+
- name: Set up Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: '22'
24+
25+
- name: Enable corepack
26+
run: corepack enable
27+
28+
- name: Install dependencies
29+
working-directory: ui
30+
run: pnpm install --frozen-lockfile
31+
32+
- name: Lint
33+
working-directory: ui
34+
run: pnpm lint
35+
36+
- name: Build
37+
working-directory: ui
38+
run: pnpm build
39+
1640
- name: Docker build
1741
run: docker build -t query-engine-ui:${{ github.sha }} ui

AGENT.md

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
## Project Overview
44

5-
FastAPI backend service that exposes a LlamaIndex Knowledge Graph query engine over HTTPS. Called by a Kotlin LangGraph4j agent as a tool. Neo4j is the graph store.
5+
Hybrid RAG system with a FastAPI backend and Vue 3 frontend. The backend exposes a LlamaIndex Knowledge Graph query engine over HTTPS, called by a Kotlin LangGraph4j agent as a tool. Neo4j is the graph store, pgvector handles vector embeddings, and Celery processes ingestion and deletion asynchronously. The UI provides document upload with interactive knowledge graph visualization, a query interface with retrieval mode selection, and document management.
66

77
## Tech Stack
88

9+
### Backend
910
- **Framework**: FastAPI + Uvicorn
1011
- **Validation**: Pydantic + Pydantic-Settings
1112
- **Logging**: Structlog (JSON in prod, console in debug)
@@ -18,21 +19,44 @@ FastAPI backend service that exposes a LlamaIndex Knowledge Graph query engine o
1819
- **Type Checking**: mypy (strict mode)
1920
- **Testing**: pytest + pytest-asyncio + httpx
2021

22+
### Frontend
23+
- **Framework**: Vue 3 + TypeScript (Composition API, `<script setup>`)
24+
- **Build**: Vite + pnpm
25+
- **Styling**: Tailwind CSS + shadcn-vue
26+
- **State**: Pinia
27+
- **Routing**: Vue Router 4
28+
- **Graph Visualization**: Cytoscape.js
29+
- **Linting**: ESLint (flat config) with eslint-plugin-vue + typescript-eslint
30+
2131
## Project Structure
2232

2333
```
24-
app/
25-
├── api/v1/ # API route handlers
26-
├── connectors/ # Document source connectors (GCS)
27-
├── core/ # Config (Pydantic Settings), logging, errors, middleware
28-
├── models/ # Pydantic request/response schemas (CamelModel base)
29-
├── services/ # Business logic (KnowledgeGraphService, IngestionPipeline)
30-
└── worker/ # Celery app, background tasks
31-
tests/ # Pytest test suite
34+
services/query-engine/ # Backend
35+
app/
36+
├── api/v1/ # API route handlers
37+
├── connectors/ # Document source connectors (GCS)
38+
├── core/ # Config (Pydantic Settings), logging, errors, middleware
39+
├── models/ # Pydantic request/response schemas (CamelModel base)
40+
├── services/ # Business logic (KnowledgeGraphService, IngestionPipeline)
41+
└── worker/ # Celery app, background tasks
42+
tests/ # Pytest test suite
43+
44+
ui/ # Frontend
45+
src/
46+
├── components/ # Reusable components (layout, graph, upload, ui)
47+
├── composables/ # Composition API hooks
48+
├── layouts/ # Page layouts
49+
├── router/ # Vue Router config
50+
├── services/ # API client
51+
├── stores/ # Pinia state
52+
├── types/ # TypeScript interfaces
53+
└── views/ # Page views (QueryView, UploadView)
3254
```
3355

3456
## Commands
3557

58+
### Backend
59+
3660
All commands run from `services/query-engine/`.
3761

3862
```bash
@@ -58,8 +82,27 @@ poetry run mypy .
5882
poetry run celery -A app.worker.celery_app:celery_app worker --loglevel=info
5983
```
6084

85+
### Frontend
86+
87+
All commands run from `ui/`.
88+
89+
```bash
90+
# Install dependencies
91+
pnpm install
92+
93+
# Run dev server
94+
pnpm dev
95+
96+
# Type check + production build
97+
pnpm build
98+
99+
# Lint
100+
pnpm lint
101+
```
102+
61103
## Code Conventions
62104

105+
### Backend
63106
- **Line length**: 100 characters
64107
- **Type hints**: Required on all functions (strict mypy)
65108
- **Modern Python syntax**: Use `list[str]` not `List[str]`, etc.
@@ -70,9 +113,15 @@ poetry run celery -A app.worker.celery_app:celery_app worker --loglevel=info
70113
- **Logging**: Use structlog, not `print()` or stdlib `logging` directly
71114
- **Docstrings**: PEP 257 — all public modules, classes, functions, and methods must have docstrings. Use triple double quotes. One-line for simple cases, multi-line with summary + blank line + description for complex ones.
72115

116+
### Frontend
117+
- **Components in `src/components/ui/`** are auto-generated by shadcn-vue and excluded from linting — do not edit directly
118+
- **Composition API only** — use `<script setup lang="ts">`, no Options API
119+
- **Import paths**: Use `@/` alias (maps to `src/`)
120+
73121
## Testing
74122

75123
- Tests live in `tests/` mirroring the app structure
76124
- Use `pytest-asyncio` with mode `auto`
77125
- Use `httpx.AsyncClient` with `ASGITransport` for endpoint tests
78126
- Fixtures defined in `tests/conftest.py`
127+
- `conftest.py` sets `CELERY_BROKER_URL=""` at import time to force in-memory rate limiter storage — no Redis or Docker required to run tests

services/query-engine/.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ LOG_LEVEL=info
55
HOST=0.0.0.0
66
PORT=8000
77
WORKERS=1
8-
CORS_ORIGINS=["*"]
8+
CORS_ORIGINS=[]
9+
CORS_ORIGIN_REGEX=^https?://(localhost|127\.0\.0\.1)(:\d+)?$
910

1011
# OpenAI / LlamaIndex
1112
OPENAI_API_KEY=

services/query-engine/app/api/v1/knowledge_graph.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ async def get_subgraph(
165165
@limiter.limit(settings.rate_limit_default)
166166
async def get_document_graph(
167167
request: Request,
168-
doc_ids: list[str] = Query(..., max_length=50, description="Document IDs (supports multi-chunk docs, max 50)"),
168+
doc_ids: list[str] = Query(
169+
..., max_length=50, description="Document IDs (max 50)",
170+
),
169171
service: KnowledgeGraphService = Depends(get_kg_service),
170172
) -> SubgraphResponse:
171173
"""Retrieve the graph for a specific ingested document."""

services/query-engine/app/api/v1/tasks.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,16 @@
3535
@limiter.limit(settings.rate_limit_default)
3636
async def get_task_status(request: Request, task_id: str) -> TaskStatusResponse:
3737
"""Poll the status of a background task."""
38-
result = AsyncResult(task_id, app=celery_app)
38+
result: AsyncResult[dict[str, Any]] = AsyncResult(task_id, app=celery_app)
3939

4040
status = _STATUS_MAP.get(result.status, TaskStatus.PENDING)
4141

42-
task_result = None
42+
task_result: SourceIngestResult | DeleteDocumentResult | None = None
4343
error = None
4444

4545
if result.successful():
46-
raw: dict[str, Any] = result.result
46+
raw = result.result
47+
assert isinstance(raw, dict)
4748
task_type = raw.get("task_type")
4849
if task_type == "delete_document":
4950
task_result = DeleteDocumentResult(**raw)

services/query-engine/app/core/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class Settings(BaseSettings):
2020
port: int = 8000
2121
workers: int = 1
2222

23-
cors_origins: list[str] = ["*"]
23+
cors_origins: list[str] = []
24+
cors_origin_regex: str = r"^https?://(localhost|127\.0\.0\.1)(:\d+)?$"
2425

2526
# OpenAI
2627
openai_api_key: str = ""

services/query-engine/app/main.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
from app.core.error_handlers import register_error_handlers
1717
from app.core.gcs import get_gcs_client
1818
from app.core.logging import setup_logging
19-
from app.core.postgres import get_pg_engine
2019
from app.core.middleware import RequestContextMiddleware
20+
from app.core.postgres import get_pg_engine
2121
from app.core.rate_limit import limiter, rate_limit_exceeded_handler
2222
from app.services.knowledge_graph import KnowledgeGraphService
2323
from app.services.query_cache import create_query_cache
@@ -45,7 +45,9 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
4545
"""Handle application startup and shutdown events."""
4646
setup_logging()
4747
logger.info("starting", app_name=settings.app_name, version=settings.app_version)
48-
pg_engine = get_pg_engine(settings.postgres_uri) if settings.postgres_enabled and settings.postgres_uri else None
48+
pg_engine = None
49+
if settings.postgres_enabled and settings.postgres_uri:
50+
pg_engine = get_pg_engine(settings.postgres_uri)
4951
cache = create_query_cache(settings, engine=pg_engine)
5052
_app.state.kg_service = KnowledgeGraphService(
5153
settings, cache=cache, engine=pg_engine
@@ -80,6 +82,7 @@ def create_app() -> FastAPI:
8082
app.add_middleware(
8183
CORSMiddleware,
8284
allow_origins=settings.cors_origins,
85+
allow_origin_regex=settings.cors_origin_regex or None,
8386
allow_credentials=True,
8487
allow_methods=["*"],
8588
allow_headers=["*"],

services/query-engine/app/models/tasks.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Task status models for background processing."""
22

33
from enum import StrEnum
4-
from typing import Any
54

65
from pydantic import Field
76

0 commit comments

Comments
 (0)