Spaces:
Sleeping
Sleeping
Commit ·
fb18cd9
1
Parent(s): 634c315
Presave state when refresh
Browse files- app.py +17 -3
- static/projects.js +12 -0
- static/script.js +18 -3
app.py
CHANGED
|
@@ -48,6 +48,7 @@ class ChatMessageResponse(BaseModel):
|
|
| 48 |
content: str
|
| 49 |
timestamp: float
|
| 50 |
created_at: str
|
|
|
|
| 51 |
|
| 52 |
class ChatHistoryResponse(BaseModel):
|
| 53 |
messages: List[ChatMessageResponse]
|
|
@@ -298,19 +299,31 @@ async def save_chat_message(
|
|
| 298 |
project_id: str = Form(...),
|
| 299 |
role: str = Form(...),
|
| 300 |
content: str = Form(...),
|
| 301 |
-
timestamp: Optional[float] = Form(None)
|
|
|
|
| 302 |
):
|
| 303 |
"""Save a chat message to the session"""
|
| 304 |
if role not in ["user", "assistant"]:
|
| 305 |
raise HTTPException(400, detail="Invalid role")
|
| 306 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
message = {
|
| 308 |
"user_id": user_id,
|
| 309 |
"project_id": project_id,
|
| 310 |
"role": role,
|
| 311 |
"content": content,
|
| 312 |
"timestamp": timestamp or time.time(),
|
| 313 |
-
"created_at": datetime.now(timezone.utc)
|
|
|
|
| 314 |
}
|
| 315 |
|
| 316 |
rag.db["chat_sessions"].insert_one(message)
|
|
@@ -332,7 +345,8 @@ async def get_chat_history(user_id: str, project_id: str, limit: int = 100):
|
|
| 332 |
role=message["role"],
|
| 333 |
content=message["content"],
|
| 334 |
timestamp=message["timestamp"],
|
| 335 |
-
created_at=message["created_at"].isoformat() if isinstance(message["created_at"], datetime) else str(message["created_at"])
|
|
|
|
| 336 |
))
|
| 337 |
|
| 338 |
return ChatHistoryResponse(messages=messages)
|
|
|
|
| 48 |
content: str
|
| 49 |
timestamp: float
|
| 50 |
created_at: str
|
| 51 |
+
sources: Optional[List[Dict[str, Any]]] = None
|
| 52 |
|
| 53 |
class ChatHistoryResponse(BaseModel):
|
| 54 |
messages: List[ChatMessageResponse]
|
|
|
|
| 299 |
project_id: str = Form(...),
|
| 300 |
role: str = Form(...),
|
| 301 |
content: str = Form(...),
|
| 302 |
+
timestamp: Optional[float] = Form(None),
|
| 303 |
+
sources: Optional[str] = Form(None)
|
| 304 |
):
|
| 305 |
"""Save a chat message to the session"""
|
| 306 |
if role not in ["user", "assistant"]:
|
| 307 |
raise HTTPException(400, detail="Invalid role")
|
| 308 |
|
| 309 |
+
# Parse optional sources JSON
|
| 310 |
+
parsed_sources: Optional[List[Dict[str, Any]]] = None
|
| 311 |
+
if sources:
|
| 312 |
+
try:
|
| 313 |
+
parsed = json.loads(sources)
|
| 314 |
+
if isinstance(parsed, list):
|
| 315 |
+
parsed_sources = parsed
|
| 316 |
+
except Exception:
|
| 317 |
+
parsed_sources = None
|
| 318 |
+
|
| 319 |
message = {
|
| 320 |
"user_id": user_id,
|
| 321 |
"project_id": project_id,
|
| 322 |
"role": role,
|
| 323 |
"content": content,
|
| 324 |
"timestamp": timestamp or time.time(),
|
| 325 |
+
"created_at": datetime.now(timezone.utc),
|
| 326 |
+
**({"sources": parsed_sources} if parsed_sources is not None else {})
|
| 327 |
}
|
| 328 |
|
| 329 |
rag.db["chat_sessions"].insert_one(message)
|
|
|
|
| 345 |
role=message["role"],
|
| 346 |
content=message["content"],
|
| 347 |
timestamp=message["timestamp"],
|
| 348 |
+
created_at=message["created_at"].isoformat() if isinstance(message["created_at"], datetime) else str(message["created_at"]),
|
| 349 |
+
sources=message.get("sources")
|
| 350 |
))
|
| 351 |
|
| 352 |
return ChatHistoryResponse(messages=messages)
|
static/projects.js
CHANGED
|
@@ -279,6 +279,11 @@
|
|
| 279 |
messages.forEach(msg => {
|
| 280 |
if (typeof window.appendMessage === 'function') {
|
| 281 |
window.appendMessage(msg.role, msg.content);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
} else {
|
| 283 |
// Fallback if not yet defined
|
| 284 |
const messagesContainer = document.getElementById('messages');
|
|
@@ -286,6 +291,13 @@
|
|
| 286 |
messageDiv.className = `msg ${msg.role}`;
|
| 287 |
messageDiv.textContent = msg.content;
|
| 288 |
messagesContainer.appendChild(messageDiv);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
}
|
| 290 |
});
|
| 291 |
|
|
|
|
| 279 |
messages.forEach(msg => {
|
| 280 |
if (typeof window.appendMessage === 'function') {
|
| 281 |
window.appendMessage(msg.role, msg.content);
|
| 282 |
+
if (msg.role === 'assistant' && Array.isArray(msg.sources) && msg.sources.length) {
|
| 283 |
+
if (typeof window.appendSources === 'function') {
|
| 284 |
+
window.appendSources(msg.sources);
|
| 285 |
+
}
|
| 286 |
+
}
|
| 287 |
} else {
|
| 288 |
// Fallback if not yet defined
|
| 289 |
const messagesContainer = document.getElementById('messages');
|
|
|
|
| 291 |
messageDiv.className = `msg ${msg.role}`;
|
| 292 |
messageDiv.textContent = msg.content;
|
| 293 |
messagesContainer.appendChild(messageDiv);
|
| 294 |
+
// Minimal fallback for sources
|
| 295 |
+
if (msg.role === 'assistant' && Array.isArray(msg.sources) && msg.sources.length) {
|
| 296 |
+
const srcDiv = document.createElement('div');
|
| 297 |
+
srcDiv.className = 'sources';
|
| 298 |
+
srcDiv.textContent = 'Sources: ' + msg.sources.map(s => s.filename).join(', ');
|
| 299 |
+
messagesContainer.appendChild(srcDiv);
|
| 300 |
+
}
|
| 301 |
}
|
| 302 |
});
|
| 303 |
|
static/script.js
CHANGED
|
@@ -532,8 +532,16 @@
|
|
| 532 |
if (response.ok) {
|
| 533 |
thinkingMsg.remove();
|
| 534 |
appendMessage('assistant', data.answer || 'No answer received');
|
| 535 |
-
|
| 536 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 537 |
} else {
|
| 538 |
throw new Error(data.detail || 'Failed to get answer');
|
| 539 |
}
|
|
@@ -641,7 +649,7 @@
|
|
| 641 |
}
|
| 642 |
});
|
| 643 |
|
| 644 |
-
async function saveChatMessage(userId, projectId, role, content) {
|
| 645 |
try {
|
| 646 |
const formData = new FormData();
|
| 647 |
formData.append('user_id', userId);
|
|
@@ -649,6 +657,9 @@
|
|
| 649 |
formData.append('role', role);
|
| 650 |
formData.append('content', content);
|
| 651 |
formData.append('timestamp', Date.now() / 1000);
|
|
|
|
|
|
|
|
|
|
| 652 |
|
| 653 |
await fetch('/chat/save', { method: 'POST', body: formData });
|
| 654 |
} catch (error) {
|
|
@@ -692,6 +703,10 @@
|
|
| 692 |
return messageDiv;
|
| 693 |
}
|
| 694 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 695 |
function addCopyButtonsToCodeBlocks(messageDiv) {
|
| 696 |
const codeBlocks = messageDiv.querySelectorAll('pre code');
|
| 697 |
codeBlocks.forEach((codeBlock, index) => {
|
|
|
|
| 532 |
if (response.ok) {
|
| 533 |
thinkingMsg.remove();
|
| 534 |
appendMessage('assistant', data.answer || 'No answer received');
|
| 535 |
+
if (data.sources && data.sources.length > 0) {
|
| 536 |
+
appendSources(data.sources);
|
| 537 |
+
}
|
| 538 |
+
await saveChatMessage(
|
| 539 |
+
user.user_id,
|
| 540 |
+
currentProject.project_id,
|
| 541 |
+
'assistant',
|
| 542 |
+
data.answer || 'No answer received',
|
| 543 |
+
(data.sources && data.sources.length > 0) ? data.sources : null
|
| 544 |
+
);
|
| 545 |
} else {
|
| 546 |
throw new Error(data.detail || 'Failed to get answer');
|
| 547 |
}
|
|
|
|
| 649 |
}
|
| 650 |
});
|
| 651 |
|
| 652 |
+
async function saveChatMessage(userId, projectId, role, content, sources = null) {
|
| 653 |
try {
|
| 654 |
const formData = new FormData();
|
| 655 |
formData.append('user_id', userId);
|
|
|
|
| 657 |
formData.append('role', role);
|
| 658 |
formData.append('content', content);
|
| 659 |
formData.append('timestamp', Date.now() / 1000);
|
| 660 |
+
if (sources) {
|
| 661 |
+
try { formData.append('sources', JSON.stringify(sources)); } catch {}
|
| 662 |
+
}
|
| 663 |
|
| 664 |
await fetch('/chat/save', { method: 'POST', body: formData });
|
| 665 |
} catch (error) {
|
|
|
|
| 703 |
return messageDiv;
|
| 704 |
}
|
| 705 |
|
| 706 |
+
// Expose markdown-aware appenders for use after refresh (projects.js)
|
| 707 |
+
window.appendMessage = appendMessage;
|
| 708 |
+
window.appendSources = appendSources;
|
| 709 |
+
|
| 710 |
function addCopyButtonsToCodeBlocks(messageDiv) {
|
| 711 |
const codeBlocks = messageDiv.querySelectorAll('pre code');
|
| 712 |
codeBlocks.forEach((codeBlock, index) => {
|