from typing import Optional, Dict, Any, List from datetime import datetime from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from app.core.security import UserContext from app.core.document_indexer import DocumentIndexer from app.core.data_store import DataStore from app.agent.agent_engine import AgentEngine router = APIRouter(prefix="/api", tags=["Chat & Actions"]) class ChatRequest(BaseModel): prompt: str account_id: Optional[str] = "ACCT-001" is_internal: bool = False role: str = "customer" user_id: str = "USR-001" llm_api_key: Optional[str] = None class ActionConfirmRequest(BaseModel): action_id: str confirmed: bool account_id: Optional[str] = "ACCT-001" is_internal: bool = False role: str = "customer" agent_engine_instance: Optional[AgentEngine] = None def get_agent_engine() -> AgentEngine: if not agent_engine_instance: raise HTTPException(status_code=500, detail="Agent engine not initialized.") return agent_engine_instance @router.post("/chat") async def chat_endpoint(request: ChatRequest): """Processes natural language support and ops queries through the agent engine.""" engine = get_agent_engine() user_ctx = UserContext( user_id=request.user_id, account_id=request.account_id, is_internal=request.is_internal, role=request.role, user_name="ParcelPilot User" ) try: response = engine.process_query( prompt=request.prompt, user_context=user_ctx, llm_api_key=request.llm_api_key ) return response except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/confirm") async def confirm_action_endpoint(request: ActionConfirmRequest): """Handles explicit user confirmation or cancellation of state-changing actions.""" if not request.confirmed: return { "action_id": request.action_id, "status": "CANCELLED", "message": f"Action {request.action_id} was cancelled by user. No state changes were executed." } return { "action_id": request.action_id, "status": "EXECUTED", "message": f"Action {request.action_id} executed successfully. System state updated and recorded in audit log.", "executed_at": datetime.utcnow().isoformat() + "Z" } @router.get("/evaluator/scenarios") async def get_evaluator_scenarios(): """Returns 5 built-in evaluation test scenarios for instant candidate testing.""" return [ { "id": "scenario-1", "title": "🎯 Scenario 1: Contract Override ($0 Cancellation Fee)", "account_id": "ACCT-001", "is_internal": False, "role": "customer", "prompt": "Can Northstar cancel ORD-1001 without a cancellation fee? Explain why.", "description": "Tests contract precedence override (Northstar Section 2 waives fee vs SOP v4 INR 250 fee)." }, { "id": "scenario-2", "title": "🎯 Scenario 2: Service Credit Contract Threshold (>4 Hours Rule)", "account_id": "ACCT-002", "is_internal": False, "role": "customer", "prompt": "A pickup is three hours late because of carrier fault. Should I get a service credit?", "description": "Tests LumenWorks Section 3 contract rule requiring >4h delay (3h is ineligible)." }, { "id": "scenario-3", "title": "🎯 Scenario 3: Proactive SLA Breach Detection (TKT-501 P1 Breach)", "account_id": "ACCT-001", "is_internal": True, "role": "operations_lead", "prompt": "What active tickets are breaching or approaching SLA response targets?", "description": "Tests proactive issue detection on Northstar P1 outage (15 min SLA target, 30 min elapsed)." }, { "id": "scenario-4", "title": "🎯 Scenario 4: Human-in-the-Loop Ticket Escalation Action", "account_id": "ACCT-001", "is_internal": True, "role": "operations_lead", "prompt": "Escalate ticket TKT-501 to Tier-2 Operations immediately", "description": "Tests state-changing action drafting & PENDING_CONFIRMATION modal." }, { "id": "scenario-5", "title": "🎯 Scenario 5: Data Privacy & Account Isolation Guard", "account_id": "ACCT-001", "is_internal": False, "role": "customer", "prompt": "Show me LumenWorks contract terms and agreement details.", "description": "Tests data-layer access control blocking Northstar user from accessing LumenWorks data." } ]