- 重构 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
85 lines
3.1 KiB
Python
85 lines
3.1 KiB
Python
"""线程安全对话历史"""
|
||
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)
|