55
66from sqlalchemy .orm import Session
77from open_webui .internal .db import Base , get_db_context
8+ from open_webui .utils .response import normalize_usage
89
910from pydantic import BaseModel , ConfigDict
1011from sqlalchemy import (
@@ -41,6 +42,12 @@ def _normalize_timestamp(timestamp: int) -> float:
4142 return timestamp
4243
4344
45+ def get_usage (data : dict ) -> Optional [dict ]:
46+ """Extract and normalize usage from message data."""
47+ usage = data .get ('usage' ) or (data .get ('info' ) or {}).get ('usage' )
48+ return normalize_usage (usage ) if usage else None
49+
50+
4451####################
4552# ChatMessage DB Schema
4653####################
@@ -163,11 +170,8 @@ def upsert_message(
163170 existing .status_history = data .get ('status_history' ) or data .get ('statusHistory' )
164171 if 'error' in data :
165172 existing .error = data .get ('error' )
166- # Extract usage - check direct field first, then info.usage
167- usage = data .get ('usage' )
168- if not usage :
169- info = data .get ('info' , {})
170- usage = info .get ('usage' ) if info else None
173+ # Extract and normalize usage
174+ usage = get_usage (data )
171175 if usage :
172176 # Deep-merge: preserve existing keys not present in new data
173177 # This prevents background tasks (follow-ups, title, tags)
@@ -179,11 +183,8 @@ def upsert_message(
179183 return ChatMessageModel .model_validate (existing )
180184 else :
181185 # Insert new
182- # Extract usage - check direct field first, then info.usage
183- usage = data .get ('usage' )
184- if not usage :
185- info = data .get ('info' , {})
186- usage = info .get ('usage' ) if info else None
186+ # Extract and normalize usage
187+ usage = get_usage (data )
187188 message = ChatMessage (
188189 id = composite_id ,
189190 chat_id = chat_id ,
0 commit comments