From 4882ba029088e4ad84ebea525f4c6f6623f0a8c4 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 6 May 2026 12:58:21 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=89=8D=E7=AB=AF=EF=BC=9A?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0TabPanel=E6=AD=A3=E7=A1=AE=E8=AF=AD=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E5=88=9B=E5=BB=BAsessions=E5=92=8Cconfig=20CRUD?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=8F=8AAPI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wwwroot/api/config_get.dspy | 42 +++++++++ wwwroot/api/config_save.dspy | 80 ++++++++++++++++++ wwwroot/api/sessions_list.dspy | 35 ++++++++ wwwroot/harnessed_reasoning_config_view.ui | 89 ++++++++++++++++++++ wwwroot/harnessed_reasoning_sessions_crud.ui | 38 +++++++++ wwwroot/hermes_reasoning.ui | 68 ++++++++------- wwwroot/menu.ui | 24 ++---- 7 files changed, 330 insertions(+), 46 deletions(-) create mode 100644 wwwroot/api/config_get.dspy create mode 100644 wwwroot/api/config_save.dspy create mode 100644 wwwroot/api/sessions_list.dspy create mode 100644 wwwroot/harnessed_reasoning_config_view.ui create mode 100644 wwwroot/harnessed_reasoning_sessions_crud.ui diff --git a/wwwroot/api/config_get.dspy b/wwwroot/api/config_get.dspy new file mode 100644 index 0000000..d811027 --- /dev/null +++ b/wwwroot/api/config_get.dspy @@ -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) diff --git a/wwwroot/api/config_save.dspy b/wwwroot/api/config_save.dspy new file mode 100644 index 0000000..6d2f484 --- /dev/null +++ b/wwwroot/api/config_save.dspy @@ -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) diff --git a/wwwroot/api/sessions_list.dspy b/wwwroot/api/sessions_list.dspy new file mode 100644 index 0000000..8596af3 --- /dev/null +++ b/wwwroot/api/sessions_list.dspy @@ -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) diff --git a/wwwroot/harnessed_reasoning_config_view.ui b/wwwroot/harnessed_reasoning_config_view.ui new file mode 100644 index 0000000..3fd5f28 --- /dev/null +++ b/wwwroot/harnessed_reasoning_config_view.ui @@ -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)" + } + ] + } + ] +} diff --git a/wwwroot/harnessed_reasoning_sessions_crud.ui b/wwwroot/harnessed_reasoning_sessions_crud.ui new file mode 100644 index 0000000..b0615e7 --- /dev/null +++ b/wwwroot/harnessed_reasoning_sessions_crud.ui @@ -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 + } + } +} diff --git a/wwwroot/hermes_reasoning.ui b/wwwroot/hermes_reasoning.ui index 325e1e2..44236a1 100644 --- a/wwwroot/hermes_reasoning.ui +++ b/wwwroot/hermes_reasoning.ui @@ -3,7 +3,7 @@ "options": { "width": "100%", "height": "100%", - "css": "" + "padding": "0" }, "subwidgets": [ { @@ -12,47 +12,57 @@ "css": "ios-navbar", "items": [ { - "widgettype": "Label", - "text": "Reasoning Engine", + "widgettype": "Text", "options": { - "css": "ios-navbar-title" + "text": "Reasoning Engine", + "css": "ios-navbar-title", + "fontSize": "18px", + "fontWeight": "bold", + "color": "#1E40AF" } }, { - "widgettype": "Label", - "text": "Manage sessions and system configuration", + "widgettype": "Text", "options": { - "css": "ios-navbar-subtitle" + "text": "Manage sessions and system configuration", + "css": "ios-navbar-subtitle", + "fontSize": "12px", + "color": "#6B7280" } } ] } }, { - "widgettype": "Tab", + "widgettype": "TabPanel", "options": { - "tabs": [ - {"title": "Sessions", "icon": "history"}, - {"title": "Configuration", "icon": "settings"} - ], - "css": "ios-tabbar" - }, - "subwidgets": [ - { - "widgettype": "urlwidget", - "options": { - "url": "{{entire_url(harnessed_reasoning_sessions_crud)}}", - "css": "ios-card" + "tab_pos": "top", + "css": "tabpanel", + "items": [ + { + "name": "sessions", + "label": "推理会话", + "icon": "history", + "content": { + "widgettype": "urlwidget", + "options": { + "url": "{{entire_url('harnessed_reasoning_sessions_crud')}}" + } + } + }, + { + "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" - } - } - ] + ] + } } ] } diff --git a/wwwroot/menu.ui b/wwwroot/menu.ui index c301557..e5d9a6e 100644 --- a/wwwroot/menu.ui +++ b/wwwroot/menu.ui @@ -10,22 +10,12 @@ "cwidth":10, "items":[ {% if get_user() %} - { - "name":"reasoning_console", - "label":"推理控制台", - "url":"{{entire_url('/harnessed_reasoning/hermes_reasoning')}}" - }, - { - "name":"reasoning_sessions", - "label":"推理会话", - "url":"{{entire_url('/harnessed_reasoning/sessions')}}" - }, - { - "name":"reasoning_config", - "label":"推理配置", - "url":"{{entire_url('/harnessed_reasoning/config')}}" - } + { + "name":"hermes_reasoning", + "label":"推理控制台", + "url":"{{entire_url('hermes_reasoning.ui')}}" + } {% endif %} - ] - } + ] + } }