重构前端:添加TabPanel正确语法,创建sessions和config CRUD页面及API

This commit is contained in:
yumoqing 2026-05-06 12:58:21 +08:00
parent 216c21466f
commit 4882ba0290
7 changed files with 330 additions and 46 deletions

View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Get reasoning configuration for current user"""
import json
result = {'success': False, 'config': {}}
try:
dbname = get_module_dbname('harnessed_reasoning')
user_id = await get_user()
sql = """SELECT * FROM harnessed_reasoning_config
WHERE user_id = ${user_id}$
ORDER BY updated_at DESC
LIMIT 1"""
async with DBPools().sqlorContext(dbname) as sor:
rows = await sor.sqlExe(sql, {'user_id': user_id})
if rows and len(rows) > 0:
config = dict(rows[0])
# Convert string booleans to actual booleans for UI
config['enable_cross_session_search'] = config.get('enable_cross_session_search', '1') == '1'
config['enable_skill_auto_loading'] = config.get('enable_skill_auto_loading', '1') == '1'
config['enable_error_recovery'] = config.get('enable_error_recovery', '1') == '1'
result['config'] = config
else:
result['config'] = {
'max_reasoning_steps': 10,
'max_tool_calls_per_step': 5,
'enable_cross_session_search': True,
'enable_skill_auto_loading': True,
'safety_mode': 'strict',
'max_context_tokens': 4000,
'enable_error_recovery': True,
'max_recovery_attempts': 3
}
result['success'] = True
except Exception as e:
result['error'] = str(e)
return json.dumps(result, ensure_ascii=False, default=str)

View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Save reasoning configuration for current user"""
import json, uuid, time
result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}}
try:
dbname = get_module_dbname('harnessed_reasoning')
user_id = await get_user()
now = time.strftime('%Y-%m-%d %H:%M:%S')
max_reasoning_steps = int(params_kw.get('max_reasoning_steps', 10))
max_tool_calls_per_step = int(params_kw.get('max_tool_calls_per_step', 5))
enable_cross_session_search = '1' if params_kw.get('enable_cross_session_search') == '1' else '0'
enable_skill_auto_loading = '1' if params_kw.get('enable_skill_auto_loading') == '1' else '0'
safety_mode = params_kw.get('safety_mode', 'strict').strip()
max_context_tokens = int(params_kw.get('max_context_tokens', 4000))
enable_error_recovery = '1' if params_kw.get('enable_error_recovery') == '1' else '0'
max_recovery_attempts = int(params_kw.get('max_recovery_attempts', 3))
async with DBPools().sqlorContext(dbname) as sor:
# Check if config exists
rows = await sor.sqlExe("SELECT id FROM harnessed_reasoning_config WHERE user_id = ${user_id}$", {'user_id': user_id})
if rows and len(rows) > 0:
config_id = rows[0]['id']
await sor.sqlExe("""UPDATE harnessed_reasoning_config SET
max_reasoning_steps = ${max_reasoning_steps}$,
max_tool_calls_per_step = ${max_tool_calls_per_step}$,
enable_cross_session_search = ${enable_cross_session_search}$,
enable_skill_auto_loading = ${enable_skill_auto_loading}$,
safety_mode = ${safety_mode}$,
max_context_tokens = ${max_context_tokens}$,
enable_error_recovery = ${enable_error_recovery}$,
max_recovery_attempts = ${max_recovery_attempts}$,
updated_at = ${updated_at}$
WHERE id = ${id}$""", {
'id': config_id,
'max_reasoning_steps': max_reasoning_steps,
'max_tool_calls_per_step': max_tool_calls_per_step,
'enable_cross_session_search': enable_cross_session_search,
'enable_skill_auto_loading': enable_skill_auto_loading,
'safety_mode': safety_mode,
'max_context_tokens': max_context_tokens,
'enable_error_recovery': enable_error_recovery,
'max_recovery_attempts': max_recovery_attempts,
'updated_at': now
})
else:
config_id = str(uuid.uuid4()).replace('-', '')[:32]
await sor.sqlExe("""INSERT INTO harnessed_reasoning_config
(id, user_id, max_reasoning_steps, max_tool_calls_per_step,
enable_cross_session_search, enable_skill_auto_loading, safety_mode,
max_context_tokens, enable_error_recovery, max_recovery_attempts,
created_at, updated_at)
VALUES (${id}$, ${user_id}$, ${max_reasoning_steps}$, ${max_tool_calls_per_step}$,
${enable_cross_session_search}$, ${enable_skill_auto_loading}$, ${safety_mode}$,
${max_context_tokens}$, ${enable_error_recovery}$, ${max_recovery_attempts}$,
${created_at}$, ${updated_at}$)""", {
'id': config_id,
'user_id': user_id,
'max_reasoning_steps': max_reasoning_steps,
'max_tool_calls_per_step': max_tool_calls_per_step,
'enable_cross_session_search': enable_cross_session_search,
'enable_skill_auto_loading': enable_skill_auto_loading,
'safety_mode': safety_mode,
'max_context_tokens': max_context_tokens,
'enable_error_recovery': enable_error_recovery,
'max_recovery_attempts': max_recovery_attempts,
'created_at': now,
'updated_at': now
})
result = {'widgettype': 'Message', 'options': {'title': 'Success', 'message': '配置保存成功', 'type': 'success'}}
except Exception as e:
result['options'] = {'title': 'Error', 'message': '保存失败: ' + str(e), 'type': 'error'}
return json.dumps(result, ensure_ascii=False)

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Reasoning sessions list API"""
import json
result = {'success': False, 'rows': [], 'total': 0}
try:
dbname = get_module_dbname('harnessed_reasoning')
ns = {
'page': int(params_kw.get('page', 1)),
'rows': int(params_kw.get('rows', 20)),
'sort': 'created_at desc'
}
user_id = await get_user()
sql = """SELECT id, initial_request, context_summary, status, created_at, updated_at
FROM harnessed_reasoning_sessions
WHERE user_id = ${user_id}$
ORDER BY created_at DESC"""
async with DBPools().sqlorContext(dbname) as sor:
data = await sor.sqlExe(sql, {'user_id': user_id})
if isinstance(data, dict):
result['total'] = data.get('total', 0)
result['rows'] = [dict(r) for r in data.get('rows', [])]
else:
result['rows'] = [dict(r) for r in (data or [])]
result['total'] = len(result['rows'])
result['success'] = True
except Exception as e:
result['error'] = str(e)
return json.dumps(result, ensure_ascii=False, default=str)

View File

@ -0,0 +1,89 @@
{
"widgettype": "VBox",
"options": {
"width": "100%",
"height": "100%",
"padding": "16px"
},
"subwidgets": [
{
"widgettype": "Form",
"id": "config_form",
"options": {
"data_url": "{{entire_url('api/config_get.dspy')}}",
"data_method": "GET",
"submit_url": "{{entire_url('api/config_save.dspy')}}",
"method": "POST",
"layout": "vertical",
"fields": [
{
"name": "max_reasoning_steps",
"label": "最大推理步骤数",
"uitype": "int",
"required": true
},
{
"name": "max_tool_calls_per_step",
"label": "每步最大工具调用数",
"uitype": "int",
"required": true
},
{
"name": "enable_cross_session_search",
"label": "启用跨会话搜索",
"uitype": "check"
},
{
"name": "enable_skill_auto_loading",
"label": "启用技能自动加载",
"uitype": "check"
},
{
"name": "safety_mode",
"label": "安全模式",
"uitype": "code",
"required": true,
"data": [
{"value": "strict", "text": "严格"},
{"value": "moderate", "text": "中等"},
{"value": "lenient", "text": "宽松"}
]
},
{
"name": "max_context_tokens",
"label": "最大上下文 token 数",
"uitype": "int",
"required": true
},
{
"name": "enable_error_recovery",
"label": "启用错误恢复",
"uitype": "check"
},
{
"name": "max_recovery_attempts",
"label": "最大恢复尝试次数",
"uitype": "int",
"required": true
}
],
"buttons": [
{
"type": "submit",
"label": "保存配置",
"variant": "primary"
}
],
"maxWidth": "500px"
},
"binds": [
{
"wid": "self",
"event": "submited",
"actiontype": "script",
"script": "await bricks.show_resp_message_or_error(event.params)"
}
]
}
]
}

View File

@ -0,0 +1,38 @@
{
"widgettype": "Tabular",
"options": {
"width": "100%",
"height": "100%",
"data_url": "{{entire_url('api/sessions_list.dspy')}}",
"data_method": "GET",
"page_rows": 20,
"row_options": {
"fields": [
{"name": "id", "width": 80, "frozen": true},
{"name": "initial_request", "title": "请求内容", "width": 300},
{"name": "context_summary", "title": "上下文摘要", "width": 200},
{
"name": "status",
"title": "状态",
"width": 100,
"uitype": "code",
"data": [
{"value": "pending", "text": "待处理"},
{"value": "executing", "text": "执行中"},
{"value": "completed", "text": "已完成"},
{"value": "failed", "text": "失败"},
{"value": "blocked", "text": "已阻止"},
{"value": "cancelled", "text": "已取消"}
]
},
{"name": "created_at", "title": "创建时间", "width": 160}
],
"editexclouded": ["id"]
},
"editable": {
"new_data_url": null,
"update_data_url": null,
"delete_data_url": null
}
}
}

View File

@ -3,7 +3,7 @@
"options": { "options": {
"width": "100%", "width": "100%",
"height": "100%", "height": "100%",
"css": "" "padding": "0"
}, },
"subwidgets": [ "subwidgets": [
{ {
@ -12,47 +12,57 @@
"css": "ios-navbar", "css": "ios-navbar",
"items": [ "items": [
{ {
"widgettype": "Label", "widgettype": "Text",
"text": "Reasoning Engine",
"options": { "options": {
"css": "ios-navbar-title" "text": "Reasoning Engine",
"css": "ios-navbar-title",
"fontSize": "18px",
"fontWeight": "bold",
"color": "#1E40AF"
} }
}, },
{ {
"widgettype": "Label", "widgettype": "Text",
"text": "Manage sessions and system configuration",
"options": { "options": {
"css": "ios-navbar-subtitle" "text": "Manage sessions and system configuration",
"css": "ios-navbar-subtitle",
"fontSize": "12px",
"color": "#6B7280"
} }
} }
] ]
} }
}, },
{ {
"widgettype": "Tab", "widgettype": "TabPanel",
"options": { "options": {
"tabs": [ "tab_pos": "top",
{"title": "Sessions", "icon": "history"}, "css": "tabpanel",
{"title": "Configuration", "icon": "settings"} "items": [
], {
"css": "ios-tabbar" "name": "sessions",
}, "label": "推理会话",
"subwidgets": [ "icon": "history",
{ "content": {
"widgettype": "urlwidget", "widgettype": "urlwidget",
"options": { "options": {
"url": "{{entire_url(harnessed_reasoning_sessions_crud)}}", "url": "{{entire_url('harnessed_reasoning_sessions_crud')}}"
"css": "ios-card" }
}
},
{
"name": "config",
"label": "推理配置",
"icon": "settings",
"content": {
"widgettype": "urlwidget",
"options": {
"url": "{{entire_url('harnessed_reasoning_config_view')}}"
}
}
} }
}, ]
{ }
"widgettype": "urlwidget",
"options": {
"url": "{{entire_url(harnessed_reasoning_config_view)}}",
"css": "ios-card"
}
}
]
} }
] ]
} }

View File

@ -10,22 +10,12 @@
"cwidth":10, "cwidth":10,
"items":[ "items":[
{% if get_user() %} {% if get_user() %}
{ {
"name":"reasoning_console", "name":"hermes_reasoning",
"label":"推理控制台", "label":"推理控制台",
"url":"{{entire_url('/harnessed_reasoning/hermes_reasoning')}}" "url":"{{entire_url('hermes_reasoning.ui')}}"
}, }
{
"name":"reasoning_sessions",
"label":"推理会话",
"url":"{{entire_url('/harnessed_reasoning/sessions')}}"
},
{
"name":"reasoning_config",
"label":"推理配置",
"url":"{{entire_url('/harnessed_reasoning/config')}}"
}
{% endif %} {% endif %}
] ]
} }
} }