Update app.py
Browse files
app.py
CHANGED
|
@@ -82,10 +82,14 @@ class InitResponse(BaseModel):
|
|
| 82 |
session_id: str
|
| 83 |
status: str
|
| 84 |
|
| 85 |
-
# Simple HTML interface
|
| 86 |
@app.get("/", response_class=HTMLResponse)
|
| 87 |
async def root():
|
| 88 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
html_content = f"""
|
| 90 |
<!DOCTYPE html>
|
| 91 |
<html>
|
|
@@ -94,7 +98,7 @@ async def root():
|
|
| 94 |
<style>
|
| 95 |
body {{
|
| 96 |
font-family: Arial, sans-serif;
|
| 97 |
-
max-width:
|
| 98 |
margin: 0 auto;
|
| 99 |
padding: 20px;
|
| 100 |
line-height: 1.6;
|
|
@@ -133,19 +137,36 @@ async def root():
|
|
| 133 |
button:hover {{
|
| 134 |
background-color: #45a049;
|
| 135 |
}}
|
| 136 |
-
#response {{
|
| 137 |
white-space: pre-wrap;
|
| 138 |
background-color: #f5f5f5;
|
| 139 |
padding: 15px;
|
| 140 |
border-radius: 4px;
|
| 141 |
-
margin-top: 20px;
|
| 142 |
min-height: 100px;
|
| 143 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
.log {{
|
| 145 |
margin-top: 20px;
|
| 146 |
font-size: 0.9em;
|
| 147 |
color: #666;
|
| 148 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
</style>
|
| 150 |
</head>
|
| 151 |
<body>
|
|
@@ -154,7 +175,6 @@ async def root():
|
|
| 154 |
<div id="init-form">
|
| 155 |
<h2>1. Initialize Chat</h2>
|
| 156 |
|
| 157 |
-
<!-- API key input removed -->
|
| 158 |
<label for="model">Model:</label>
|
| 159 |
<input type="text" id="model" value="mistralai/mistral-small-3.1-24b-instruct:free">
|
| 160 |
|
|
@@ -181,12 +201,22 @@ async def root():
|
|
| 181 |
<button onclick="resetChat()" style="background-color: #f44336;">Reset</button>
|
| 182 |
</div>
|
| 183 |
|
| 184 |
-
<div id="response-container" style="display: none;">
|
| 185 |
-
|
| 186 |
-
<div
|
| 187 |
-
|
| 188 |
-
<
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
</div>
|
| 191 |
</div>
|
| 192 |
</div>
|
|
@@ -198,7 +228,8 @@ async def root():
|
|
| 198 |
|
| 199 |
<script>
|
| 200 |
let currentSessionId = null;
|
| 201 |
-
|
|
|
|
| 202 |
async function initializeChat() {{
|
| 203 |
const model = document.getElementById('model').value;
|
| 204 |
const temperature = parseFloat(document.getElementById('temperature').value);
|
|
@@ -222,7 +253,10 @@ async def root():
|
|
| 222 |
document.getElementById('session-id').textContent = currentSessionId;
|
| 223 |
document.getElementById('init-form').style.display = 'none';
|
| 224 |
document.getElementById('chat-form').style.display = 'block';
|
| 225 |
-
document.getElementById('response-container').style.display = '
|
|
|
|
|
|
|
|
|
|
| 226 |
}} else {{
|
| 227 |
alert('Initialization failed: ' + (data.detail || 'Unknown error'));
|
| 228 |
}}
|
|
@@ -230,7 +264,71 @@ async def root():
|
|
| 230 |
alert('An error occurred: ' + error.message);
|
| 231 |
}}
|
| 232 |
}}
|
| 233 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
async function sendMessage() {{
|
| 235 |
if (!currentSessionId) {{
|
| 236 |
alert('Please initialize a chat session first.');
|
|
@@ -246,51 +344,55 @@ async def root():
|
|
| 246 |
return;
|
| 247 |
}}
|
| 248 |
|
| 249 |
-
|
|
|
|
|
|
|
| 250 |
document.getElementById('thinking-log').textContent = '';
|
| 251 |
-
|
|
|
|
|
|
|
| 252 |
try {{
|
| 253 |
-
const
|
| 254 |
method: 'POST',
|
| 255 |
headers: {{
|
| 256 |
'Content-Type': 'application/json',
|
| 257 |
}},
|
| 258 |
body: JSON.stringify({{
|
| 259 |
session_id: currentSessionId,
|
| 260 |
-
message: message
|
| 261 |
-
thinking_rounds: thinkingRounds ? parseInt(thinkingRounds) : null,
|
| 262 |
-
alternatives_per_round: alternatives ? parseInt(alternatives) : 3
|
| 263 |
}}),
|
| 264 |
}});
|
| 265 |
-
|
| 266 |
-
const
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
document.getElementById('response').textContent = data.response;
|
| 270 |
-
|
| 271 |
-
// Display thinking history
|
| 272 |
-
let thinkingLogHTML = '';
|
| 273 |
-
data.thinking_history.forEach(item => {{
|
| 274 |
-
const selected = item.selected ? ' ✓ Selected' : '';
|
| 275 |
-
thinkingLogHTML += "<p><strong>Round " + item.round + selected + ":</strong> ";
|
| 276 |
-
|
| 277 |
-
if (item.explanation && item.selected) {{
|
| 278 |
-
thinkingLogHTML += "<br><em>Reason for selection: " + item.explanation + "</em>";
|
| 279 |
-
}}
|
| 280 |
-
thinkingLogHTML += "</p>";
|
| 281 |
-
}});
|
| 282 |
-
|
| 283 |
-
document.getElementById('thinking-log').innerHTML = thinkingLogHTML;
|
| 284 |
}} else {{
|
| 285 |
-
document.getElementById('response').textContent = 'Error: ' + (
|
| 286 |
}}
|
| 287 |
}} catch (error) {{
|
| 288 |
-
document.getElementById('response').textContent = '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
}}
|
| 290 |
}}
|
| 291 |
|
| 292 |
function resetChat() {{
|
| 293 |
currentSessionId = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
document.getElementById('init-form').style.display = 'block';
|
| 295 |
document.getElementById('chat-form').style.display = 'none';
|
| 296 |
document.getElementById('response-container').style.display = 'none';
|
|
@@ -317,7 +419,7 @@ async def initialize_chat(config: ChatConfig):
|
|
| 317 |
# Generate a session ID
|
| 318 |
session_id = f"session_{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}"
|
| 319 |
|
| 320 |
-
# If the environment variable is missing, raise an error
|
| 321 |
if not API_KEY:
|
| 322 |
raise HTTPException(status_code=400, detail="The OPENROUTE_API environment variable is not set.")
|
| 323 |
|
|
@@ -338,9 +440,29 @@ async def initialize_chat(config: ChatConfig):
|
|
| 338 |
logger.error(f"Error initializing chat: {str(e)}")
|
| 339 |
raise HTTPException(status_code=500, detail=f"Failed to initialize chat: {str(e)}")
|
| 340 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
@app.post("/api/send_message")
|
| 342 |
async def send_message(request: MessageRequest):
|
| 343 |
-
"""Send a message and get a response with the
|
| 344 |
try:
|
| 345 |
if request.session_id not in chat_instances:
|
| 346 |
raise HTTPException(status_code=404, detail="Session not found")
|
|
@@ -558,7 +680,7 @@ async def websocket_endpoint(websocket: WebSocket, session_id: str):
|
|
| 558 |
"message": "Starting recursive thinking process..."
|
| 559 |
})
|
| 560 |
|
| 561 |
-
# Process the message
|
| 562 |
result = chat.think_and_respond(message_data["content"], verbose=True)
|
| 563 |
processing_time = (datetime.now() - start_time).total_seconds()
|
| 564 |
|
|
|
|
| 82 |
session_id: str
|
| 83 |
status: str
|
| 84 |
|
| 85 |
+
# Simple HTML interface
|
| 86 |
@app.get("/", response_class=HTMLResponse)
|
| 87 |
async def root():
|
| 88 |
+
"""
|
| 89 |
+
Root endpoint with a simple HTML interface.
|
| 90 |
+
Modified to display side-by-side "Original" vs. "Chain-of-Thought" outputs,
|
| 91 |
+
and to show real-time "thinking in progress" messages.
|
| 92 |
+
"""
|
| 93 |
html_content = f"""
|
| 94 |
<!DOCTYPE html>
|
| 95 |
<html>
|
|
|
|
| 98 |
<style>
|
| 99 |
body {{
|
| 100 |
font-family: Arial, sans-serif;
|
| 101 |
+
max-width: 1000px;
|
| 102 |
margin: 0 auto;
|
| 103 |
padding: 20px;
|
| 104 |
line-height: 1.6;
|
|
|
|
| 137 |
button:hover {{
|
| 138 |
background-color: #45a049;
|
| 139 |
}}
|
| 140 |
+
#original-response, #chain-response {{
|
| 141 |
white-space: pre-wrap;
|
| 142 |
background-color: #f5f5f5;
|
| 143 |
padding: 15px;
|
| 144 |
border-radius: 4px;
|
|
|
|
| 145 |
min-height: 100px;
|
| 146 |
}}
|
| 147 |
+
.responses-container {{
|
| 148 |
+
display: flex;
|
| 149 |
+
gap: 20px;
|
| 150 |
+
margin-top: 20px;
|
| 151 |
+
}}
|
| 152 |
+
.response-column {{
|
| 153 |
+
flex: 1;
|
| 154 |
+
display: flex;
|
| 155 |
+
flex-direction: column;
|
| 156 |
+
}}
|
| 157 |
+
.column-title {{
|
| 158 |
+
margin-top: 0;
|
| 159 |
+
}}
|
| 160 |
.log {{
|
| 161 |
margin-top: 20px;
|
| 162 |
font-size: 0.9em;
|
| 163 |
color: #666;
|
| 164 |
}}
|
| 165 |
+
.thinking-progress {{
|
| 166 |
+
margin-top: 10px;
|
| 167 |
+
font-style: italic;
|
| 168 |
+
color: #555;
|
| 169 |
+
}}
|
| 170 |
</style>
|
| 171 |
</head>
|
| 172 |
<body>
|
|
|
|
| 175 |
<div id="init-form">
|
| 176 |
<h2>1. Initialize Chat</h2>
|
| 177 |
|
|
|
|
| 178 |
<label for="model">Model:</label>
|
| 179 |
<input type="text" id="model" value="mistralai/mistral-small-3.1-24b-instruct:free">
|
| 180 |
|
|
|
|
| 201 |
<button onclick="resetChat()" style="background-color: #f44336;">Reset</button>
|
| 202 |
</div>
|
| 203 |
|
| 204 |
+
<div class="responses-container" id="response-container" style="display: none;">
|
| 205 |
+
<!-- Original Response Column -->
|
| 206 |
+
<div class="response-column">
|
| 207 |
+
<h2 class="column-title">Original (No Chain-of-Thought)</h2>
|
| 208 |
+
<div id="original-response">The original LLM response will appear here...</div>
|
| 209 |
+
</div>
|
| 210 |
+
|
| 211 |
+
<!-- Chain-of-Thought Response Column -->
|
| 212 |
+
<div class="response-column">
|
| 213 |
+
<h2 class="column-title">Chain-of-Thought</h2>
|
| 214 |
+
<div id="chain-thinking-progress" class="thinking-progress"></div>
|
| 215 |
+
<div id="chain-response">The chain-of-thought response will appear here...</div>
|
| 216 |
+
<div class="log">
|
| 217 |
+
<h3>Thinking Process Log:</h3>
|
| 218 |
+
<div id="thinking-log"></div>
|
| 219 |
+
</div>
|
| 220 |
</div>
|
| 221 |
</div>
|
| 222 |
</div>
|
|
|
|
| 228 |
|
| 229 |
<script>
|
| 230 |
let currentSessionId = null;
|
| 231 |
+
let ws = null; // WebSocket for Chain-of-Thought
|
| 232 |
+
|
| 233 |
async function initializeChat() {{
|
| 234 |
const model = document.getElementById('model').value;
|
| 235 |
const temperature = parseFloat(document.getElementById('temperature').value);
|
|
|
|
| 253 |
document.getElementById('session-id').textContent = currentSessionId;
|
| 254 |
document.getElementById('init-form').style.display = 'none';
|
| 255 |
document.getElementById('chat-form').style.display = 'block';
|
| 256 |
+
document.getElementById('response-container').style.display = 'flex';
|
| 257 |
+
|
| 258 |
+
// Connect to WebSocket for chain-of-thought streaming
|
| 259 |
+
connectWebSocket();
|
| 260 |
}} else {{
|
| 261 |
alert('Initialization failed: ' + (data.detail || 'Unknown error'));
|
| 262 |
}}
|
|
|
|
| 264 |
alert('An error occurred: ' + error.message);
|
| 265 |
}}
|
| 266 |
}}
|
| 267 |
+
|
| 268 |
+
function connectWebSocket() {{
|
| 269 |
+
if (!currentSessionId) {{
|
| 270 |
+
return;
|
| 271 |
+
}}
|
| 272 |
+
const loc = window.location;
|
| 273 |
+
let wsUrl = '';
|
| 274 |
+
if (loc.protocol === 'https:') {{
|
| 275 |
+
wsUrl = 'wss:';
|
| 276 |
+
}} else {{
|
| 277 |
+
wsUrl = 'ws:';
|
| 278 |
+
}}
|
| 279 |
+
wsUrl += '//' + loc.host + '/ws/' + currentSessionId;
|
| 280 |
+
ws = new WebSocket(wsUrl);
|
| 281 |
+
|
| 282 |
+
ws.onopen = function() {{
|
| 283 |
+
console.log('WebSocket connection opened for chain-of-thought.');
|
| 284 |
+
}};
|
| 285 |
+
|
| 286 |
+
ws.onmessage = function(event) {{
|
| 287 |
+
const data = JSON.parse(event.data);
|
| 288 |
+
|
| 289 |
+
if (data.type === 'status') {{
|
| 290 |
+
// Show "thinking in progress" messages
|
| 291 |
+
document.getElementById('chain-thinking-progress').textContent = data.message;
|
| 292 |
+
}} else if (data.type === 'chunk') {{
|
| 293 |
+
// In this example code, "chunk" is not truly partial, but let's append anyway
|
| 294 |
+
document.getElementById('chain-thinking-progress').textContent += '\\n' + data.content;
|
| 295 |
+
}} else if (data.type === 'final') {{
|
| 296 |
+
// Final answer
|
| 297 |
+
document.getElementById('chain-thinking-progress').textContent = '';
|
| 298 |
+
document.getElementById('chain-response').textContent = data.response;
|
| 299 |
+
|
| 300 |
+
// Display thinking history
|
| 301 |
+
const thinkingLog = document.getElementById('thinking-log');
|
| 302 |
+
thinkingLog.innerHTML = '';
|
| 303 |
+
data.thinking_history.forEach(item => {{
|
| 304 |
+
const selected = item.selected ? ' ✓ Selected' : '';
|
| 305 |
+
let logEntry = document.createElement('p');
|
| 306 |
+
logEntry.innerHTML = '<strong>Round ' + item.round + selected + ':</strong> ';
|
| 307 |
+
if (item.explanation && item.selected) {{
|
| 308 |
+
logEntry.innerHTML += '<br><em>Reason for selection: ' + item.explanation + '</em>';
|
| 309 |
+
}}
|
| 310 |
+
thinkingLog.appendChild(logEntry);
|
| 311 |
+
}});
|
| 312 |
+
}} else if (data.type === 'error') {{
|
| 313 |
+
document.getElementById('chain-thinking-progress').textContent = '';
|
| 314 |
+
document.getElementById('chain-response').textContent = 'Error: ' + data.error;
|
| 315 |
+
}} else if (data.error) {{
|
| 316 |
+
// Session not found or other errors
|
| 317 |
+
document.getElementById('chain-thinking-progress').textContent = '';
|
| 318 |
+
document.getElementById('chain-response').textContent = 'Error: ' + data.error;
|
| 319 |
+
}}
|
| 320 |
+
}};
|
| 321 |
+
|
| 322 |
+
ws.onerror = function(error) {{
|
| 323 |
+
console.error('WebSocket error:', error);
|
| 324 |
+
document.getElementById('chain-thinking-progress').textContent = 'WebSocket error. Check console.';
|
| 325 |
+
}};
|
| 326 |
+
|
| 327 |
+
ws.onclose = function() {{
|
| 328 |
+
console.log('WebSocket closed.');
|
| 329 |
+
}};
|
| 330 |
+
}}
|
| 331 |
+
|
| 332 |
async function sendMessage() {{
|
| 333 |
if (!currentSessionId) {{
|
| 334 |
alert('Please initialize a chat session first.');
|
|
|
|
| 344 |
return;
|
| 345 |
}}
|
| 346 |
|
| 347 |
+
// Clear out previous responses/log
|
| 348 |
+
document.getElementById('original-response').textContent = 'Loading original response...';
|
| 349 |
+
document.getElementById('chain-response').textContent = '';
|
| 350 |
document.getElementById('thinking-log').textContent = '';
|
| 351 |
+
document.getElementById('chain-thinking-progress').textContent = 'Thinking...';
|
| 352 |
+
|
| 353 |
+
// 1) Get Original Response via standard fetch
|
| 354 |
try {{
|
| 355 |
+
const originalRes = await fetch('/api/send_message_original', {{
|
| 356 |
method: 'POST',
|
| 357 |
headers: {{
|
| 358 |
'Content-Type': 'application/json',
|
| 359 |
}},
|
| 360 |
body: JSON.stringify({{
|
| 361 |
session_id: currentSessionId,
|
| 362 |
+
message: message
|
|
|
|
|
|
|
| 363 |
}}),
|
| 364 |
}});
|
| 365 |
+
|
| 366 |
+
const originalData = await originalRes.json();
|
| 367 |
+
if (originalRes.ok) {{
|
| 368 |
+
document.getElementById('original-response').textContent = originalData.response;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 |
}} else {{
|
| 370 |
+
document.getElementById('original-response').textContent = 'Error: ' + (originalData.detail || 'Unknown error');
|
| 371 |
}}
|
| 372 |
}} catch (error) {{
|
| 373 |
+
document.getElementById('original-response').textContent = 'Error: ' + error.message;
|
| 374 |
+
}}
|
| 375 |
+
|
| 376 |
+
// 2) Send message to WebSocket for chain-of-thought
|
| 377 |
+
if (ws && ws.readyState === WebSocket.OPEN) {{
|
| 378 |
+
const payload = {{
|
| 379 |
+
type: 'message',
|
| 380 |
+
content: message,
|
| 381 |
+
thinking_rounds: thinkingRounds ? parseInt(thinkingRounds) : null,
|
| 382 |
+
alternatives_per_round: alternatives ? parseInt(alternatives) : 3
|
| 383 |
+
}};
|
| 384 |
+
ws.send(JSON.stringify(payload));
|
| 385 |
+
}} else {{
|
| 386 |
+
document.getElementById('chain-thinking-progress').textContent = 'WebSocket not connected. Unable to get chain-of-thought response.';
|
| 387 |
}}
|
| 388 |
}}
|
| 389 |
|
| 390 |
function resetChat() {{
|
| 391 |
currentSessionId = null;
|
| 392 |
+
if (ws) {{
|
| 393 |
+
ws.close();
|
| 394 |
+
ws = null;
|
| 395 |
+
}}
|
| 396 |
document.getElementById('init-form').style.display = 'block';
|
| 397 |
document.getElementById('chat-form').style.display = 'none';
|
| 398 |
document.getElementById('response-container').style.display = 'none';
|
|
|
|
| 419 |
# Generate a session ID
|
| 420 |
session_id = f"session_{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}"
|
| 421 |
|
| 422 |
+
# If the environment variable is missing, raise an error
|
| 423 |
if not API_KEY:
|
| 424 |
raise HTTPException(status_code=400, detail="The OPENROUTE_API environment variable is not set.")
|
| 425 |
|
|
|
|
| 440 |
logger.error(f"Error initializing chat: {str(e)}")
|
| 441 |
raise HTTPException(status_code=500, detail=f"Failed to initialize chat: {str(e)}")
|
| 442 |
|
| 443 |
+
@app.post("/api/send_message_original")
|
| 444 |
+
async def send_message_original(request: MessageRequest):
|
| 445 |
+
"""
|
| 446 |
+
Return a direct LLM response without applying the chain-of-thought logic.
|
| 447 |
+
"""
|
| 448 |
+
if request.session_id not in chat_instances:
|
| 449 |
+
raise HTTPException(status_code=404, detail="Session not found")
|
| 450 |
+
|
| 451 |
+
chat = chat_instances[request.session_id]["chat"]
|
| 452 |
+
try:
|
| 453 |
+
# Make a direct call to the LLM without recursion logic
|
| 454 |
+
messages = [{"role": "user", "content": request.message}]
|
| 455 |
+
response_data = chat._call_api(messages, temperature=chat.temperature, stream=False)
|
| 456 |
+
# Extract the text from the response
|
| 457 |
+
original_text = response_data["choices"][0]["message"]["content"]
|
| 458 |
+
return {"response": original_text.strip()}
|
| 459 |
+
except Exception as e:
|
| 460 |
+
logger.error(f"Error getting original response: {str(e)}")
|
| 461 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 462 |
+
|
| 463 |
@app.post("/api/send_message")
|
| 464 |
async def send_message(request: MessageRequest):
|
| 465 |
+
"""Send a message and get a response with the chain-of-thought process (HTTP-based, not streaming)."""
|
| 466 |
try:
|
| 467 |
if request.session_id not in chat_instances:
|
| 468 |
raise HTTPException(status_code=404, detail="Session not found")
|
|
|
|
| 680 |
"message": "Starting recursive thinking process..."
|
| 681 |
})
|
| 682 |
|
| 683 |
+
# Process the message with chain-of-thought
|
| 684 |
result = chat.think_and_respond(message_data["content"], verbose=True)
|
| 685 |
processing_time = (datetime.now() - start_time).total_seconds()
|
| 686 |
|