""" HCFS API Server - FastAPI-based REST API for context operations. """ from typing import List, Optional from datetime import datetime from pydantic import BaseModel from fastapi import FastAPI, HTTPException, Depends from fastapi.responses import JSONResponse from ..core.context_db import ContextDatabase, Context from ..core.embeddings import EmbeddingManager # Pydantic models class ContextCreateRequest(BaseModel): path: str content: str summary: Optional[str] = None author: Optional[str] = None class ContextResponse(BaseModel): id: int path: str content: str summary: Optional[str] author: Optional[str] created_at: datetime updated_at: datetime version: int class SearchRequest(BaseModel): query: str path_prefix: Optional[str] = None top_k: int = 5 search_type: str = "hybrid" # "semantic", "hybrid" class SearchResult(BaseModel): context: ContextResponse score: float class ContextAPI: """HCFS REST API server.""" def __init__(self, context_db: ContextDatabase, embedding_manager: EmbeddingManager): self.context_db = context_db self.embedding_manager = embedding_manager self.app = FastAPI( title="HCFS Context API", description="Context-Aware Hierarchical Context File System API", version="0.1.0" ) self._setup_routes() def _setup_routes(self): """Setup API routes.""" @self.app.get("/health") async def health_check(): """Health check endpoint.""" return {"status": "healthy", "service": "hcfs-api"} @self.app.post("/context", response_model=ContextResponse) async def create_context(request: ContextCreateRequest): """Create a new context.""" context = Context( id=None, path=request.path, content=request.content, summary=request.summary, author=request.author ) # Store with embedding context_id = self.embedding_manager.store_context_with_embedding(context) # Retrieve the stored context stored_contexts = self.context_db.list_contexts_at_path(request.path) stored_context = next((c for c in stored_contexts if c.id == context_id), None) if not stored_context: raise HTTPException(status_code=500, detail="Failed to store context") return ContextResponse(**stored_context.__dict__) @self.app.get("/context/{path:path}", response_model=List[ContextResponse]) async def get_context(path: str, depth: int = 1): """Get contexts for a path with optional parent inheritance.""" contexts = self.context_db.get_context_by_path(f"/{path}", depth=depth) return [ContextResponse(**ctx.__dict__) for ctx in contexts] @self.app.get("/context", response_model=List[ContextResponse]) async def list_contexts(path: str): """List all contexts at a specific path.""" contexts = self.context_db.list_contexts_at_path(path) return [ContextResponse(**ctx.__dict__) for ctx in contexts] @self.app.put("/context/{context_id}") async def update_context(context_id: int, content: str, summary: Optional[str] = None): """Update an existing context.""" success = self.context_db.update_context(context_id, content, summary) if not success: raise HTTPException(status_code=404, detail="Context not found") # Update embedding contexts = self.context_db.list_contexts_at_path("") # Get updated context updated_context = next((c for c in contexts if c.id == context_id), None) if updated_context: embedding = self.embedding_manager.generate_embedding(updated_context.content) self.embedding_manager._store_embedding(context_id, embedding) return {"message": "Context updated successfully"} @self.app.delete("/context/{context_id}") async def delete_context(context_id: int): """Delete a context.""" success = self.context_db.delete_context(context_id) if not success: raise HTTPException(status_code=404, detail="Context not found") return {"message": "Context deleted successfully"} @self.app.post("/search", response_model=List[SearchResult]) async def search_contexts(request: SearchRequest): """Search contexts using semantic or hybrid search.""" if request.search_type == "semantic": results = self.embedding_manager.semantic_search( request.query, request.path_prefix, request.top_k ) elif request.search_type == "hybrid": results = self.embedding_manager.hybrid_search( request.query, request.path_prefix, request.top_k ) else: raise HTTPException(status_code=400, detail="Invalid search_type") return [ SearchResult( context=ContextResponse(**ctx.__dict__), score=score ) for ctx, score in results ] @self.app.get("/similar/{context_id}", response_model=List[SearchResult]) async def get_similar_contexts(context_id: int, top_k: int = 5): """Find contexts similar to a given context.""" results = self.embedding_manager.get_similar_contexts(context_id, top_k) return [ SearchResult( context=ContextResponse(**ctx.__dict__), score=score ) for ctx, score in results ] def create_app(db_path: str = "hcfs_context.db") -> FastAPI: """Create FastAPI application with HCFS components.""" context_db = ContextDatabase(db_path) embedding_manager = EmbeddingManager(context_db) api = ContextAPI(context_db, embedding_manager) return api.app