feat: implement TTS, Document processing, and Memory Service /facts API
- TTS: xtts-v2 integration with voice cloning support
- Document: docling integration for PDF/DOCX/PPTX processing
- Memory Service: added /facts/upsert, /facts/{key}, /facts endpoints
- Added required dependencies (TTS, docling)
This commit is contained in:
@@ -477,6 +477,102 @@ async def get_context(
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# FACTS (Simple Key-Value storage for Gateway compatibility)
|
||||
# ============================================================================
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing import Any
|
||||
|
||||
class FactUpsertRequest(BaseModel):
|
||||
"""Request to upsert a user fact"""
|
||||
user_id: str
|
||||
fact_key: str
|
||||
fact_value: Optional[str] = None
|
||||
fact_value_json: Optional[dict] = None
|
||||
team_id: Optional[str] = None
|
||||
|
||||
@app.post("/facts/upsert")
|
||||
async def upsert_fact(request: FactUpsertRequest):
|
||||
"""
|
||||
Create or update a user fact.
|
||||
|
||||
This is a simple key-value store for Gateway compatibility.
|
||||
Facts are stored in PostgreSQL without vector indexing.
|
||||
"""
|
||||
try:
|
||||
# Ensure facts table exists (will be created on first call)
|
||||
await db.ensure_facts_table()
|
||||
|
||||
# Upsert the fact
|
||||
result = await db.upsert_fact(
|
||||
user_id=request.user_id,
|
||||
fact_key=request.fact_key,
|
||||
fact_value=request.fact_value,
|
||||
fact_value_json=request.fact_value_json,
|
||||
team_id=request.team_id
|
||||
)
|
||||
|
||||
logger.info(f"fact_upserted", user_id=request.user_id, fact_key=request.fact_key)
|
||||
return {"status": "ok", "fact_id": result.get("fact_id") if result else None}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"fact_upsert_failed", error=str(e), user_id=request.user_id)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/facts/{fact_key}")
|
||||
async def get_fact(
|
||||
fact_key: str,
|
||||
user_id: str = Query(...),
|
||||
team_id: Optional[str] = None
|
||||
):
|
||||
"""Get a specific fact for a user"""
|
||||
try:
|
||||
fact = await db.get_fact(user_id=user_id, fact_key=fact_key, team_id=team_id)
|
||||
if not fact:
|
||||
raise HTTPException(status_code=404, detail="Fact not found")
|
||||
return fact
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"fact_get_failed", error=str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/facts")
|
||||
async def list_facts(
|
||||
user_id: str = Query(...),
|
||||
team_id: Optional[str] = None
|
||||
):
|
||||
"""List all facts for a user"""
|
||||
try:
|
||||
facts = await db.list_facts(user_id=user_id, team_id=team_id)
|
||||
return {"facts": facts}
|
||||
except Exception as e:
|
||||
logger.error(f"facts_list_failed", error=str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.delete("/facts/{fact_key}")
|
||||
async def delete_fact(
|
||||
fact_key: str,
|
||||
user_id: str = Query(...),
|
||||
team_id: Optional[str] = None
|
||||
):
|
||||
"""Delete a fact"""
|
||||
try:
|
||||
deleted = await db.delete_fact(user_id=user_id, fact_key=fact_key, team_id=team_id)
|
||||
if not deleted:
|
||||
raise HTTPException(status_code=404, detail="Fact not found")
|
||||
return {"status": "ok", "deleted": True}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"fact_delete_failed", error=str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ADMIN
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user