LiamKhoaLe commited on
Commit
fb18cd9
·
1 Parent(s): 634c315

Presave state when refresh

Browse files
Files changed (3) hide show
  1. app.py +17 -3
  2. static/projects.js +12 -0
  3. 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
- await saveChatMessage(user.user_id, currentProject.project_id, 'assistant', data.answer || 'No answer received');
536
- if (data.sources && data.sources.length > 0) appendSources(data.sources);
 
 
 
 
 
 
 
 
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) => {