xiaoli-cli 7.0.0: 重构底层架构 + 移除工具截断
- 重构 core/ 底层 (7文件): app/runner/engine/parser/history/safety/display - FC原生调用为主,prompt-JSON保底 - 移除所有工具返回截断 (ai_search/git_tools/cmd_executor/browser_auto/network_tools/sub_agent/tool_search) - 删除无用文件 (旧服务器/WebUI/彩蛋/启动器等21个文件) - 清理 constants.py 死代码 - 版本号更新为 7.0.0
This commit is contained in:
commit
78fea7ccdd
57 files changed
+18358
No files matched your search
+146
@@ -0,0 +1,146 @@
|
||||
"""JSON解析 — 保底方案"""
|
||||
import json, re, uuid
|
||||
from .types import ToolCall, Plan
|
||||
|
||||
|
||||
def parse_prompt_json(text):
|
||||
"""解析prompt-JSON,返回 (text_content, tool_calls_or_None)"""
|
||||
text = text.strip()
|
||||
json_str = _extract_json(text)
|
||||
if json_str is None:
|
||||
return text, None
|
||||
try:
|
||||
data = json.loads(json_str)
|
||||
except json.JSONDecodeError:
|
||||
return text, None
|
||||
idx = text.find(json_str)
|
||||
prefix = text[:idx].strip() if idx > 0 else ""
|
||||
if isinstance(data, list):
|
||||
calls = [c for c in (_make(c) for c in data) if c]
|
||||
return prefix, calls if calls else None
|
||||
if isinstance(data, dict):
|
||||
c = _make(data)
|
||||
if c:
|
||||
return prefix, [c]
|
||||
return prefix, data
|
||||
return text, None
|
||||
|
||||
|
||||
def _extract_json(text):
|
||||
"""平衡括号扫描"""
|
||||
depth = 0
|
||||
start = -1
|
||||
in_str = False
|
||||
esc = False
|
||||
for i, c in enumerate(text):
|
||||
if esc:
|
||||
esc = False
|
||||
continue
|
||||
if c == '\\' and in_str:
|
||||
esc = True
|
||||
continue
|
||||
if c == '"' and not esc:
|
||||
in_str = not in_str
|
||||
continue
|
||||
if in_str:
|
||||
continue
|
||||
if c == '{':
|
||||
if depth == 0:
|
||||
start = i
|
||||
depth += 1
|
||||
elif c == '}':
|
||||
depth -= 1
|
||||
if depth == 0 and start != -1:
|
||||
candidate = text[start:i+1]
|
||||
if '"action"' in candidate or '"tool"' in candidate:
|
||||
return candidate
|
||||
start = -1
|
||||
elif c == '[' and depth == 0:
|
||||
bd = 1
|
||||
j = i + 1
|
||||
in_s = False
|
||||
e2 = False
|
||||
while j < len(text) and bd > 0:
|
||||
cj = text[j]
|
||||
if e2:
|
||||
e2 = False
|
||||
elif cj == '\\' and in_s:
|
||||
e2 = True
|
||||
elif cj == '"' and not e2:
|
||||
in_s = not in_s
|
||||
elif not in_s:
|
||||
if cj == '[': bd += 1
|
||||
elif cj == ']': bd -= 1
|
||||
j += 1
|
||||
if bd == 0:
|
||||
candidate = text[i:j]
|
||||
if '"action"' in candidate or '"tool"' in candidate:
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def _make(data):
|
||||
"""dict → ToolCall"""
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
cid = data.get("id", str(uuid.uuid4())[:8])
|
||||
name = ""
|
||||
args = ""
|
||||
arguments = None
|
||||
if data.get("action") == "use_tool":
|
||||
name = data.get("tool", data.get("name", ""))
|
||||
ra = data.get("args", data.get("arguments", ""))
|
||||
if isinstance(ra, str): args = ra
|
||||
elif isinstance(ra, dict):
|
||||
arguments = ra
|
||||
args = json.dumps(ra, ensure_ascii=False)
|
||||
else: args = str(ra)
|
||||
elif "name" in data and "arguments" in data:
|
||||
name = data["name"]
|
||||
ra = data["arguments"]
|
||||
if isinstance(ra, str):
|
||||
args = ra
|
||||
try: arguments = json.loads(ra)
|
||||
except: pass
|
||||
elif isinstance(ra, dict):
|
||||
arguments = ra
|
||||
args = json.dumps(ra, ensure_ascii=False)
|
||||
elif "tool" in data and "args" in data:
|
||||
name = data["tool"]
|
||||
ra = data["args"]
|
||||
if isinstance(ra, str): args = ra
|
||||
elif isinstance(ra, dict):
|
||||
arguments = ra
|
||||
args = json.dumps(ra, ensure_ascii=False)
|
||||
else:
|
||||
return None
|
||||
if not name:
|
||||
return None
|
||||
return ToolCall(id=cid, name=name, args=args, arguments=arguments, raw=data)
|
||||
|
||||
|
||||
def interpret(resp, fc_mode):
|
||||
"""EngineResponse → Plan"""
|
||||
# FC模式
|
||||
if fc_mode and resp.tool_calls:
|
||||
return Plan(action="use_tool", text=resp.text or "", tool_calls=resp.tool_calls)
|
||||
# prompt-JSON
|
||||
text, parsed = parse_prompt_json(resp.text)
|
||||
if parsed is None:
|
||||
return Plan(action="final", text=resp.text)
|
||||
if isinstance(parsed, list):
|
||||
return Plan(action="use_tool", text=text, tool_calls=parsed)
|
||||
if isinstance(parsed, dict):
|
||||
action = parsed.get("action", "")
|
||||
if action == "use_tool":
|
||||
c = _make(parsed)
|
||||
if c:
|
||||
return Plan(action="use_tool", text=text, tool_calls=[c])
|
||||
return Plan(action="final", text=resp.text)
|
||||
if action == "continue" or parsed.get("continue"):
|
||||
msg = parsed.get("content", parsed.get("message", ""))
|
||||
return Plan(action="continue", text=msg or text)
|
||||
if "message" in parsed:
|
||||
return Plan(action="final", text=parsed["message"])
|
||||
return Plan(action="final", text=text or resp.text)
|
||||
return Plan(action="final", text=resp.text)
|
||||
Reference in new issue
Block a user