Files
HCFS/hcfs-python/hcfs/api/server.py
2025-07-30 09:34:16 +10:00

172 lines
6.3 KiB
Python

"""
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