Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ test-results
**/.mypy_cache
**/.ruff_cache
**/*.tsbuildinfo
.env*
.env.local
.envrc
*.pem
*.key
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: ci
on:
push:
pull_request:

jobs:
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- uses: dtolnay/rust-toolchain@stable
- name: Install
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
pip install ruff maturin
- name: Build depth_engine
run: |
maturin build --manifest-path crates/depth_engine/Cargo.toml --out dist
pip install dist/*.whl
- name: Lint
run: ruff check api tests
- name: Compile
run: python -m compileall -q api scripts evaluation
- name: Test
run: pytest -q
- name: Diff check
run: git diff --check
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ retrieval results before retiring the old database volume or backups.

```text
api/ FastAPI application and PostgreSQL adapter
db/ PostgreSQL schema and development seed data
crates/ Rust depth_engine (offline parse/chunk/retrieval)
db/ PostgreSQL schema (001/002/003) and development seed data
docker-compose.yml PostgreSQL pgvector + Redis for local dev
scripts/ Offline ingestion, migration, validation, and replication
evaluation/ Offline evaluation harnesses
demo/ Standalone demo server
tests/ Unit, integration, and quality tests
```

Expand All @@ -112,9 +113,17 @@ tests/ Unit, integration, and quality tests
pip install -e ".[dev]"
pytest
python -m compileall -q api scripts evaluation
ruff check api tests
git diff --check
```

Docker builds expect the repo root as context:

```bash
docker build -f api/Dockerfile .
docker build -f api/Dockerfile.test .
```

## Code navigation graph

The optional `code-review-graph` tool maintains an ignored structural index in
Expand Down
7 changes: 7 additions & 0 deletions api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ WORKDIR /app
ENV PYTHONPATH=/app \
PYTHONUNBUFFERED=1

# NOTE: build from repo root: docker build -f api/Dockerfile .
COPY . /tmp/src
RUN set -eux; \
if [ -d /tmp/src/api ]; then \
pip install --no-cache-dir -r /tmp/src/api/requirements.txt; \
mkdir -p /app/api; \
cp -a /tmp/src/api/. /app/api/; \
rm -rf /app/api/tests /app/api/scripts; \
elif [ -f /tmp/src/requirements.txt ]; then \
pip install --no-cache-dir -r /tmp/src/requirements.txt; \
mkdir -p /app/api; \
Expand All @@ -21,6 +23,11 @@ RUN set -eux; \
fi; \
rm -rf /tmp/src

RUN useradd -m -u 10001 appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')" || exit 1

CMD ["sh", "-c", "uvicorn api.main:app --host 0.0.0.0 --port ${PORT:-8000}"]
9 changes: 6 additions & 3 deletions api/Dockerfile.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ FROM python:3.11-slim

WORKDIR /app

# NOTE: build from repo root: docker build -f api/Dockerfile.test .
COPY pyproject.toml /app/pyproject.toml
COPY api/requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt \
&& pip install --no-cache-dir pytest
&& pip install --no-cache-dir pytest pytest-asyncio pytest-mock pytest-cov

COPY api /app
COPY api /app/api
COPY tests /app/tests

ENV PYTHONPATH=/app

CMD ["python", "-m", "pytest", "tests"]
CMD ["python", "-m", "pytest", "tests", "api/tests", "-q"]
50 changes: 50 additions & 0 deletions api/adapters/pg_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,67 @@ def get_pool() -> asyncpg.Pool:
raise RuntimeError("PostgreSQL pool has not been initialised")
return _pool

_ALLOWED_RPC_FUNCTIONS = frozenset({
"hybrid_search_v5",
"hybrid_search_trusted_v5",
"hybrid_search_with_graph_v5",
"hybrid_search_trusted_with_graph_v5",
"queue_document",
"dequeue_document",
"complete_document",
"get_neighbor_chunks",
"get_embedding_dimension",
"link_chunk_to_concept",
"get_concept_lineage",
"delete_collection",
})

_ALLOWED_TABLES = frozenset({
"api_keys",
"knowledge_collections",
"knowledge_documents",
"knowledge_chunks",
"knowledge_concepts",
"knowledge_edges",
"knowledge_chunk_concepts",
"knowledge_ingestion_queue",
"knowledge_query_logs",
})

_ALLOWED_COLUMNS = frozenset({
"id",
"key_hash",
"is_active",
"plan",
"api_key_id",
"collection_id",
"document_id",
"content_hash",
"status",
"name",
"expires_at",
"scopes",
"revoked_at",
})

async def execute_rpc(fn_name: str, params: dict) -> list[dict]:
if fn_name not in _ALLOWED_RPC_FUNCTIONS:
raise ValueError(f"RPC function not allowed: {fn_name}")
values = list(params.values())
placeholders = ", ".join(f"${i}" for i in range(1, len(values) + 1))
async with get_pool().acquire() as conn:
rows = await conn.fetch(f"SELECT * FROM {fn_name}({placeholders})", *values)
return [dict(row) for row in rows]

async def fetch_one(table: str, where: dict) -> dict | None:
if table not in _ALLOWED_TABLES:
raise ValueError(f"Table not allowed: {table}")
if not where:
raise ValueError("where must not be empty")
columns = list(where)
for column in columns:
if column not in _ALLOWED_COLUMNS:
raise ValueError(f"Column not allowed: {column}")
clause = " AND ".join(f"{column} = ${i}" for i, column in enumerate(columns, 1))
async with get_pool().acquire() as conn:
row = await conn.fetchrow(f"SELECT * FROM {table} WHERE {clause} LIMIT 1", *(where[c] for c in columns))
Expand Down
2 changes: 0 additions & 2 deletions api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,4 @@ def get_settings() -> Settings:

def reinitialize_cache() -> None:
"""Clear cache and recompute on next access (for testing)."""
global _STREAM_CONFIG
_STREAM_CONFIG = None
get_settings.cache_clear()
Loading
Loading