pycoze 0.1.307__py3-none-any.whl → 0.1.334__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycoze/bot/__init__.py +1 -1
- pycoze/bot/chat.py +99 -0
- pycoze/bot/chat_base.py +247 -0
- pycoze/bot/lib.py +245 -0
- pycoze/bot/{agent/chat.py → message.py} +0 -1
- pycoze/bot/prompt.md +393 -0
- pycoze/bot/tools.py +281 -0
- pycoze/reference/lib.py +47 -13
- {pycoze-0.1.307.dist-info → pycoze-0.1.334.dist-info}/METADATA +1 -1
- {pycoze-0.1.307.dist-info → pycoze-0.1.334.dist-info}/RECORD +13 -16
- pycoze/bot/agent/__init__.py +0 -5
- pycoze/bot/agent/agent.py +0 -95
- pycoze/bot/agent/agent_types/__init__.py +0 -4
- pycoze/bot/agent/agent_types/const.py +0 -1
- pycoze/bot/agent/agent_types/openai_func_call_agent.py +0 -181
- pycoze/bot/agent/assistant.py +0 -35
- pycoze/bot/agent_chat.py +0 -110
- pycoze/bot/bot.py +0 -23
- {pycoze-0.1.307.dist-info → pycoze-0.1.334.dist-info}/LICENSE +0 -0
- {pycoze-0.1.307.dist-info → pycoze-0.1.334.dist-info}/WHEEL +0 -0
- {pycoze-0.1.307.dist-info → pycoze-0.1.334.dist-info}/top_level.txt +0 -0
pycoze/reference/lib.py
CHANGED
@@ -1,9 +1,42 @@
|
|
1
1
|
import os
|
2
2
|
import sys
|
3
3
|
import importlib
|
4
|
-
import
|
4
|
+
import functools
|
5
|
+
import inspect
|
6
|
+
import asyncio
|
7
|
+
import nest_asyncio
|
5
8
|
|
9
|
+
# 应用 nest_asyncio 补丁,以允许在已经运行的事件循环中再次运行事件循环
|
10
|
+
nest_asyncio.apply()
|
6
11
|
|
12
|
+
def call_func(func, args=None, kwargs=None):
|
13
|
+
if args is None:
|
14
|
+
args = ()
|
15
|
+
if kwargs is None:
|
16
|
+
kwargs = {}
|
17
|
+
|
18
|
+
if inspect.isgeneratorfunction(func):
|
19
|
+
return list(func(*args, **kwargs))
|
20
|
+
elif inspect.iscoroutinefunction(func):
|
21
|
+
coro = func(*args, **kwargs) if inspect.iscoroutinefunction(func) else func
|
22
|
+
try:
|
23
|
+
# 尝试获取当前运行的事件循环
|
24
|
+
loop = asyncio.get_running_loop()
|
25
|
+
except RuntimeError:
|
26
|
+
# 如果没有运行的事件循环,使用 asyncio.run()
|
27
|
+
return asyncio.run(coro)
|
28
|
+
else:
|
29
|
+
# 如果事件循环已经在运行,直接调度协程并等待结果
|
30
|
+
async def run_coro():
|
31
|
+
return await coro
|
32
|
+
# 在当前事件循环中调度任务并等待完成
|
33
|
+
future = asyncio.ensure_future(run_coro())
|
34
|
+
while not future.done():
|
35
|
+
loop.run_until_complete(asyncio.sleep(0.1)) # 避免阻塞
|
36
|
+
return future.result()
|
37
|
+
else:
|
38
|
+
return func(*args, **kwargs)
|
39
|
+
|
7
40
|
class ChangeDirectoryAndPath:
|
8
41
|
"""Context manager to change the current working directory and sys.path."""
|
9
42
|
|
@@ -18,7 +51,8 @@ class ChangeDirectoryAndPath:
|
|
18
51
|
return self
|
19
52
|
|
20
53
|
def __exit__(self, exc_type, exc_value, traceback):
|
21
|
-
sys.path
|
54
|
+
if self.module_path in sys.path:
|
55
|
+
sys.path.remove(self.module_path)
|
22
56
|
os.chdir(self.old_path)
|
23
57
|
|
24
58
|
|
@@ -56,16 +90,16 @@ def wrapped_func(func, module_path):
|
|
56
90
|
except:
|
57
91
|
print(f"called unknown")
|
58
92
|
with ChangeDirectoryAndPath(module_path):
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
return result
|
67
|
-
_wrapped.__name__ = func.__name__
|
68
|
-
_wrapped.__doc__ = func.__doc__
|
69
|
-
return _wrapped
|
93
|
+
try:
|
94
|
+
result = call_func(func, args, kwargs)
|
95
|
+
except Exception as e:
|
96
|
+
print(f"调用 {func.__name__} 时发生错误: {str(e)}")
|
97
|
+
raise
|
98
|
+
|
99
|
+
print(f"{func.__name__} 调用完毕,结果为:", result)
|
70
100
|
|
101
|
+
return result
|
102
|
+
|
103
|
+
wrapped_function = functools.wraps(func)(_wrapped)
|
104
|
+
return wrapped_function
|
71
105
|
|
@@ -11,19 +11,16 @@ pycoze/api/lib/tab.py,sha256=lD2Uvylzi-zsHj-27YI1mS4ggbIGtCi3vypvGvY48gM,2619
|
|
11
11
|
pycoze/api/lib/view.py,sha256=_PIpTfeuTPPlMDKshMGsqFQYMq7ZiO4Hg5XwHwDoU60,7357
|
12
12
|
pycoze/api/lib/web.py,sha256=GWgtiTJOolKOX2drXcwuyqTcbo5FQVxa1NuBGcNyjyc,223
|
13
13
|
pycoze/api/lib/window.py,sha256=bTkQCzQZ7i3pYXB70bUSTBNJ9C4TW_X3yMae1VkquGk,1944
|
14
|
-
pycoze/bot/__init__.py,sha256=
|
15
|
-
pycoze/bot/
|
16
|
-
pycoze/bot/
|
17
|
-
pycoze/bot/
|
18
|
-
pycoze/bot/
|
19
|
-
pycoze/bot/
|
20
|
-
pycoze/bot/
|
21
|
-
pycoze/bot/agent/agent_types/__init__.py,sha256=zmU2Kmrv5mCdfg-QlPn2H6pWxbGeq8s7YTqLhpzJC6k,179
|
22
|
-
pycoze/bot/agent/agent_types/const.py,sha256=BfUKPrhAHREoMLHuFNG2bCIEkC1-f7K0LEqNg4RwiRE,70
|
23
|
-
pycoze/bot/agent/agent_types/openai_func_call_agent.py,sha256=3qOyrddujtJ50W9SbH5bapbVTwjgE_LC2TnYJWUH9yc,6649
|
14
|
+
pycoze/bot/__init__.py,sha256=rL3Q-ycczRpSFfKn84fg3QBl5k22WpyeIU5qOEjEby8,79
|
15
|
+
pycoze/bot/chat.py,sha256=ts2Qr7IZGyTFlnIccG8m04H7c8Yr1xtjPgzmjeQUzo4,3351
|
16
|
+
pycoze/bot/chat_base.py,sha256=H6zLxblMbyDLsf5T13YYCoEfLl__axw67PbfM0vnYbg,9242
|
17
|
+
pycoze/bot/lib.py,sha256=smigeWuhl8esHE-Y5l_9bpjJkEJ5OqrxTyPcO8JIubM,7224
|
18
|
+
pycoze/bot/message.py,sha256=Zq-_k8HztBMOUIs3hbOvWvwHBNopn4UJJBliCROIGcc,718
|
19
|
+
pycoze/bot/prompt.md,sha256=XHP8EdtzmnlMbM0xTe5GKnjAIFq7KauUARiNly2atz4,15777
|
20
|
+
pycoze/bot/tools.py,sha256=j8l0sXEPn_yjTllHcmYEr_auVDuOicXdDco4bhZVIIA,9694
|
24
21
|
pycoze/reference/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
22
|
pycoze/reference/bot.py,sha256=pxHVYo0G3P3YZ--vBYbMEiEyBoxxPwaO5dMTf9WFMSc,2014
|
26
|
-
pycoze/reference/lib.py,sha256=
|
23
|
+
pycoze/reference/lib.py,sha256=ixiexCA6XWAda_G2ZVDSbFqTJmbkTvYLN47-nc5zyFU,3530
|
27
24
|
pycoze/reference/tool.py,sha256=h7G3KSoZYWq5IJu6E0-shIQ3XiJeJsgSM85GxEnhF98,1107
|
28
25
|
pycoze/reference/workflow.py,sha256=whQtw_FAxvlSbjow1oNFLdytPjjRs_pDBbQmNjaX6zc,1340
|
29
26
|
pycoze/ui/__init__.py,sha256=uaXet23wUk64TcZjpBX8qOx4aUhwA_ucrmcxy7Q4Qr4,929
|
@@ -36,8 +33,8 @@ pycoze/utils/arg.py,sha256=jop1tBfe5hYkHW1NSpCeaZBEznkgguBscj_7M2dWfrs,503
|
|
36
33
|
pycoze/utils/env.py,sha256=5pWlXfM1F5ZU9hhv1rHlDEanjEW5wf0nbyez9bNRqqA,559
|
37
34
|
pycoze/utils/socket.py,sha256=bZbFFRH4mfThzRqt55BAAGQ6eICx_ja4x8UGGrUdAm8,2428
|
38
35
|
pycoze/utils/text_or_file.py,sha256=gpxZVWt2DW6YiEg_MnMuwg36VNf3TX383QD_1oZNB0Y,551
|
39
|
-
pycoze-0.1.
|
40
|
-
pycoze-0.1.
|
41
|
-
pycoze-0.1.
|
42
|
-
pycoze-0.1.
|
43
|
-
pycoze-0.1.
|
36
|
+
pycoze-0.1.334.dist-info/LICENSE,sha256=QStd_Qsd0-kAam_-sOesCIp_uKrGWeoKwt9M49NVkNU,1090
|
37
|
+
pycoze-0.1.334.dist-info/METADATA,sha256=3ILPNMNEHtM9bWi3C9mmZvzCxaa0kxmz3_QGewzOUbk,854
|
38
|
+
pycoze-0.1.334.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
39
|
+
pycoze-0.1.334.dist-info/top_level.txt,sha256=76dPeDhKvOCleL3ZC5gl1-y4vdS1tT_U1hxWVAn7sFo,7
|
40
|
+
pycoze-0.1.334.dist-info/RECORD,,
|
pycoze/bot/agent/__init__.py
DELETED
pycoze/bot/agent/agent.py
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import json
|
3
|
-
from langchain_openai import ChatOpenAI
|
4
|
-
from .chat import info
|
5
|
-
from .assistant import Runnable
|
6
|
-
from langchain_core.messages import (
|
7
|
-
HumanMessage,
|
8
|
-
AIMessage,
|
9
|
-
AIMessageChunk,
|
10
|
-
SystemMessage,
|
11
|
-
)
|
12
|
-
from .agent_types.const import HumanToolString
|
13
|
-
|
14
|
-
|
15
|
-
async def run_agent(agent, inputs: list, tool_compatibility_mode: bool):
|
16
|
-
exist_ids = set()
|
17
|
-
content_list = []
|
18
|
-
async for event in agent.astream_events(inputs, version="v2"):
|
19
|
-
kind = event["event"]
|
20
|
-
if kind == "on_chain_end":
|
21
|
-
if "data" in event:
|
22
|
-
if (
|
23
|
-
"output" in event["data"]
|
24
|
-
and event["data"]["output"] == "end"
|
25
|
-
and "input" in event["data"]
|
26
|
-
and isinstance(event["data"]["input"], list)
|
27
|
-
):
|
28
|
-
input_list = event["data"]["input"]
|
29
|
-
for msg in input_list:
|
30
|
-
if isinstance(msg, HumanMessage) or isinstance(
|
31
|
-
msg, SystemMessage
|
32
|
-
):
|
33
|
-
if (
|
34
|
-
not tool_compatibility_mode
|
35
|
-
or not msg.content.startswith(HumanToolString)
|
36
|
-
):
|
37
|
-
content_list = [] # 防止内容重复
|
38
|
-
if isinstance(msg, AIMessage) and not isinstance(
|
39
|
-
msg, AIMessageChunk
|
40
|
-
):
|
41
|
-
content = msg.content
|
42
|
-
if content:
|
43
|
-
content_list.append(content)
|
44
|
-
elif kind == "on_chat_model_stream":
|
45
|
-
content = event["data"]["chunk"].content
|
46
|
-
if content:
|
47
|
-
info("assistant", content)
|
48
|
-
elif kind == "on_chain_start":
|
49
|
-
data = event["data"]
|
50
|
-
if "input" in data:
|
51
|
-
input_list = (
|
52
|
-
data["input"]
|
53
|
-
if isinstance(data["input"], list)
|
54
|
-
else [data["input"]]
|
55
|
-
)
|
56
|
-
if len(input_list) == 0:
|
57
|
-
continue
|
58
|
-
msg = input_list[-1]
|
59
|
-
if isinstance(msg, AIMessage) and not isinstance(msg, AIMessageChunk):
|
60
|
-
if "tool_calls" in msg.additional_kwargs:
|
61
|
-
tool_calls = msg.additional_kwargs["tool_calls"]
|
62
|
-
for t in tool_calls:
|
63
|
-
if t["id"] in exist_ids:
|
64
|
-
continue
|
65
|
-
exist_ids.add(t["id"])
|
66
|
-
tool = t["function"]["name"]
|
67
|
-
info("assistant", f"\n[调用工具:{tool}]\n\n")
|
68
|
-
|
69
|
-
return "\n".join(content_list)
|
70
|
-
|
71
|
-
|
72
|
-
if __name__ == "__main__":
|
73
|
-
from langchain_experimental.tools import PythonREPLTool
|
74
|
-
import threading
|
75
|
-
|
76
|
-
llm_file = r"C:\Users\aiqqq\AppData\Roaming\pycoze\JsonStorage\llm.json"
|
77
|
-
with open(llm_file, "r", encoding="utf-8") as f:
|
78
|
-
cfg = json.load(f)
|
79
|
-
chat = ChatOpenAI(
|
80
|
-
api_key=cfg["apiKey"],
|
81
|
-
base_url=cfg["baseURL"],
|
82
|
-
model=cfg["model"],
|
83
|
-
temperature=0,
|
84
|
-
)
|
85
|
-
python_tool = PythonREPLTool()
|
86
|
-
agent = Runnable(
|
87
|
-
agent_execution_mode="FuncCall",
|
88
|
-
tools=[python_tool],
|
89
|
-
llm=chat,
|
90
|
-
assistant_message="请以女友的口吻回答,输出不小于100字,可以随便说点其他的",
|
91
|
-
tool_compatibility_mode=False,
|
92
|
-
)
|
93
|
-
|
94
|
-
inputs = [HumanMessage(content="计算根号7+根号88")]
|
95
|
-
print(asyncio.run(run_agent(agent, inputs, True, threading.Event())))
|
@@ -1 +0,0 @@
|
|
1
|
-
HumanToolString = 'The tool call is done, the result is as follows:\n'
|
@@ -1,181 +0,0 @@
|
|
1
|
-
# reference:https://github.com/maxtheman/opengpts/blob/d3425b1ba80aec48953a327ecd9a61b80efb0e69/backend/app/agent_types/openai_agent.py
|
2
|
-
import json
|
3
|
-
from langchain.tools import BaseTool
|
4
|
-
from langchain_core.utils.function_calling import convert_to_openai_tool
|
5
|
-
from langchain_core.language_models.base import LanguageModelLike
|
6
|
-
from langchain_core.messages import SystemMessage, ToolMessage, HumanMessage
|
7
|
-
from langgraph.graph import END
|
8
|
-
from langgraph.graph.message import MessageGraph
|
9
|
-
from langgraph.prebuilt import ToolExecutor, ToolInvocation
|
10
|
-
import re
|
11
|
-
import json
|
12
|
-
import random
|
13
|
-
from .const import HumanToolString
|
14
|
-
|
15
|
-
|
16
|
-
def get_all_markdown_json(content):
|
17
|
-
# Find all markdown json blocks
|
18
|
-
markdown_json_blocks = re.findall(r"```json(.*?)```", content, re.DOTALL)
|
19
|
-
json_list = []
|
20
|
-
|
21
|
-
for block in markdown_json_blocks:
|
22
|
-
try:
|
23
|
-
# Remove any leading/trailing whitespace and parse the JSON
|
24
|
-
json_data = json.loads(block.strip())
|
25
|
-
json_list.append(json_data)
|
26
|
-
except json.JSONDecodeError:
|
27
|
-
# If the block is not valid JSON, skip it
|
28
|
-
continue
|
29
|
-
|
30
|
-
return json_list
|
31
|
-
|
32
|
-
|
33
|
-
def get_tools(last_message):
|
34
|
-
if "tool_calls" in last_message.additional_kwargs:
|
35
|
-
return last_message.additional_kwargs["tool_calls"]
|
36
|
-
else:
|
37
|
-
tool_calls = []
|
38
|
-
if '"name"' in last_message.content and '"parameters":' in last_message.content:
|
39
|
-
print("name 和 paremeters 模式")
|
40
|
-
all_json = get_all_markdown_json(last_message.content)
|
41
|
-
tool_calls = []
|
42
|
-
for tool_call in all_json:
|
43
|
-
if "name" not in tool_call or "parameters" not in tool_call:
|
44
|
-
return "end"
|
45
|
-
tool_call["arguments"] = json.dumps(tool_call["parameters"])
|
46
|
-
tool_call.pop("parameters")
|
47
|
-
tool_calls.append(
|
48
|
-
{
|
49
|
-
"function": tool_call,
|
50
|
-
"id": random.randint(0, 1000000),
|
51
|
-
}
|
52
|
-
)
|
53
|
-
if "<|tool▁sep|>" in last_message.content:
|
54
|
-
print("deepseek的bug: <|tool▁sep|> 模式")
|
55
|
-
name = (
|
56
|
-
last_message.content.split("<|tool▁sep|>")[1].split("```")[0].strip()
|
57
|
-
)
|
58
|
-
all_json = get_all_markdown_json(last_message.content)
|
59
|
-
tool_calls = []
|
60
|
-
for argument in all_json:
|
61
|
-
tool_calls.append(
|
62
|
-
{
|
63
|
-
"function": {
|
64
|
-
"name": name,
|
65
|
-
"arguments": json.dumps(argument),
|
66
|
-
},
|
67
|
-
"id": random.randint(0, 1000000),
|
68
|
-
}
|
69
|
-
)
|
70
|
-
|
71
|
-
return tool_calls
|
72
|
-
|
73
|
-
|
74
|
-
def create_openai_func_call_agent_executor(
|
75
|
-
tools: list[BaseTool],
|
76
|
-
llm: LanguageModelLike,
|
77
|
-
system_message: str,
|
78
|
-
tool_compatibility_mode: str,
|
79
|
-
**kwargs
|
80
|
-
):
|
81
|
-
|
82
|
-
async def _get_messages(messages):
|
83
|
-
msgs = []
|
84
|
-
for m in messages:
|
85
|
-
if isinstance(m, ToolMessage):
|
86
|
-
_dict = m.model_dump()
|
87
|
-
_dict["content"] = str(_dict["content"])
|
88
|
-
m_c = ToolMessage(**_dict)
|
89
|
-
msgs.append(m_c)
|
90
|
-
else:
|
91
|
-
msgs.append(m)
|
92
|
-
|
93
|
-
return [SystemMessage(content=system_message)] + msgs
|
94
|
-
|
95
|
-
if tools and not tool_compatibility_mode:
|
96
|
-
llm_with_tools = llm.bind(tools=[convert_to_openai_tool(t) for t in tools])
|
97
|
-
else:
|
98
|
-
llm_with_tools = llm
|
99
|
-
agent = _get_messages | llm_with_tools
|
100
|
-
tool_executor = ToolExecutor(tools)
|
101
|
-
|
102
|
-
# Define the function that determines whether to continue or not
|
103
|
-
def should_continue(messages):
|
104
|
-
# If there is no FuncCall, then we finish
|
105
|
-
last_message = messages[-1]
|
106
|
-
if last_message.content.strip().endswith("```"):
|
107
|
-
last_message.content = last_message.content + "\n\n" # 避免影响阅读
|
108
|
-
tools = get_tools(last_message)
|
109
|
-
if last_message.tool_calls or tools:
|
110
|
-
return "continue"
|
111
|
-
return "end"
|
112
|
-
|
113
|
-
# Define the function to execute tools
|
114
|
-
async def call_tool(messages):
|
115
|
-
actions: list[ToolInvocation] = []
|
116
|
-
# Based on the continue condition
|
117
|
-
# we know the last message involves a FuncCall
|
118
|
-
last_message = messages[-1]
|
119
|
-
for tool_call in get_tools(last_message):
|
120
|
-
function = tool_call["function"]
|
121
|
-
function_name = function["name"]
|
122
|
-
if function_name == "a_delay_function":
|
123
|
-
return [
|
124
|
-
ToolMessage(
|
125
|
-
tool_call_id=tool_call["id"],
|
126
|
-
content="a_delay_function只是一个占位符,请忽略重新调用工具",
|
127
|
-
additional_kwargs={"name": tool_call["function"]["name"]},
|
128
|
-
)
|
129
|
-
]
|
130
|
-
|
131
|
-
_tool_input = json.loads(function["arguments"] or "{}")
|
132
|
-
# We construct an ToolInvocation from the function_call
|
133
|
-
actions.append(
|
134
|
-
ToolInvocation(
|
135
|
-
tool=function_name,
|
136
|
-
tool_input=_tool_input,
|
137
|
-
)
|
138
|
-
)
|
139
|
-
# We call the tool_executor and get back a response
|
140
|
-
responses = await tool_executor.abatch(actions, **kwargs)
|
141
|
-
# We use the response to create a ToolMessage
|
142
|
-
tool_messages = []
|
143
|
-
for tool_call, response in zip(get_tools(last_message), responses):
|
144
|
-
if not isinstance(response, (str, int, float, bool, list, tuple)):
|
145
|
-
response = repr(
|
146
|
-
response
|
147
|
-
) # 不支持其他类型,包括dict也不支持,因此需要转换为字符串
|
148
|
-
|
149
|
-
message = ToolMessage(
|
150
|
-
tool_call_id=tool_call["id"],
|
151
|
-
content=response,
|
152
|
-
additional_kwargs={"name": tool_call["function"]["name"]},
|
153
|
-
)
|
154
|
-
tool_messages.append(message)
|
155
|
-
if tool_compatibility_mode:
|
156
|
-
# HumanMessage
|
157
|
-
tool_msgs_str = repr(tool_messages)
|
158
|
-
tool_messages = [HumanMessage(content=HumanToolString + tool_msgs_str)]
|
159
|
-
return tool_messages
|
160
|
-
|
161
|
-
workflow = MessageGraph()
|
162
|
-
|
163
|
-
# Define the two nodes we will cycle between
|
164
|
-
workflow.add_node("agent", agent)
|
165
|
-
workflow.add_node("call_tool", call_tool)
|
166
|
-
|
167
|
-
workflow.set_entry_point("agent")
|
168
|
-
|
169
|
-
workflow.add_conditional_edges(
|
170
|
-
"agent",
|
171
|
-
should_continue,
|
172
|
-
{
|
173
|
-
"continue": "call_tool",
|
174
|
-
"end": END,
|
175
|
-
},
|
176
|
-
)
|
177
|
-
|
178
|
-
# 调用完工具后,再次调用agent
|
179
|
-
workflow.add_edge("call_tool", "agent")
|
180
|
-
|
181
|
-
return workflow.compile()
|
pycoze/bot/agent/assistant.py
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
from typing import Sequence
|
2
|
-
from langchain.tools import BaseTool
|
3
|
-
from langchain_core.language_models.base import LanguageModelLike
|
4
|
-
from langchain_core.runnables import RunnableBinding
|
5
|
-
from .agent_types import create_openai_func_call_agent_executor
|
6
|
-
|
7
|
-
|
8
|
-
class Runnable(RunnableBinding):
|
9
|
-
agent_execution_mode: str
|
10
|
-
tools: Sequence[BaseTool]
|
11
|
-
llm: LanguageModelLike
|
12
|
-
assistant_message: str
|
13
|
-
|
14
|
-
def __init__(
|
15
|
-
self,
|
16
|
-
*,
|
17
|
-
agent_execution_mode: str,
|
18
|
-
tools: Sequence[BaseTool],
|
19
|
-
llm: LanguageModelLike,
|
20
|
-
assistant_message: str,
|
21
|
-
tool_compatibility_mode: bool
|
22
|
-
) -> None:
|
23
|
-
|
24
|
-
agent_executor = create_openai_func_call_agent_executor(
|
25
|
-
tools, llm, assistant_message, tool_compatibility_mode
|
26
|
-
)
|
27
|
-
agent_executor = agent_executor.with_config({"recursion_limit": 50})
|
28
|
-
super().__init__(
|
29
|
-
tools=tools if not tool_compatibility_mode else [],
|
30
|
-
llm=llm,
|
31
|
-
agent_execution_mode=agent_execution_mode,
|
32
|
-
assistant_message=assistant_message,
|
33
|
-
bound=agent_executor,
|
34
|
-
return_intermediate_steps=True,
|
35
|
-
)
|
pycoze/bot/agent_chat.py
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from langchain_openai import ChatOpenAI
|
3
|
-
from .agent import run_agent, Runnable,CHAT_DATA, clear_chat_data
|
4
|
-
import asyncio
|
5
|
-
from pycoze import utils
|
6
|
-
from pycoze.reference.bot import ref_bot
|
7
|
-
from pycoze.reference.tool import ref_tools
|
8
|
-
from pycoze.reference.workflow import ref_workflow
|
9
|
-
from langchain_core.utils.function_calling import convert_to_openai_tool
|
10
|
-
import os
|
11
|
-
|
12
|
-
cfg = utils.read_json_file("llm.json")
|
13
|
-
|
14
|
-
def load_bot_setting(bot_setting_file: str):
|
15
|
-
with open(bot_setting_file, "r", encoding="utf-8") as f:
|
16
|
-
return json.load(f)
|
17
|
-
|
18
|
-
|
19
|
-
def load_abilities(bot_setting_file: str):
|
20
|
-
with open(bot_setting_file, "r", encoding="utf-8") as f:
|
21
|
-
bot_setting = json.load(f)
|
22
|
-
|
23
|
-
abilities = []
|
24
|
-
for bot_id in bot_setting["bots"]:
|
25
|
-
bot = ref_bot(bot_id, as_agent_tool=True)
|
26
|
-
if bot:
|
27
|
-
abilities.append(bot)
|
28
|
-
for tool_id in bot_setting["tools"]:
|
29
|
-
abilities.extend(ref_tools(tool_id, as_agent_tool=True))
|
30
|
-
for workflow_id in bot_setting["workflows"]:
|
31
|
-
workflow = ref_workflow(workflow_id, as_agent_tool=True)
|
32
|
-
if workflow:
|
33
|
-
abilities.append(workflow)
|
34
|
-
return abilities
|
35
|
-
|
36
|
-
|
37
|
-
async def check_interrupt_file(interval, interrupt_file,agent_task):
|
38
|
-
while True:
|
39
|
-
await asyncio.sleep(interval)
|
40
|
-
if os.path.exists(interrupt_file):
|
41
|
-
os.remove(interrupt_file)
|
42
|
-
agent_task.cancel()
|
43
|
-
break
|
44
|
-
|
45
|
-
async def run_with_interrupt_check(agent, history, tool_compatibility_mode, interrupt_file, check_interval=1):
|
46
|
-
clear_chat_data()
|
47
|
-
try:
|
48
|
-
agent_task = asyncio.create_task(run_agent(agent, history, tool_compatibility_mode))
|
49
|
-
check_task = asyncio.create_task(check_interrupt_file(check_interval, interrupt_file, agent_task))
|
50
|
-
result = await agent_task
|
51
|
-
return result
|
52
|
-
except asyncio.CancelledError:
|
53
|
-
return CHAT_DATA['info']
|
54
|
-
except Exception as e:
|
55
|
-
import traceback
|
56
|
-
print(traceback.format_exc())
|
57
|
-
return None # 返回 None 或者处理异常后的结果
|
58
|
-
finally:
|
59
|
-
if not agent_task.done():
|
60
|
-
agent_task.cancel()
|
61
|
-
# 确保即使发生异常也会取消检查任务
|
62
|
-
if not check_task.done():
|
63
|
-
check_task.cancel()
|
64
|
-
try:
|
65
|
-
await check_task
|
66
|
-
except asyncio.CancelledError:
|
67
|
-
pass # 忽略取消错误
|
68
|
-
|
69
|
-
async def agent_chat(bot_setting_file, history):
|
70
|
-
bot_setting = load_bot_setting(bot_setting_file)
|
71
|
-
abilities = load_abilities(bot_setting_file)
|
72
|
-
|
73
|
-
chat = ChatOpenAI(
|
74
|
-
api_key=cfg["apiKey"],
|
75
|
-
base_url=cfg["baseURL"],
|
76
|
-
model=cfg["model"],
|
77
|
-
temperature=bot_setting["temperature"],
|
78
|
-
stop_sequences=[
|
79
|
-
"tool▁calls▁end",
|
80
|
-
"tool▁call▁end",
|
81
|
-
], # 不然会虚构工具调用过程和结果
|
82
|
-
)
|
83
|
-
prompt = bot_setting["prompt"]
|
84
|
-
if cfg["toolCompatibilityMode"] and len(abilities) > 0:
|
85
|
-
prompt += """
|
86
|
-
作为一个AI,你如果不确定结果,请务必使用工具查询。
|
87
|
-
你可以通过下面的方式使用工具,并耐心等待工具返回结果。
|
88
|
-
如果你需要调用工具,请使用以正确markdown中的json代码格式进行结尾(务必保证json格式正确,不要出现反斜杠未转义等问题):
|
89
|
-
```json
|
90
|
-
{"name": 函数名, "parameters": 参数词典}
|
91
|
-
```
|
92
|
-
"""
|
93
|
-
prompt += "\nAvailable functions:\n"
|
94
|
-
for t in abilities:
|
95
|
-
prompt += f"\n```json\n{json.dumps(convert_to_openai_tool(t))}\n```"
|
96
|
-
agent = Runnable(
|
97
|
-
agent_execution_mode="FuncCall",
|
98
|
-
tools=abilities,
|
99
|
-
llm=chat,
|
100
|
-
assistant_message=prompt,
|
101
|
-
tool_compatibility_mode=cfg["toolCompatibilityMode"],
|
102
|
-
)
|
103
|
-
params = utils.params
|
104
|
-
if "interruptFile" in params:
|
105
|
-
interrupt_file_path = params["interruptFile"]
|
106
|
-
result = await run_with_interrupt_check(agent, history, cfg["toolCompatibilityMode"], interrupt_file_path)
|
107
|
-
else:
|
108
|
-
result = await run_agent(agent, history, cfg["toolCompatibilityMode"])
|
109
|
-
return result
|
110
|
-
|
pycoze/bot/bot.py
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
from langchain_core.messages import HumanMessage, AIMessage
|
2
|
-
import json
|
3
|
-
from .agent import INPUT_MESSAGE, output, CHAT_DATA, clear_chat_data
|
4
|
-
from .agent_chat import agent_chat
|
5
|
-
import asyncio
|
6
|
-
|
7
|
-
|
8
|
-
def chat(bot_setting_file: str):
|
9
|
-
history = []
|
10
|
-
while True:
|
11
|
-
input_text = input()
|
12
|
-
if not input_text.startswith(INPUT_MESSAGE):
|
13
|
-
raise ValueError("Invalid message")
|
14
|
-
message = json.loads(input_text[len(INPUT_MESSAGE) :])
|
15
|
-
history.append(HumanMessage(message["content"]))
|
16
|
-
result = asyncio.run(agent_chat(bot_setting_file, history))
|
17
|
-
output("assistant", result)
|
18
|
-
history.append(AIMessage(result))
|
19
|
-
|
20
|
-
|
21
|
-
def get_chat_response(bot_setting_file: str, input_text: str):
|
22
|
-
result = asyncio.run(agent_chat(bot_setting_file, [HumanMessage(input_text)]))
|
23
|
-
return result
|
File without changes
|
File without changes
|
File without changes
|