|
2 | 2 | import json |
3 | 3 | import time |
4 | 4 | import uuid |
5 | | -from typing import Optional |
| 5 | +from typing import Optional, Callable |
6 | 6 |
|
7 | 7 | from sqlalchemy.orm import Session |
8 | 8 | from open_webui.internal.db import Base, JSONField, get_db, get_db_context |
@@ -462,62 +462,89 @@ def get_message_by_id_and_message_id(self, id: str, message_id: str) -> Optional |
462 | 462 | return chat.chat.get('history', {}).get('messages', {}).get(message_id, {}) |
463 | 463 |
|
464 | 464 | def upsert_message_to_chat_by_id_and_message_id( |
465 | | - self, id: str, message_id: str, message: dict |
| 465 | + self, |
| 466 | + id: str, |
| 467 | + message_id: str, |
| 468 | + message: dict, |
| 469 | + merge_fn: Optional[Callable[[dict], dict]] = None, |
466 | 470 | ) -> Optional[ChatModel]: |
467 | | - chat = self.get_chat_by_id(id) |
468 | | - if chat is None: |
469 | | - return None |
| 471 | + try: |
| 472 | + with get_db_context() as db: |
| 473 | + chat_item = db.get(Chat, id) |
| 474 | + if chat_item is None: |
| 475 | + return None |
470 | 476 |
|
471 | | - # Sanitize message content for null characters before upserting |
472 | | - if isinstance(message.get('content'), str): |
473 | | - message['content'] = sanitize_text_for_db(message['content']) |
| 477 | + user_id = chat_item.user_id |
| 478 | + chat = chat_item.chat |
| 479 | + history = chat.get('history', {}) |
474 | 480 |
|
475 | | - user_id = chat.user_id |
476 | | - chat = chat.chat |
477 | | - history = chat.get('history', {}) |
| 481 | + if merge_fn is not None: |
| 482 | + existing_message = history.get('messages', {}).get(message_id, {}) |
| 483 | + message = merge_fn(existing_message) |
478 | 484 |
|
479 | | - if message_id in history.get('messages', {}): |
480 | | - history['messages'][message_id] = { |
481 | | - **history['messages'][message_id], |
482 | | - **message, |
483 | | - } |
484 | | - else: |
485 | | - history['messages'][message_id] = message |
| 485 | + # Sanitize message content for null characters before upserting |
| 486 | + if isinstance(message.get('content'), str): |
| 487 | + message['content'] = sanitize_text_for_db(message['content']) |
486 | 488 |
|
487 | | - history['currentId'] = message_id |
| 489 | + if message_id in history.get('messages', {}): |
| 490 | + history['messages'][message_id] = { |
| 491 | + **history['messages'][message_id], |
| 492 | + **message, |
| 493 | + } |
| 494 | + else: |
| 495 | + history['messages'][message_id] = message |
488 | 496 |
|
489 | | - chat['history'] = history |
| 497 | + history['currentId'] = message_id |
| 498 | + chat['history'] = history |
490 | 499 |
|
491 | | - # Dual-write to chat_message table |
492 | | - try: |
493 | | - ChatMessages.upsert_message( |
494 | | - message_id=message_id, |
495 | | - chat_id=id, |
496 | | - user_id=user_id, |
497 | | - data=history['messages'][message_id], |
498 | | - ) |
499 | | - except Exception as e: |
500 | | - log.warning(f'Failed to write to chat_message table: {e}') |
| 500 | + chat_item.chat = self._clean_null_bytes(chat) |
| 501 | + chat_item.title = self._clean_null_bytes(chat['title']) if 'title' in chat else 'New Chat' |
| 502 | + chat_item.updated_at = int(time.time()) |
| 503 | + db.commit() |
| 504 | + db.refresh(chat_item) |
| 505 | + |
| 506 | + # Dual-write to chat_message table |
| 507 | + try: |
| 508 | + ChatMessages.upsert_message( |
| 509 | + message_id=message_id, |
| 510 | + chat_id=id, |
| 511 | + user_id=user_id, |
| 512 | + data=history['messages'][message_id], |
| 513 | + ) |
| 514 | + except Exception as e: |
| 515 | + log.warning(f'Failed to write to chat_message table: {e}') |
501 | 516 |
|
502 | | - return self.update_chat_by_id(id, chat) |
| 517 | + return ChatModel.model_validate(chat_item) |
| 518 | + except Exception: |
| 519 | + return None |
503 | 520 |
|
504 | 521 | def add_message_status_to_chat_by_id_and_message_id( |
505 | 522 | self, id: str, message_id: str, status: dict |
506 | 523 | ) -> Optional[ChatModel]: |
507 | | - chat = self.get_chat_by_id(id) |
508 | | - if chat is None: |
509 | | - return None |
| 524 | + try: |
| 525 | + with get_db_context() as db: |
| 526 | + chat_item = db.get(Chat, id) |
| 527 | + if chat_item is None: |
| 528 | + return None |
| 529 | + |
| 530 | + chat = chat_item.chat |
| 531 | + history = chat.get('history', {}) |
510 | 532 |
|
511 | | - chat = chat.chat |
512 | | - history = chat.get('history', {}) |
| 533 | + if message_id in history.get('messages', {}): |
| 534 | + status_history = history['messages'][message_id].get('statusHistory', []) |
| 535 | + status_history.append(status) |
| 536 | + history['messages'][message_id]['statusHistory'] = status_history |
513 | 537 |
|
514 | | - if message_id in history.get('messages', {}): |
515 | | - status_history = history['messages'][message_id].get('statusHistory', []) |
516 | | - status_history.append(status) |
517 | | - history['messages'][message_id]['statusHistory'] = status_history |
| 538 | + chat['history'] = history |
518 | 539 |
|
519 | | - chat['history'] = history |
520 | | - return self.update_chat_by_id(id, chat) |
| 540 | + chat_item.chat = self._clean_null_bytes(chat) |
| 541 | + chat_item.updated_at = int(time.time()) |
| 542 | + db.commit() |
| 543 | + db.refresh(chat_item) |
| 544 | + |
| 545 | + return ChatModel.model_validate(chat_item) |
| 546 | + except Exception: |
| 547 | + return None |
521 | 548 |
|
522 | 549 | def add_message_files_by_id_and_message_id(self, id: str, message_id: str, files: list[dict]) -> list[dict]: |
523 | 550 | with get_db_context() as db: |
|
0 commit comments