修复llmusage展示脚本500错误并优化CRUD性能

- 将llmusage_ioinfo_display.ui和llmusage_usages_display.ui从Jinja模板改为.dspy脚本
- 在utils.py中添加get_llmusage_by_id和read_ioinfo_content两个函数
- 重写get_llmusage.dspy,避免sqlPaging子查询包装,分离count和data查询
- 为llmusage表添加use_time索引提升排序性能
- 更新load_path.py和json/llmusage.json引用新的.dspy文件
This commit is contained in:
yumoqing 2026-06-26 17:55:52 +08:00
parent a6efef56e9
commit 6c2ccda2c7
8 changed files with 138 additions and 75 deletions

View File

@ -31,7 +31,7 @@
"height": "70%"
},
"options": {
"url": "{{entire_url('../llmusage_usages_display.ui')}}?id=${id}$"
"url": "{{entire_url('../llmusage_usages_display.dspy')}}?id=${id}$"
}
},
{
@ -45,7 +45,7 @@
"height": "70%"
},
"options": {
"url": "{{entire_url('../llmusage_ioinfo_display.ui')}}?id=${id}$"
"url": "{{entire_url('../llmusage_ioinfo_display.dspy')}}?id=${id}$"
}
}
],

View File

@ -149,6 +149,32 @@ async def write_llmio(luid, io_dic):
webpath = await fs.save(name, s, userid='llmio')
return webpath
async def get_llmusage_by_id(usage_id):
"""从数据库获取 llmusage 记录"""
env = ServerEnv()
async with get_sor_context(env, 'llmage') as sor:
sql = "select id, llmid, ioinfo, usages from llmusage where id = ${id}$"
recs = await sor.sqlExe(sql, {'id': usage_id})
if recs and len(recs) > 0:
return dict(recs[0])
return None
async def read_ioinfo_content(ioinfo_webpath):
"""从 FileStorage 读取交互信息文件内容"""
if not ioinfo_webpath:
return None
try:
fs = FileStorage()
real_path = fs.realPath(ioinfo_webpath)
async with aiofiles.open(real_path, 'rb') as f:
bin_data = await f.read()
return json.loads(bin_data.decode('utf-8'))
except Exception as e:
debug(f'read_ioinfo_content error: {e}')
return None
async def llm_query_orders(userorgid, page, pagerows=80):
env = ServerEnv()
async with get_sor_context(env, 'llmage') as sor:

View File

@ -151,6 +151,13 @@
"userorgid",
"use_time"
]
},
{
"name": "idx_llmusage_usetime",
"idxtype": "index",
"idxfields": [
"use_time"
]
}
],
"codes": [
@ -193,4 +200,4 @@
"cond": "parentid='accounting_status'"
}
]
}
}

View File

@ -155,8 +155,8 @@ PATHS_LOGINED = [
f"/{MOD}/llmusage/delete_llmusage.dspy",
f"/{MOD}/llmusage/get_llmusage.dspy",
f"/{MOD}/llmusage/update_llmusage.dspy",
f"/{MOD}/llmusage_usages_display.ui",
f"/{MOD}/llmusage_ioinfo_display.ui",
f"/{MOD}/llmusage_usages_display.dspy",
f"/{MOD}/llmusage_ioinfo_display.dspy",
# CRUD 子目录 — llmusage_accounting_failed/
f"/{MOD}/llmusage_accounting_failed/index.ui",

View File

@ -0,0 +1,48 @@
import json
from llmage.utils import get_llmusage_by_id, read_ioinfo_content
usage_id = params_kw.get('id')
if not usage_id:
return {"widgettype": "Message", "options": {"message": "缺少 id 参数", "type": "error"}}
record = await get_llmusage_by_id(usage_id)
if not record:
return {"widgettype": "Message", "options": {"message": "未找到记录", "type": "error"}}
llmid = record.get('llmid', '')
ioinfo_webpath = record.get('ioinfo', '')
ioinfo_content = await read_ioinfo_content(ioinfo_webpath) if ioinfo_webpath else None
ioinfo_text = json.dumps(ioinfo_content, ensure_ascii=False, indent=2) if ioinfo_content else "无交互内容"
return {
"widgettype": "VScrollPanel",
"options": {
"width": "100%",
"height": "100%"
},
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"width": "100%",
"padding": "16px",
"spacing": 12
},
"subwidgets": [
{
"widgettype": "Title4",
"options": {
"text": f"Model: {llmid}"
}
},
{
"widgettype": "Text",
"options": {
"text": ioinfo_text
}
}
]
}
]
}

View File

@ -1,35 +0,0 @@
{% set sor = db.sqlorContext() %}
{% set row = sor.execute_sql('select llmid, ioinfo from llmusage where id = ${id}$').first() %}
{% set ioinfo = row.ioinfo if row and row.ioinfo else '' %}
{% set model_id = row.llmid if row else '' %}
{
"widgettype": "VScrollPanel",
"options": {
"width": "100%",
"height": "100%"
},
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"width": "100%",
"padding": "16px",
"spacing": 12
},
"subwidgets": [
{
"widgettype": "Title4",
"options": {
"text": "Model: {{ model_id }}"
}
},
{
"widgettype": "Text",
"options": {
"text": {{ ioinfo | tojson }}
}
}
]
}
]
}

View File

@ -0,0 +1,52 @@
import json
from llmage.utils import get_llmusage_by_id
usage_id = params_kw.get('id')
if not usage_id:
return {"widgettype": "Message", "options": {"message": "缺少 id 参数", "type": "error"}}
record = await get_llmusage_by_id(usage_id)
if not record:
return {"widgettype": "Message", "options": {"message": "未找到记录", "type": "error"}}
llmid = record.get('llmid', '')
usages_raw = record.get('usages', '')
try:
usages_obj = json.loads(usages_raw) if isinstance(usages_raw, str) else usages_raw
except (json.JSONDecodeError, TypeError):
usages_obj = usages_raw
usages_text = json.dumps(usages_obj, ensure_ascii=False, indent=2) if usages_obj else "无使用信息"
return {
"widgettype": "VScrollPanel",
"options": {
"width": "100%",
"height": "100%"
},
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"width": "100%",
"padding": "16px",
"spacing": 12
},
"subwidgets": [
{
"widgettype": "Title4",
"options": {
"text": f"Model: {llmid}"
}
},
{
"widgettype": "Text",
"options": {
"text": usages_text
}
}
]
}
]
}

View File

@ -1,35 +0,0 @@
{% set sor = db.sqlorContext() %}
{% set row = sor.execute_sql('select llmid, usages from llmusage where id = ${id}$').first() %}
{% set usages = row.usages if row and row.usages else '' %}
{% set model_id = row.llmid if row else '' %}
{
"widgettype": "VScrollPanel",
"options": {
"width": "100%",
"height": "100%"
},
"subwidgets": [
{
"widgettype": "VBox",
"options": {
"width": "100%",
"padding": "16px",
"spacing": 12
},
"subwidgets": [
{
"widgettype": "Title4",
"options": {
"text": "Model: {{ model_id }}"
}
},
{
"widgettype": "Text",
"options": {
"text": {{ usages | tojson }}
}
}
]
}
]
}