Spaces:
Sleeping
Sleeping
File size: 8,480 Bytes
2598850 e300631 2496d5a e300631 2598850 b98b0d0 2496d5a 2598850 2496d5a e300631 2496d5a b98b0d0 2496d5a 2598850 e300631 e2a5449 b98b0d0 e2a5449 b98b0d0 e2a5449 b98b0d0 e2a5449 b98b0d0 2496d5a e2a5449 b98b0d0 2496d5a 2598850 2496d5a 2598850 b98b0d0 e2a5449 b98b0d0 e2a5449 b98b0d0 e2a5449 b98b0d0 e300631 b98b0d0 e2a5449 b98b0d0 e2a5449 b98b0d0 e2a5449 b98b0d0 e2a5449 e300631 b98b0d0 2496d5a b98b0d0 2496d5a b98b0d0 42608e5 b98b0d0 e2a5449 b98b0d0 e2a5449 2598850 d0dbd33 2598850 a8a080c 2496d5a 2598850 2496d5a b98b0d0 2496d5a b98b0d0 2496d5a b98b0d0 2496d5a 1a79b7b 2496d5a b98b0d0 e2a5449 2496d5a b98b0d0 d0dbd33 b98b0d0 e2a5449 2598850 2496d5a e2a5449 b98b0d0 d0dbd33 e2a5449 2598850 2496d5a e2a5449 b98b0d0 e2a5449 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
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 ---
@app.middleware("http")
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 ---
@app.exception_handler(RequestValidationError)
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 ---
@app.get("/debug/logs")
async def get_debug_logs():
"""Endpoint to view the last 50 requests and responses."""
return {
"count": len(DEBUG_LOGS),
"logs": DEBUG_LOGS
}
@app.post("/api/honeypot-detection", response_model=HoneypotResponse)
@app.post("/api/honeypot-detection/", response_model=HoneypotResponse)
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)
@app.get("/")
async def root():
return {
"message": "Agentic Honey-Pot API is running.",
"endpoints": {
"detection": "/api/honeypot-detection",
"debug_logs": "/debug/logs"
}
}
|