Spaces:
Running on Zero
Running on Zero
| import asyncio | |
| from fastapi import FastAPI, HTTPException, Security, Depends | |
| from fastapi.security.api_key import APIKeyHeader | |
| from pydantic import BaseModel, Field | |
| from typing import List, Optional, Dict, Any | |
| from starlette.status import HTTP_403_FORBIDDEN, HTTP_429_TOO_MANY_REQUESTS | |
| from evaluator import evaluate_trajectories_batch | |
| from report_generator import generate_ai_report | |
| from database import verify_api_key_db, save_evaluation_to_db | |
| app = FastAPI( | |
| title="Limina AI API", | |
| description="High-performance infrastructure for multi-turn AI agent evaluation", | |
| version="1.0.0" | |
| ) | |
| API_KEY_NAME = "X-API-Key" | |
| api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False) | |
| async def get_api_key_context(header: str = Security(api_key_header)) -> Dict[str, Any]: | |
| if not header: | |
| raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Missing API Key (X-API-Key).") | |
| auth_context = verify_api_key_db(header) | |
| if not auth_context: | |
| raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Access Denied. Invalid API Key.") | |
| if auth_context.get("quota_exceeded"): | |
| raise HTTPException( | |
| status_code=HTTP_429_TOO_MANY_REQUESTS, | |
| detail=f"Monthly Free Quota Exceeded ({auth_context['usage']}/{auth_context['limit']}). Upgrade to Limina Pro." | |
| ) | |
| return auth_context | |
| cpu_semaphore = asyncio.Semaphore(4) | |
| class NodeInput(BaseModel): | |
| id: str | |
| type: str | |
| text: str | |
| expected_keys: Optional[List[str]] = None | |
| runs: Optional[List[str]] = None | |
| execution_time_ms: Optional[float] = None | |
| class EdgeInput(BaseModel): | |
| from_node: str = Field(..., alias="from") | |
| to_node: str = Field(..., alias="to") | |
| class Config: | |
| allow_population_by_alias = True | |
| class TrajectoryInput(BaseModel): | |
| session_id: str | |
| description: str | |
| nodes: List[NodeInput] | |
| edges: List[EdgeInput] | |
| class EvaluationReport(BaseModel): | |
| executive_summary: dict | |
| regression_report: dict | |
| results: list | |
| narrative_report: str | |
| async def read_root(): | |
| return { | |
| "status": "online", | |
| "engine": "Limina AI Trajectory Engine", | |
| "version": "1.0.0", | |
| "cloud_sync": "active" | |
| } | |
| async def evaluate_trajectory( | |
| payload: List[TrajectoryInput], | |
| auth_ctx: Dict[str, Any] = Depends(get_api_key_context) | |
| ): | |
| if not payload: | |
| raise HTTPException(status_code=400, detail="Payload is empty.") | |
| raw_payload = [p.dict(by_alias=True) for p in payload] | |
| async with cpu_semaphore: | |
| report = await asyncio.to_thread(evaluate_trajectories_batch, raw_payload, "standard", False) | |
| if not report: | |
| raise HTTPException(status_code=500, detail="Evaluation engine failed.") | |
| ai_markdown_report = await asyncio.to_thread(generate_ai_report, report) | |
| report["narrative_report"] = ai_markdown_report | |
| if auth_ctx.get("project_id"): | |
| asyncio.create_task( | |
| asyncio.to_thread(save_evaluation_to_db, auth_ctx["project_id"], report) | |
| ) | |
| return report |