172 lines
6.3 KiB
Python
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 |