Spaces:
Sleeping
Sleeping
| import os | |
| import time | |
| import logging | |
| import json | |
| from fastapi import FastAPI, Request, HTTPException, Depends, status | |
| from fastapi.exceptions import RequestValidationError | |
| from fastapi.responses import JSONResponse | |
| from fastapi.security import APIKeyHeader | |
| from typing import Dict, Any, Optional, List | |
| from datetime import datetime | |
| # LangGraph and Model Imports | |
| from langgraph.checkpoint.memory import MemorySaver | |
| from langgraph.checkpoint.base import BaseCheckpointSaver | |
| from agent import create_honeypot_graph | |
| from models import ( | |
| HoneypotRequest, HoneypotResponse, | |
| AgentState, ExtractedIntelligence, Message, EngagementMetrics | |
| ) | |
| # --- Logging Configuration --- | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # --- Global Debug Store --- | |
| # This will store the last 50 requests and responses in memory for easy debugging | |
| DEBUG_LOGS: List[Dict[str, Any]] = [] | |
| MAX_DEBUG_LOGS = 50 | |
| # --- Helpers (Strict Message Objects) --- | |
| def _ensure_message(obj: Any) -> Message: | |
| """Ensure conversation items are Message objects (as required by the tester contract).""" | |
| if isinstance(obj, Message): | |
| return obj | |
| if isinstance(obj, dict): | |
| return Message(**obj) | |
| raise TypeError(f"Invalid message type in conversationHistory: {type(obj)}") | |
| def _ensure_intelligence(obj: Any) -> ExtractedIntelligence: | |
| """Ensure extractedIntelligence is an ExtractedIntelligence model.""" | |
| if isinstance(obj, ExtractedIntelligence): | |
| return obj | |
| if isinstance(obj, dict): | |
| return ExtractedIntelligence(**obj) | |
| # If missing/None, return empty model | |
| return ExtractedIntelligence() | |
| def _normalize_history(history: Any) -> List[Message]: | |
| """Normalize conversationHistory to a list[Message].""" | |
| if history is None: | |
| return [] | |
| if not isinstance(history, list): | |
| raise TypeError(f"conversationHistory must be a list, got: {type(history)}") | |
| return [_ensure_message(m) for m in history] | |
| def add_debug_log(direction: str, path: str, method: str, headers: Dict[str, str], body: Any): | |
| log_entry = { | |
| "timestamp": datetime.now().isoformat(), | |
| "direction": direction, | |
| "path": path, | |
| "method": method, | |
| "headers": {k: v for k, v in headers.items() if k.lower() != "x-api-key"}, # Hide key for safety | |
| "body": body | |
| } | |
| DEBUG_LOGS.insert(0, log_entry) | |
| if len(DEBUG_LOGS) > MAX_DEBUG_LOGS: | |
| DEBUG_LOGS.pop() | |
| # --- Configuration --- | |
| API_KEY_NAME = "x-api-key" | |
| API_KEY = os.environ.get("HONEYPOT_API_KEY", "sk_test_123456789") | |
| api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False) | |
| # --- Initialization --- | |
| app = FastAPI( | |
| title="Agentic Honey-Pot API - Super Debug Mode", | |
| description="REST API for Scam Detection with enhanced logging.", | |
| version="1.3.0" | |
| ) | |
| # Initialize LangGraph Checkpointer | |
| checkpointer: BaseCheckpointSaver = MemorySaver() | |
| honeypot_app = create_honeypot_graph(checkpointer) | |
| # --- Middleware for Global Logging --- | |
| async def log_requests(request: Request, call_next): | |
| path = request.url.path | |
| method = request.method | |
| headers = dict(request.headers) | |
| # Capture request body | |
| body = None | |
| if method == "POST": | |
| try: | |
| raw_body = await request.body() | |
| body = json.loads(raw_body) | |
| except: | |
| body = "Could not parse body as JSON" | |
| add_debug_log("INCOMING", path, method, headers, body) | |
| logger.info(f"INCOMING {method} {path} | Body: {body}") | |
| start_time = time.time() | |
| response = await call_next(request) | |
| process_time = time.time() - start_time | |
| # Capture response body (this is a bit tricky in FastAPI middleware) | |
| # For simplicity, we'll log the status code here and log the actual body in the endpoint | |
| logger.info(f"OUTGOING {method} {path} | Status: {response.status_code} | Time: {process_time:.4f}s") | |
| return response | |
| # --- Exception Handler for Diagnostic Logging --- | |
| async def validation_exception_handler(request: Request, exc: RequestValidationError): | |
| body = await request.body() | |
| try: | |
| payload = json.loads(body) | |
| except: | |
| payload = body.decode() | |
| error_detail = exc.errors() | |
| logger.error(f"422 Unprocessable Entity Error! Payload: {payload} | Errors: {error_detail}") | |
| response_body = { | |
| "detail": error_detail, | |
| "received_body": payload, | |
| "message": "Validation failed. Check /debug/logs for details." | |
| } | |
| add_debug_log("OUTGOING_ERROR", request.url.path, request.method, {}, response_body) | |
| return JSONResponse(status_code=422, content=response_body) | |
| # --- Dependency for API Key Validation --- | |
| async def get_api_key(api_key_header: str = Depends(api_key_header)): | |
| if api_key_header is None or api_key_header != API_KEY: | |
| raise HTTPException( | |
| status_code=status.HTTP_401_UNAUTHORIZED, | |
| detail="Invalid API Key or missing 'x-api-key' header.", | |
| ) | |
| return api_key_header | |
| # --- API Endpoints --- | |
| async def get_debug_logs(): | |
| """Endpoint to view the last 50 requests and responses.""" | |
| return { | |
| "count": len(DEBUG_LOGS), | |
| "logs": DEBUG_LOGS | |
| } | |
| async def honeypot_detection( | |
| request: Request, | |
| request_data: HoneypotRequest, | |
| api_key: str = Depends(get_api_key) | |
| ) -> Dict[str, Any]: | |
| session_id = request_data.sessionId | |
| config = {"configurable": {"thread_id": session_id}} | |
| checkpoint = honeypot_app.get_state(config) | |
| start_time = time.time() | |
| if checkpoint and checkpoint.values: | |
| current_state_dict = checkpoint.values | |
| current_state_dict.setdefault("callbackSent", False) | |
| current_state_dict.setdefault("agentNotes", "") | |
| current_state_dict["extractedIntelligence"] = _ensure_intelligence(current_state_dict.get("extractedIntelligence")) | |
| current_state_dict["conversationHistory"] = _normalize_history(current_state_dict.get("conversationHistory")) | |
| current_state_dict.setdefault("totalMessagesExchanged", 0) | |
| current_state_dict.setdefault("sessionId", session_id) | |
| current_state = AgentState(**current_state_dict) | |
| current_state["conversationHistory"].append(request_data.message) | |
| current_state["totalMessagesExchanged"] += 1 | |
| input_state = current_state | |
| else: | |
| initial_history = request_data.conversationHistory + [request_data.message] | |
| input_state = AgentState( | |
| sessionId=session_id, | |
| conversationHistory=initial_history, | |
| scamDetected=False, | |
| extractedIntelligence=ExtractedIntelligence(), | |
| agentNotes="New session started. ", | |
| totalMessagesExchanged=len(initial_history), | |
| should_continue_engagement=False, | |
| agent_response_text="", | |
| callbackSent=False | |
| ) | |
| try: | |
| final_state_dict = honeypot_app.invoke(input_state, config=config) | |
| final_state = AgentState(**final_state_dict) | |
| engagement_duration = int(time.time() - start_time) | |
| response_content = { | |
| "status": "success", | |
| "scamDetected": final_state["scamDetected"], | |
| "engagementMetrics": { | |
| "engagementDurationSeconds": engagement_duration, | |
| "totalMessagesExchanged": final_state["totalMessagesExchanged"] | |
| }, | |
| "extractedIntelligence": final_state["extractedIntelligence"].model_dump(), | |
| "agentNotes": final_state["agentNotes"] | |
| } | |
| # Log the successful response body | |
| add_debug_log("OUTGOING_SUCCESS", str(request.url.path), "POST", {}, response_content) | |
| return response_content | |
| except Exception as e: | |
| error_msg = f"Internal Error: {str(e)}" | |
| logger.error(error_msg) | |
| add_debug_log("OUTGOING_ERROR", "/api/honeypot-detection/", "POST", {}, {"error": error_msg}) | |
| raise HTTPException(status_code=500, detail=error_msg) | |
| async def root(): | |
| return { | |
| "message": "Agentic Honey-Pot API is running.", | |
| "endpoints": { | |
| "detection": "/api/honeypot-detection", | |
| "debug_logs": "/debug/logs" | |
| } | |
| } | |