This commit is contained in:
1 parent
df67a298be
commit
bb32884ecb
4 files changed
+69
-11
No files matched your search
@@ -3463,6 +3463,7 @@ async def _send_message_core(
|
|||||||
|
|
||||||
|
|
||||||
_schema = {"type": "object", "properties": {}}
|
_schema = {"type": "object", "properties": {}}
|
||||||
|
_schema = _normalize_mcp_schema(_schema)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -5228,7 +5229,7 @@ async def _send_message_core(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
tool_result = f"[工具调用已被策略拦截] {_policy_check.get('message', _reason)}"
|
tool_result = f"[策略拦截: {_policy_check.get('message', _reason)}] 建议: 检查权限设置或新开对话"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -5404,7 +5405,7 @@ async def _send_message_core(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
tool_result = "[MCP 工具未找到或映射缺失]"
|
tool_result = "[MCP 工具未找到] 建议: 检查工具名或尝试其他MCP工具。可用: " + str(list(mcp_tool_map.keys())[:5])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -8402,6 +8403,35 @@ def _resolve_agent_tool_policy(agents):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_mcp_schema(schema: dict) -> dict:
|
||||||
|
"""MCP JSON Schema -> OpenAI function calling 兼容。
|
||||||
|
处理: $ref, oneOf/anyOf -> first option, 剥离非标关键字"""
|
||||||
|
if not isinstance(schema, dict):
|
||||||
|
return {"type": "object", "properties": {}}
|
||||||
|
if "$ref" in schema:
|
||||||
|
return {"type": "object", "properties": {}}
|
||||||
|
if "oneOf" in schema or "anyOf" in schema:
|
||||||
|
opts = schema.get("oneOf") or schema.get("anyOf") or []
|
||||||
|
if opts and isinstance(opts[0], dict):
|
||||||
|
return _normalize_mcp_schema(opts[0])
|
||||||
|
return {"type": "object", "properties": {}}
|
||||||
|
if "allOf" in schema:
|
||||||
|
merged = {"type": "object", "properties": {}}
|
||||||
|
for s in schema.get("allOf", []):
|
||||||
|
sub = _normalize_mcp_schema(s)
|
||||||
|
if sub.get("properties"):
|
||||||
|
merged["properties"].update(sub["properties"])
|
||||||
|
return merged
|
||||||
|
result = {}
|
||||||
|
for k in ("type", "properties", "required", "description", "enum", "items"):
|
||||||
|
if k in schema:
|
||||||
|
result[k] = schema[k]
|
||||||
|
if result.get("type") != "object":
|
||||||
|
result["type"] = "object"
|
||||||
|
if "properties" not in result:
|
||||||
|
result["properties"] = {}
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _check_tool_policy(func_name, mcp_info, skills, policy, call_count, round_count):
|
def _check_tool_policy(func_name, mcp_info, skills, policy, call_count, round_count):
|
||||||
|
|
||||||
|
|||||||
@@ -1272,7 +1272,7 @@ def _execute_skill_tool(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
return f"工具 '{func_name}' 执行结果: 已处理输入内容, 但未找到对应的技能定义。请基于通用知识回复用户。"
|
return f"工具 '{func_name}' 执行结果: 已处理输入内容, 但未找到对应的技能定义。请基于通用知识回复用户。如有已加载的技能,可使用其名称直接调用。"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1779,3 +1779,8 @@ html.dark .ai-sending-timeout {
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-process-running { animation: aiPulse 1.5s ease-in-out infinite; }
|
||||||
|
@keyframes aiPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.6; } }
|
||||||
|
.ai-process-error { background: rgba(255,0,0,0.05); border-left: 2px solid #e74c3c; }
|
||||||
|
.ai-process-running { border-left: 2px solid #3498db; }
|
||||||
@@ -531,18 +531,41 @@ const AiProcessBlock: React.FC<{ steps: any[]; autoExpand: boolean }> = ({ steps
|
|||||||
AI 思考 / 执行过程({steps.length} 步 — {open ? '自动收拢中... 点击手动收起' : '点击展开查看'})
|
AI 思考 / 执行过程({steps.length} 步 — {open ? '自动收拢中... 点击手动收起' : '点击展开查看'})
|
||||||
</summary>
|
</summary>
|
||||||
<div className="ai-process-list">
|
<div className="ai-process-list">
|
||||||
{steps.map((s: any, i: number) => (
|
{steps.map((s: any, i: number) => {
|
||||||
<div key={i} className={`ai-process-item ai-process-${s.type}`}>
|
const isLast = i === steps.length - 1;
|
||||||
<span className="ai-process-ico">
|
const hasResult = s.result && s.result.length > 0;
|
||||||
{s.type === 'thinking' ? '💡' : s.type === 'skill' ? '🛠' : '🔌'}
|
const isError = hasResult && (s.result.includes('error') || s.result.includes('失败') || s.result.includes('异常') || s.result.includes('拦截'));
|
||||||
</span>
|
const isRunning = isLast && !hasResult;
|
||||||
|
const stepIcon = s.type === 'thinking' ? '💡' : s.type === 'skill' ? '🛠' : '🔌';
|
||||||
|
const statusIcon = isRunning ? ' ⏳' : isError ? ' ❌' : hasResult ? ' ✅' : '';
|
||||||
|
const resultDisplay = (s.result || '').length > 400 ? (s.result || '').slice(0, 400) + '...' : (s.result || '');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={i} className={`ai-process-item ai-process-${s.type}${isRunning ? ' ai-process-running' : ''}${isError ? ' ai-process-error' : ''}`}>
|
||||||
|
<span className="ai-process-ico">{stepIcon}{statusIcon}</span>
|
||||||
<div className="ai-process-body">
|
<div className="ai-process-body">
|
||||||
{s.type === 'thinking' && (<><b className="ai-process-title">思考</b><p className="ai-process-text">{s.text}</p></>)}
|
{s.type === 'thinking' && (<><b className="ai-process-title">思考</b><p className="ai-process-text">{s.text}</p></>)}
|
||||||
{s.type === 'skill' && (<><b className="ai-process-title">调用技能:{s.name}</b><p className="ai-process-text">{(s.result || '').slice(0, 400)}</p></>)}
|
{s.type === 'skill' && (<>
|
||||||
{s.type === 'mcp' && (<><b className="ai-process-title">MCP · {s.server}:{s.name}</b><p className="ai-process-text">{(s.result || '').slice(0, 400)}</p></>)}
|
<b className="ai-process-title">调用技能:{s.name}{statusIcon}</b>
|
||||||
|
{s.args && Object.keys(s.args).length > 0 && (
|
||||||
|
<span style={{fontSize:'12px',color:'#666',margin:'2px 0',display:'block'}}>
|
||||||
|
参数: {JSON.stringify(s.args).slice(0, 200)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{hasResult && <p className="ai-process-text">{resultDisplay}</p>}
|
||||||
|
</>)}
|
||||||
|
{s.type === 'mcp' && (<>
|
||||||
|
<b className="ai-process-title">MCP · {s.server}:{s.name}{statusIcon}</b>
|
||||||
|
{s.args && Object.keys(s.args).length > 0 && (
|
||||||
|
<span style={{fontSize:'12px',color:'#666',margin:'2px 0',display:'block'}}>
|
||||||
|
参数: {JSON.stringify(s.args).slice(0, 200)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{hasResult && <p className="ai-process-text">{resultDisplay}</p>}
|
||||||
|
</>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
)})}
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user