Files
ATOCore/src/atocore/api/routes.py

133 lines
3.2 KiB
Python
Raw Normal View History

"""FastAPI route definitions."""
from pathlib import Path
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from atocore.context.builder import (
ContextPack,
build_context,
get_last_context_pack,
_pack_to_dict,
)
from atocore.ingestion.pipeline import ingest_file, ingest_folder
from atocore.retrieval.retriever import retrieve
from atocore.retrieval.vector_store import get_vector_store
router = APIRouter()
# --- Request/Response models ---
class IngestRequest(BaseModel):
path: str # file or folder path
class IngestResponse(BaseModel):
results: list[dict]
class QueryRequest(BaseModel):
prompt: str
top_k: int = 10
filter_tags: list[str] | None = None
class QueryResponse(BaseModel):
results: list[dict]
class ContextBuildRequest(BaseModel):
prompt: str
project: str | None = None
budget: int | None = None
class ContextBuildResponse(BaseModel):
formatted_context: str
full_prompt: str
chunks_used: int
total_chars: int
budget: int
budget_remaining: int
duration_ms: int
chunks: list[dict]
# --- Endpoints ---
@router.post("/ingest", response_model=IngestResponse)
def api_ingest(req: IngestRequest):
"""Ingest a markdown file or folder."""
target = Path(req.path)
if target.is_file():
results = [ingest_file(target)]
elif target.is_dir():
results = ingest_folder(target)
else:
raise HTTPException(status_code=404, detail=f"Path not found: {req.path}")
return IngestResponse(results=results)
@router.post("/query", response_model=QueryResponse)
def api_query(req: QueryRequest):
"""Retrieve relevant chunks for a prompt."""
chunks = retrieve(req.prompt, top_k=req.top_k, filter_tags=req.filter_tags)
return QueryResponse(
results=[
{
"chunk_id": c.chunk_id,
"content": c.content,
"score": c.score,
"heading_path": c.heading_path,
"source_file": c.source_file,
"title": c.title,
}
for c in chunks
]
)
@router.post("/context/build", response_model=ContextBuildResponse)
def api_build_context(req: ContextBuildRequest):
"""Build a full context pack for a prompt."""
pack = build_context(
user_prompt=req.prompt,
project_hint=req.project,
budget=req.budget,
)
pack_dict = _pack_to_dict(pack)
return ContextBuildResponse(
formatted_context=pack.formatted_context,
full_prompt=pack.full_prompt,
chunks_used=len(pack.chunks_used),
total_chars=pack.total_chars,
budget=pack.budget,
budget_remaining=pack.budget_remaining,
duration_ms=pack.duration_ms,
chunks=pack_dict["chunks"],
)
@router.get("/health")
def api_health():
"""Health check."""
store = get_vector_store()
return {
"status": "ok",
"version": "0.1.0",
"vectors_count": store.count,
}
@router.get("/debug/context")
def api_debug_context():
"""Inspect the last assembled context pack."""
pack = get_last_context_pack()
if pack is None:
return {"message": "No context pack built yet."}
return _pack_to_dict(pack)