|
6 | 6 | from sqlalchemy import select, delete, func, cast, Integer, distinct |
7 | 7 | from sqlalchemy.ext.asyncio import AsyncSession |
8 | 8 | from open_webui.internal.db import Base, get_async_db_context |
9 | | -from open_webui.utils.response import merge_usage, normalize_usage |
| 9 | +from open_webui.utils.response import normalize_usage |
10 | 10 | from pydantic import BaseModel, ConfigDict |
11 | 11 | from sqlalchemy import ( |
12 | 12 | JSON, |
@@ -200,11 +200,15 @@ async def upsert_message( |
200 | 200 | existing.error = data.get('error') |
201 | 201 | if 'context_summary' in data or 'contextSummary' in data: |
202 | 202 | existing.context_summary = data.get('context_summary') or data.get('contextSummary') |
203 | | - # Extract and normalize usage |
| 203 | + # Usage is monotonic per message; keep the largest snapshot so |
| 204 | + # idempotent re-saves dedupe instead of double-counting. |
204 | 205 | usage = get_usage(data) |
205 | 206 | if usage: |
206 | | - existing_usage = normalize_usage(existing.usage or {}) if existing.usage else {} |
207 | | - existing.usage = existing_usage if usage == existing_usage else merge_usage(existing_usage, usage) |
| 207 | + new_total = usage.get('total_tokens', 0) |
| 208 | + # Normalize legacy rows missing total_tokens |
| 209 | + existing_total = normalize_usage(existing.usage or {}).get('total_tokens', 0) |
| 210 | + if existing.usage is None or new_total >= existing_total: |
| 211 | + existing.usage = usage |
208 | 212 | existing.updated_at = now |
209 | 213 | await db.commit() |
210 | 214 | await db.refresh(existing) |
|
0 commit comments