Spaces:
Sleeping
Sleeping
File size: 4,899 Bytes
ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 71a3245 ca20ec1 | 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 | 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
@asynccontextmanager
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>
"""
@app.get("/", response_class=HTMLResponse)
async def get_frontend():
return html_content
@app.get("/health")
def health_check():
return {"status": "ok"}
@app.post("/chat", response_model=ChatResponse)
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
) |