添加推理交互和模型配置:创建推理控制台UI和API,扩展config模型添加LLM模型字段(model_name/provider/api_key/temperature等)

This commit is contained in:
yumoqing 2026-05-06 15:05:28 +08:00
parent 4882ba0290
commit 3dfd935dfe
8 changed files with 479 additions and 93 deletions

View File

@ -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",

View File

@ -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

View File

@ -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
})

View File

@ -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)

View File

@ -1,14 +1,21 @@
{
"widgettype": "TabPanel",
"options": {
"tab_pos": "top",
"items": [
{
"name": "reasoning_config",
"label": "推理引擎",
"icon": "settings",
"content": {
"widgettype": "VBox",
"options": {
"width": "100%",
"height": "100%",
"padding": "16px"
},
"subwidgets": [
{
"widgettype": "Form",
"id": "config_form",
"id": "engine_config_form",
"options": {
"data_url": "{{entire_url('api/config_get.dspy')}}",
"data_method": "GET",
@ -29,14 +36,10 @@
"required": true
},
{
"name": "enable_cross_session_search",
"label": "启用跨会话搜索",
"uitype": "check"
},
{
"name": "enable_skill_auto_loading",
"label": "启用技能自动加载",
"uitype": "check"
"name": "max_context_tokens",
"label": "最大上下文 token 数",
"uitype": "int",
"required": true
},
{
"name": "safety_mode",
@ -50,10 +53,14 @@
]
},
{
"name": "max_context_tokens",
"label": "最大上下文 token 数",
"uitype": "int",
"required": true
"name": "enable_cross_session_search",
"label": "启用跨会话搜索",
"uitype": "check"
},
{
"name": "enable_skill_auto_loading",
"label": "启用技能自动加载",
"uitype": "check"
},
{
"name": "enable_error_recovery",
@ -70,7 +77,7 @@
"buttons": [
{
"type": "submit",
"label": "保存配置",
"label": "保存引擎配置",
"variant": "primary"
}
],
@ -86,4 +93,108 @@
]
}
]
}
},
{
"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)"
}
]
}
]
}
}
]
}
}

View File

@ -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",

View File

@ -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 %}
]

View File

@ -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')}}"
}
}
]
}
]
}