fix: recover_usages支持ioinfo两种存储格式

ioinfo字段有两种存储方式:
1. JSON内容(流式模型如qwen3-max) - 直接解析
2. 文件路径(异步模型如viduq3-pro) - 读取文件再解析

修改后两种情况都能正确提取usage
This commit is contained in:
Hermes Agent 2026-06-15 17:16:35 +08:00
parent 0d2b39ddd7
commit fe4e8271bf

View File

@ -1,6 +1,6 @@
ns = params_kw.copy() ns = params_kw.copy()
limit = int(ns.get('limit') or 100) limit = int(ns.get('limit') or 200)
single_id = ns.get('id') or None single_id = ns.get('id') or None
from ahserver.filestorage import FileStorage from ahserver.filestorage import FileStorage
@ -9,23 +9,21 @@ import os
db = DBPools() db = DBPools()
dbname = get_module_dbname('llmage') dbname = get_module_dbname('llmage')
details = []
recovered = 0 recovered = 0
failed = 0 failed = 0
skipped = 0 skipped = 0
errors = []
try: try:
async with db.sqlorContext(dbname) as sor: async with db.sqlorContext(dbname) as sor:
if single_id: if single_id:
sql = """select a.id, a.llmid, a.ioinfo, a.status, a.use_date, sql = """select a.id, a.llmid, a.ioinfo, a.status, b.model
b.model
from llmusage a from llmusage a
left join llm b on a.llmid = b.id left join llm b on a.llmid = b.id
where a.id = ${id}$""" where a.id = ${id}$"""
params = {'id': single_id} params = {'id': single_id}
else: else:
sql = """select a.id, a.llmid, a.ioinfo, a.status, a.use_date, sql = """select a.id, a.llmid, a.ioinfo, a.status, b.model
b.model
from llmusage a from llmusage a
left join llm b on a.llmid = b.id left join llm b on a.llmid = b.id
where a.usages is null where a.usages is null
@ -63,20 +61,28 @@ try:
continue continue
try: try:
real_path = fs.realPath(ioinfo) # ioinfo 可能是 JSON 内容,也可能是文件路径
if not os.path.isfile(real_path): io_data = None
failed += 1 if ioinfo.startswith('{') or ioinfo.startswith('"'):
continue # 直接是 JSON 内容
io_data = json.loads(ioinfo)
with open(real_path, 'r', encoding='utf-8') as f: else:
io_data = json.load(f) # 文件路径
real_path = fs.realPath(ioinfo)
if not os.path.isfile(real_path):
errors.append(f'{rid}: 文件不存在')
failed += 1
continue
with open(real_path, 'r', encoding='utf-8') as f:
io_data = json.load(f)
outputs = io_data.get('output', []) outputs = io_data.get('output', [])
if not outputs: if not outputs:
errors.append(f'{rid}: output为空')
failed += 1 failed += 1
continue continue
# 从最后一条output中获取usage # 从最后一条output开始倒序找usage
usage = None usage = None
for out in reversed(outputs): for out in reversed(outputs):
if isinstance(out, dict) and out.get('usage'): if isinstance(out, dict) and out.get('usage'):
@ -84,6 +90,7 @@ try:
break break
if not usage: if not usage:
errors.append(f'{rid}: output中未找到usage')
failed += 1 failed += 1
continue continue
@ -96,6 +103,7 @@ try:
except Exception as e: except Exception as e:
debug(f'recover_usages error for {rid}: {e}') debug(f'recover_usages error for {rid}: {e}')
errors.append(f'{rid}: {e}')
failed += 1 failed += 1
except Exception as e: except Exception as e:
@ -113,14 +121,16 @@ except Exception as e:
total = recovered + failed + skipped total = recovered + failed + skipped
msg = f"处理 {total} 条: 恢复成功 {recovered}, 失败 {failed}, 跳过 {skipped}" msg = f"处理 {total} 条: 恢复成功 {recovered}, 失败 {failed}, 跳过 {skipped}"
if errors:
msg += f"\n失败详情(前5条): {'; '.join(errors[:5])}"
return { return {
"widgettype": "Message", "widgettype": "Message",
"options": { "options": {
"title": "恢复Usages完成", "title": "恢复Usages完成",
"cwidth": 24, "cwidth": 30,
"cheight": 5, "cheight": 6,
"timeout": 6, "timeout": 8,
"message": msg "message": msg
} }
} }