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 |
|