Skip to content

Commit 485d689

Browse files
committed
refac
1 parent 85c7373 commit 485d689

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

backend/open_webui/models/chat_messages.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,81 @@ async def get_messages_by_chat_id(self, chat_id: str, db: Optional[AsyncSession]
241241
messages = result.scalars().all()
242242
return [ChatMessageModel.model_validate(message) for message in messages]
243243

244+
# DB column names that differ from the JSON message keys.
245+
DB_TO_JSON_KEY_MAP = {
246+
'parent_id': 'parentId',
247+
'model_id': 'model',
248+
'status_history': 'statusHistory',
249+
'created_at': 'timestamp',
250+
}
251+
# DB-internal columns excluded from the reconstructed message dict.
252+
EXCLUDED_COLUMNS = frozenset({'id', 'chat_id', 'user_id', 'updated_at'})
253+
254+
async def get_messages_map_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> Optional[dict]:
255+
"""Build a {message_id: message_dict} map from chat_message rows.
256+
257+
Returns the same shape as chat.history.messages so callers
258+
(get_message_list, middleware) work unchanged. Returns None if
259+
no rows exist for the chat (caller should fall back to the
260+
embedded JSON blob for legacy chats).
261+
"""
262+
async with get_async_db_context(db) as db:
263+
result = await db.execute(
264+
select(ChatMessage).filter_by(chat_id=chat_id)
265+
)
266+
rows = result.scalars().all()
267+
268+
if not rows:
269+
return None
270+
271+
# Strip the composite-id prefix ("{chat_id}-") to recover the
272+
# original message_id used as map key.
273+
prefix = f'{chat_id}-'
274+
prefix_len = len(prefix)
275+
col_keys = [c.key for c in ChatMessage.__table__.columns]
276+
277+
messages_map: dict[str, dict] = {}
278+
for row in rows:
279+
msg_id = row.id[prefix_len:] if row.id.startswith(prefix) else row.id
280+
281+
msg: dict = {'id': msg_id}
282+
for key in col_keys:
283+
if key in self.EXCLUDED_COLUMNS:
284+
continue
285+
val = getattr(row, key)
286+
if val is None:
287+
continue
288+
json_key = self.DB_TO_JSON_KEY_MAP.get(key, key)
289+
msg[json_key] = val
290+
291+
# Ensure content always has a value
292+
msg.setdefault('content', '')
293+
294+
# Mirror usage into info.usage for callers that read it there
295+
if 'usage' in msg:
296+
msg['info'] = {'usage': msg['usage']}
297+
298+
messages_map[msg_id] = msg
299+
300+
# Reconstruct childrenIds from parentId links so that the map
301+
# is fully navigable (callers like the frontend rely on this).
302+
for msg_id, msg in messages_map.items():
303+
parent_id = msg.get('parentId')
304+
if parent_id and parent_id in messages_map:
305+
parent = messages_map[parent_id]
306+
children = parent.get('childrenIds')
307+
if children is None:
308+
parent['childrenIds'] = [msg_id]
309+
elif msg_id not in children:
310+
children.append(msg_id)
311+
312+
# Ensure every message has a childrenIds list (leaf nodes get [])
313+
for msg in messages_map.values():
314+
if 'childrenIds' not in msg:
315+
msg['childrenIds'] = []
316+
317+
return messages_map
318+
244319
async def get_messages_by_user_id(
245320
self,
246321
user_id: str,

backend/open_webui/models/chats.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,18 @@ async def get_chat_title_by_id(self, id: str) -> Optional[str]:
460460
return row[0] or 'New Chat'
461461

462462
async def get_messages_map_by_chat_id(self, id: str) -> Optional[dict]:
463+
"""Message map for walking history (see ``get_message_list``).
464+
465+
Prefer ``chat_message`` rows to avoid loading the large ``chat``
466+
JSON blob; fall back to embedded history when no rows exist
467+
(legacy chats).
468+
"""
469+
# Fast path: build from normalized chat_message rows.
470+
messages_map = await ChatMessages.get_messages_map_by_chat_id(id)
471+
if messages_map is not None:
472+
return messages_map
473+
474+
# No rows — fall back to the embedded JSON blob for legacy chats.
463475
chat = await self.get_chat_by_id(id)
464476
if chat is None:
465477
return None

0 commit comments

Comments
 (0)