Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI | |
| from fastapi.responses import HTMLResponse | |
| from pydantic import BaseModel, Field | |
| from contextlib import asynccontextmanager | |
| from typing import List, Optional | |
| from app.database import db_manager | |
| from app.agent import shl_agent | |
| # 1. Define Schemas inside main.py to ensure compliance | |
| class Recommendation(BaseModel): | |
| name: str | |
| url: str | |
| test_type: str | |
| class ChatMessage(BaseModel): | |
| role: str | |
| content: str | |
| class ChatRequest(BaseModel): | |
| # CRITICAL: The grader expects 'messages', not 'conversation' | |
| messages: List[ChatMessage] | |
| class ChatResponse(BaseModel): | |
| reply: str | |
| recommendations: List[Recommendation] = Field(default_factory=list) | |
| end_of_conversation: bool = False | |
| # 2. Lifecycle | |
| async def lifespan(app: FastAPI): | |
| catalog_file_path = "shl_product_catalog.json" | |
| db_manager.initialize_catalog(catalog_file_path) | |
| yield | |
| app = FastAPI(lifespan=lifespan) | |
| # 3. Frontend UI | |
| html_content = """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>SHL Assessment Consultant</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f4f4f9; } | |
| #chat-container { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); height: 500px; overflow-y: auto; margin-bottom: 20px; } | |
| .message { margin-bottom: 15px; padding: 10px; border-radius: 5px; } | |
| .user { background-color: #e3f2fd; text-align: right; border-left: 4px solid #2196f3; } | |
| .agent { background-color: #f1f8e9; text-align: left; border-left: 4px solid #4caf50; } | |
| .system { text-align: center; color: #888; font-style: italic; font-size: 0.9em; } | |
| table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 0.9em; } | |
| th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } | |
| th { background-color: #4caf50; color: white; } | |
| .input-area { display: flex; gap: 10px; } | |
| input[type="text"] { flex-grow: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; } | |
| button { padding: 10px 20px; background-color: #2196f3; color: white; border: none; border-radius: 4px; cursor: pointer; } | |
| </style> | |
| </head> | |
| <body> | |
| <h2>SHL Assessment Consultant</h2> | |
| <div id="chat-container"> | |
| <div class="message system">Start a conversation. E.g., "I'm hiring a Java developer."</div> | |
| </div> | |
| <div class="input-area"> | |
| <input type="text" id="user-input" placeholder="Type your message..." onkeypress="if(event.key === 'Enter') sendMessage()"> | |
| <button onclick="sendMessage()">Send</button> | |
| </div> | |
| <script> | |
| let conversationHistory = []; | |
| async function sendMessage() { | |
| const inputField = document.getElementById('user-input'); | |
| const userText = inputField.value.trim(); | |
| if (!userText) return; | |
| const chatContainer = document.getElementById('chat-container'); | |
| chatContainer.innerHTML += `<div class="message user"><strong>You:</strong> ${userText}</div>`; | |
| conversationHistory.push({ "role": "user", "content": userText }); | |
| inputField.value = ''; | |
| try { | |
| // CRITICAL: Sending 'messages' key, not 'conversation' | |
| const response = await fetch('/chat', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ messages: conversationHistory }) | |
| }); | |
| const data = await response.json(); | |
| chatContainer.innerHTML += `<div class="message agent"><strong>Consultant:</strong> ${data.reply}</div>`; | |
| conversationHistory.push({ "role": "assistant", "content": data.reply }); | |
| if (data.end_of_conversation) { | |
| conversationHistory = []; | |
| } | |
| } catch (error) { | |
| chatContainer.innerHTML += `<div class="message system" style="color:red;">Error.</div>`; | |
| } | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| async def get_frontend(): | |
| return html_content | |
| def health_check(): | |
| return {"status": "ok"} | |
| def chat_endpoint(request: ChatRequest): | |
| try: | |
| # CRITICAL: Processing 'messages' not 'conversation' | |
| response_dict = shl_agent.handle_conversation(request.messages) | |
| return ChatResponse(**response_dict) | |
| except Exception as e: | |
| return ChatResponse( | |
| reply="I ran into an issue — could you rephrase it?", | |
| recommendations=[], | |
| end_of_conversation=False | |
| ) |