Files
xiaoli-cli/core/history.py
T
shuiyu1123 78fea7ccdd 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
2026-07-05 13:17:27 +08:00

85 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""线程安全对话历史"""
import json
import threading
from .types import Message
class History:
def __init__(self, max_size=999999):
self._msgs = []
self._lock = threading.RLock()
self._max = max_size
def add_user(self, text):
with self._lock:
self._msgs.append(Message("user", text))
self._trim()
def add_assistant(self, text, tool_calls=None):
with self._lock:
m = Message("assistant", text)
if tool_calls:
m.tool_calls = tool_calls
self._msgs.append(m)
self._trim()
def add_tool(self, call_id, name, result):
with self._lock:
self._msgs.append(Message("tool", result, tool_call_id=call_id, name=name))
self._trim()
def add_cancel(self, name, args):
with self._lock:
self._msgs.append(Message("user", f"工具 {name} 已取消。参数: {args}"))
self._trim()
def _trim(self):
if len(self._msgs) > self._max:
self._msgs = self._msgs[-self._max:]
def render(self, system_prompt="", current=None, fc=False):
"""渲染为消息列表。fc=True 用 role:tool,否则工具结果用 role:user"""
with self._lock:
result = []
if system_prompt:
result.append({"role": "system", "content": system_prompt})
for m in self._msgs:
if m.role == "tool" and not fc:
result.append({"role": "user", "content": m.content})
else:
entry = {"role": m.role, "content": m.content}
if fc and m.role == "tool" and m.tool_call_id:
entry["tool_call_id"] = m.tool_call_id
if m.name:
entry["name"] = m.name
if fc and m.role == "assistant" and m.tool_calls:
# content 为空时设为 None(OpenAI 规范:有 tool_calls 时 content 可为 null)
if not entry.get("content"):
entry["content"] = None
# arguments 必须是 JSON 字符串(OpenAI 规范)
entry["tool_calls"] = [
{"id": tc.id, "type": "function",
"function": {
"name": tc.name,
"arguments": json.dumps(tc.arguments, ensure_ascii=False) if tc.arguments
else json.dumps({"args": tc.args}, ensure_ascii=False)
}}
for tc in m.tool_calls
]
result.append(entry)
if current:
result.append({"role": "user", "content": current})
return result
def snapshot(self):
with self._lock:
return list(self._msgs)
def clear(self):
with self._lock:
self._msgs.clear()
def __len__(self):
with self._lock:
return len(self._msgs)