From 3dfd935dfe953a0683700c786a293602fdf00f58 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 6 May 2026 15:05:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=A8=E7=90=86=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=92=8C=E6=A8=A1=E5=9E=8B=E9=85=8D=E7=BD=AE=EF=BC=9A?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=8E=A8=E7=90=86=E6=8E=A7=E5=88=B6=E5=8F=B0?= =?UTF-8?q?UI=E5=92=8CAPI=EF=BC=8C=E6=89=A9=E5=B1=95config=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E6=B7=BB=E5=8A=A0LLM=E6=A8=A1=E5=9E=8B=E5=AD=97?= =?UTF-8?q?=E6=AE=B5(model=5Fname/provider/api=5Fkey/temperature=E7=AD=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/harnessed_reasoning_config.json | 59 +++++ wwwroot/api/config_get.dspy | 13 +- wwwroot/api/config_save.dspy | 42 +++- wwwroot/api/reasoning_submit.dspy | 61 +++++ wwwroot/harnessed_reasoning_config_view.ui | 279 ++++++++++++++------- wwwroot/hermes_reasoning.ui | 17 +- wwwroot/menu.ui | 15 ++ wwwroot/reasoning_console.ui | 86 +++++++ 8 files changed, 479 insertions(+), 93 deletions(-) create mode 100644 wwwroot/api/reasoning_submit.dspy create mode 100644 wwwroot/reasoning_console.ui diff --git a/models/harnessed_reasoning_config.json b/models/harnessed_reasoning_config.json index 38f304b..acf5109 100644 --- a/models/harnessed_reasoning_config.json +++ b/models/harnessed_reasoning_config.json @@ -81,6 +81,65 @@ "nullable": "no", "default": "3" }, + { + "name": "model_name", + "title": "LLM model name (e.g. qwen3-max, claude-sonnet-4)", + "type": "str", + "length": 64, + "nullable": "yes", + "default": "qwen3-max" + }, + { + "name": "model_provider", + "title": "Model provider (e.g. openrouter, anthropic, dashscope)", + "type": "str", + "length": 32, + "nullable": "yes", + "default": "openrouter" + }, + { + "name": "api_key", + "title": "API key for LLM provider", + "type": "text", + "nullable": "yes", + "default": "" + }, + { + "name": "api_endpoint", + "title": "API endpoint URL (optional, uses default if empty)", + "type": "str", + "length": 255, + "nullable": "yes", + "default": "" + }, + { + "name": "temperature", + "title": "LLM temperature (0.0-1.0)", + "type": "float", + "nullable": "no", + "default": "0.7" + }, + { + "name": "max_output_tokens", + "title": "Maximum output tokens", + "type": "int", + "nullable": "no", + "default": "4096" + }, + { + "name": "top_p", + "title": "Top-p sampling parameter", + "type": "float", + "nullable": "no", + "default": "0.9" + }, + { + "name": "system_prompt", + "title": "System prompt template for reasoning", + "type": "text", + "nullable": "yes", + "default": "" + }, { "name": "created_at", "title": "Creation timestamp", diff --git a/wwwroot/api/config_get.dspy b/wwwroot/api/config_get.dspy index d811027..ee4590a 100644 --- a/wwwroot/api/config_get.dspy +++ b/wwwroot/api/config_get.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""Get reasoning configuration for current user""" +"""Get reasoning configuration for current user (includes model config)""" import json result = {'success': False, 'config': {}} @@ -18,7 +18,6 @@ try: 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' @@ -32,7 +31,15 @@ try: 'safety_mode': 'strict', 'max_context_tokens': 4000, 'enable_error_recovery': True, - 'max_recovery_attempts': 3 + 'max_recovery_attempts': 3, + 'model_name': 'qwen3-max', + 'model_provider': 'openrouter', + 'api_key': '', + 'api_endpoint': '', + 'temperature': 0.7, + 'max_output_tokens': 4096, + 'top_p': 0.9, + 'system_prompt': '' } result['success'] = True diff --git a/wwwroot/api/config_save.dspy b/wwwroot/api/config_save.dspy index 6d2f484..6e13ecc 100644 --- a/wwwroot/api/config_save.dspy +++ b/wwwroot/api/config_save.dspy @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -"""Save reasoning configuration for current user""" +"""Save reasoning configuration for current user (includes model config)""" import json, uuid, time result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': 'Invalid request', 'type': 'error'}} @@ -10,6 +10,7 @@ try: user_id = await get_user() now = time.strftime('%Y-%m-%d %H:%M:%S') + # Reasoning engine params 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' @@ -19,8 +20,17 @@ try: 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)) + # Model configuration + model_name = params_kw.get('model_name', 'qwen3-max').strip() + model_provider = params_kw.get('model_provider', 'openrouter').strip() + api_key = params_kw.get('api_key', '').strip() + api_endpoint = params_kw.get('api_endpoint', '').strip() + temperature = float(params_kw.get('temperature', 0.7)) + max_output_tokens = int(params_kw.get('max_output_tokens', 4096)) + top_p = float(params_kw.get('top_p', 0.9)) + system_prompt = params_kw.get('system_prompt', '').strip() + 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: @@ -34,6 +44,14 @@ try: max_context_tokens = ${max_context_tokens}$, enable_error_recovery = ${enable_error_recovery}$, max_recovery_attempts = ${max_recovery_attempts}$, + model_name = ${model_name}$, + model_provider = ${model_provider}$, + api_key = ${api_key}$, + api_endpoint = ${api_endpoint}$, + temperature = ${temperature}$, + max_output_tokens = ${max_output_tokens}$, + top_p = ${top_p}$, + system_prompt = ${system_prompt}$, updated_at = ${updated_at}$ WHERE id = ${id}$""", { 'id': config_id, @@ -45,6 +63,14 @@ try: 'max_context_tokens': max_context_tokens, 'enable_error_recovery': enable_error_recovery, 'max_recovery_attempts': max_recovery_attempts, + 'model_name': model_name, + 'model_provider': model_provider, + 'api_key': api_key, + 'api_endpoint': api_endpoint, + 'temperature': temperature, + 'max_output_tokens': max_output_tokens, + 'top_p': top_p, + 'system_prompt': system_prompt, 'updated_at': now }) else: @@ -53,10 +79,14 @@ try: (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, + model_name, model_provider, api_key, api_endpoint, + temperature, max_output_tokens, top_p, system_prompt, 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}$, + ${model_name}$, ${model_provider}$, ${api_key}$, ${api_endpoint}$, + ${temperature}$, ${max_output_tokens}$, ${top_p}$, ${system_prompt}$, ${created_at}$, ${updated_at}$)""", { 'id': config_id, 'user_id': user_id, @@ -68,6 +98,14 @@ try: 'max_context_tokens': max_context_tokens, 'enable_error_recovery': enable_error_recovery, 'max_recovery_attempts': max_recovery_attempts, + 'model_name': model_name, + 'model_provider': model_provider, + 'api_key': api_key, + 'api_endpoint': api_endpoint, + 'temperature': temperature, + 'max_output_tokens': max_output_tokens, + 'top_p': top_p, + 'system_prompt': system_prompt, 'created_at': now, 'updated_at': now }) diff --git a/wwwroot/api/reasoning_submit.dspy b/wwwroot/api/reasoning_submit.dspy new file mode 100644 index 0000000..25e0820 --- /dev/null +++ b/wwwroot/api/reasoning_submit.dspy @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""Submit reasoning request and return results""" +import json, time, uuid + +result = {'widgettype': 'Message', 'options': {'title': 'Error', 'message': '推理请求失败', 'type': 'error'}} + +try: + request_text = params_kw.get('request', '').strip() + if not request_text: + result['options'] = {'title': 'Error', 'message': '请输入推理请求', 'type': 'error'} + else: + user_id = await get_user() + execute_immediately = params_kw.get('execute_immediately', '1') == '1' + + # Call the reasoning engine from ServerEnv + reasoning_result = await hermes_reason_and_execute( + request=request_text, + execute_immediately=execute_immediately + ) + + if reasoning_result.get('success'): + # Build result widget showing reasoning output + plan_items = [] + for step in reasoning_result.get('execution_plan', []): + plan_items.append(f"步骤{step.get('step_number', '?')}: {step.get('description', '')}") + + plan_text = '\n'.join(plan_items) if plan_items else '无执行计划' + safety_text = '\n'.join(reasoning_result.get('safety_violations', [])) or '无安全风险' + + summary = ( + f"请求: {request_text}\n\n" + f"上下文: {reasoning_result.get('context_summary', '无')}\n\n" + f"置信度: {reasoning_result.get('confidence_score', 0):.0%}\n\n" + f"执行计划:\n{plan_text}\n\n" + f"安全检查:\n{safety_text}\n\n" + f"状态: {reasoning_result.get('status', 'unknown')}" + ) + + if reasoning_result.get('execution_results'): + summary += f"\n\n执行结果:\n{json.dumps(reasoning_result['execution_results'], ensure_ascii=False, indent=2)}" + + result = { + 'widgettype': 'Message', + 'options': { + 'title': '推理完成', + 'message': summary, + 'type': 'success' + } + } + else: + result['options'] = { + 'title': '推理失败', + 'message': reasoning_result.get('error', '未知错误'), + 'type': 'error' + } + +except Exception as e: + result['options'] = {'title': 'Error', 'message': '推理异常: ' + str(e), 'type': 'error'} + +return json.dumps(result, ensure_ascii=False) diff --git a/wwwroot/harnessed_reasoning_config_view.ui b/wwwroot/harnessed_reasoning_config_view.ui index 3fd5f28..5cd30a5 100644 --- a/wwwroot/harnessed_reasoning_config_view.ui +++ b/wwwroot/harnessed_reasoning_config_view.ui @@ -1,89 +1,200 @@ { - "widgettype": "VBox", + "widgettype": "TabPanel", "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 + "tab_pos": "top", + "items": [ + { + "name": "reasoning_config", + "label": "推理引擎", + "icon": "settings", + "content": { + "widgettype": "VBox", + "options": { + "padding": "16px" }, - { - "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)" + "subwidgets": [ + { + "widgettype": "Form", + "id": "engine_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": "max_context_tokens", + "label": "最大上下文 token 数", + "uitype": "int", + "required": true + }, + { + "name": "safety_mode", + "label": "安全模式", + "uitype": "code", + "required": true, + "data": [ + {"value": "strict", "text": "严格"}, + {"value": "moderate", "text": "中等"}, + {"value": "lenient", "text": "宽松"} + ] + }, + { + "name": "enable_cross_session_search", + "label": "启用跨会话搜索", + "uitype": "check" + }, + { + "name": "enable_skill_auto_loading", + "label": "启用技能自动加载", + "uitype": "check" + }, + { + "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)" + } + ] + } + ] } - ] - } - ] + }, + { + "name": "model_config", + "label": "模型配置", + "icon": "code", + "content": { + "widgettype": "VBox", + "options": { + "padding": "16px" + }, + "subwidgets": [ + { + "widgettype": "Form", + "id": "model_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": "model_name", + "label": "模型名称", + "uitype": "text", + "required": true, + "placeholder": "qwen3-max, claude-sonnet-4, gpt-4o..." + }, + { + "name": "model_provider", + "label": "模型提供商", + "uitype": "code", + "required": true, + "data": [ + {"value": "openrouter", "text": "OpenRouter"}, + {"value": "anthropic", "text": "Anthropic"}, + {"value": "dashscope", "text": "Dashscope (阿里云)"}, + {"value": "openai", "text": "OpenAI"}, + {"value": "custom", "text": "自定义"} + ] + }, + { + "name": "api_key", + "label": "API Key", + "uitype": "password", + "required": false, + "placeholder": "输入 API Key" + }, + { + "name": "api_endpoint", + "label": "API Endpoint (可选)", + "uitype": "text", + "placeholder": "留空使用默认端点" + }, + { + "name": "temperature", + "label": "Temperature (0.0-1.0)", + "uitype": "float", + "required": true + }, + { + "name": "max_output_tokens", + "label": "最大输出 token 数", + "uitype": "int", + "required": true + }, + { + "name": "top_p", + "label": "Top-p (0.0-1.0)", + "uitype": "float", + "required": true + }, + { + "name": "system_prompt", + "label": "系统提示词模板", + "uitype": "textarea", + "rows": 4, + "placeholder": "留空使用默认系统提示词" + } + ], + "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/hermes_reasoning.ui b/wwwroot/hermes_reasoning.ui index 44236a1..1d336dc 100644 --- a/wwwroot/hermes_reasoning.ui +++ b/wwwroot/hermes_reasoning.ui @@ -15,7 +15,6 @@ "widgettype": "Text", "options": { "text": "Reasoning Engine", - "css": "ios-navbar-title", "fontSize": "18px", "fontWeight": "bold", "color": "#1E40AF" @@ -24,8 +23,7 @@ { "widgettype": "Text", "options": { - "text": "Manage sessions and system configuration", - "css": "ios-navbar-subtitle", + "text": "AI reasoning and execution planning", "fontSize": "12px", "color": "#6B7280" } @@ -39,6 +37,17 @@ "tab_pos": "top", "css": "tabpanel", "items": [ + { + "name": "console", + "label": "推理控制台", + "icon": "terminal", + "content": { + "widgettype": "urlwidget", + "options": { + "url": "{{entire_url('reasoning_console.ui')}}" + } + } + }, { "name": "sessions", "label": "推理会话", @@ -52,7 +61,7 @@ }, { "name": "config", - "label": "推理配置", + "label": "配置管理", "icon": "settings", "content": { "widgettype": "urlwidget", diff --git a/wwwroot/menu.ui b/wwwroot/menu.ui index e5d9a6e..35e8914 100644 --- a/wwwroot/menu.ui +++ b/wwwroot/menu.ui @@ -14,6 +14,21 @@ "name":"hermes_reasoning", "label":"推理控制台", "url":"{{entire_url('hermes_reasoning.ui')}}" + }, + { + "name":"reasoning_console", + "label":"推理交互", + "url":"{{entire_url('reasoning_console.ui')}}" + }, + { + "name":"reasoning_sessions", + "label":"推理会话", + "url":"{{entire_url('harnessed_reasoning_sessions_crud')}}" + }, + { + "name":"reasoning_config", + "label":"推理配置", + "url":"{{entire_url('harnessed_reasoning_config_view')}}" } {% endif %} ] diff --git a/wwwroot/reasoning_console.ui b/wwwroot/reasoning_console.ui new file mode 100644 index 0000000..3059b7e --- /dev/null +++ b/wwwroot/reasoning_console.ui @@ -0,0 +1,86 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "padding": "16px", + "spacing": "16px" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "推理控制台", + "fontSize": "20px", + "fontWeight": "bold", + "color": "#1E40AF" + } + }, + { + "widgettype": "Form", + "id": "reasoning_form", + "options": { + "submit_url": "{{entire_url('api/reasoning_submit.dspy')}}", + "method": "POST", + "layout": "vertical", + "fields": [ + { + "name": "request", + "label": "推理请求", + "uitype": "textarea", + "required": true, + "rows": 4, + "placeholder": "请输入您的请求,例如:分析当前项目的安全问题并生成修复方案" + }, + { + "name": "execute_immediately", + "label": "提交后立即执行(否则仅生成计划)", + "uitype": "check", + "checked": true + } + ], + "buttons": [ + { + "type": "submit", + "label": "开始推理", + "variant": "primary" + } + ], + "maxWidth": "700px" + }, + "binds": [ + { + "wid": "self", + "event": "submited", + "actiontype": "script", + "script": "await bricks.show_resp_message_or_error(event.params)" + } + ] + }, + { + "widgettype": "VBox", + "options": { + "width": "100%", + "padding": "16px" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "历史推理记录", + "fontSize": "16px", + "fontWeight": "bold", + "color": "#374151", + "marginBottom": "8px" + } + }, + { + "widgettype": "urlwidget", + "options": { + "url": "{{entire_url('harnessed_reasoning_sessions_crud')}}" + } + } + ] + } + ] +}