forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchat.py
More file actions
121 lines (102 loc) · 4.01 KB
/
Copy pathchat.py
File metadata and controls
121 lines (102 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""Chat Node"""
from typing import List, cast
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import SystemMessage, AIMessage, ToolMessage
from langchain.tools import tool
from copilotkit.langchain import copilotkit_customize_config
from copilotkit.demos.research_canvas.state import AgentState
from copilotkit.demos.research_canvas.model import get_model
from copilotkit.demos.research_canvas.download import get_resource
@tool
def Search(queries: List[str]): # pylint: disable=invalid-name,unused-argument
"""A list of one or more search queries to find good resources to support the research."""
@tool
def WriteReport(report: str): # pylint: disable=invalid-name,unused-argument
"""Write the research report."""
@tool
def WriteResearchQuestion(research_question: str): # pylint: disable=invalid-name,unused-argument
"""Write the research question."""
@tool
def DeleteResources(urls: List[str]): # pylint: disable=invalid-name,unused-argument
"""Delete the URLs from the resources."""
async def chat_node(state: AgentState, config: RunnableConfig):
"""
Chat Node
"""
config = copilotkit_customize_config(
config,
emit_intermediate_state=[{
"state_key": "report",
"tool": "WriteReport",
"tool_argument": "report",
}, {
"state_key": "research_question",
"tool": "WriteResearchQuestion",
"tool_argument": "research_question",
}],
emit_tool_calls="DeleteResources"
)
state["resources"] = state.get("resources", [])
research_question = state.get("research_question", "")
report = state.get("report", "")
resources = []
for resource in state["resources"]:
content = get_resource(resource["url"])
if content == "ERROR":
continue
resources.append({
**resource,
"content": content
})
model = get_model(state)
# Prepare the kwargs for the ainvoke method
ainvoke_kwargs = {}
if model.__class__.__name__ in ["ChatOpenAI"]:
ainvoke_kwargs["parallel_tool_calls"] = False
response = await model.bind_tools(
[
Search,
WriteReport,
WriteResearchQuestion,
DeleteResources,
],
**ainvoke_kwargs
).ainvoke([
SystemMessage(
content=f"""
You are a research assistant. You help the user with writing a research report.
Do not recite the resources, instead use them to answer the user's question.
You should use the search tool to get resources before answering the user's question.
If you finished writing the report, ask the user proactively for next steps, changes etc, make it engaging.
To write the report, you should use the WriteReport tool. Never EVER respond with the report, only use the tool.
This is the research question:
{research_question}
This is the research report:
{report}
Here are the resources that you have available:
{resources}
"""
),
*state["messages"],
], config)
ai_message = cast(AIMessage, response)
if ai_message.tool_calls:
if ai_message.tool_calls[0]["name"] == "WriteReport":
return {
"report": ai_message.tool_calls[0]["args"]["report"],
"messages": [ai_message, ToolMessage(
tool_call_id=ai_message.tool_calls[0]["id"],
content="Report written."
)]
}
if ai_message.tool_calls[0]["name"] == "WriteResearchQuestion":
return {
"research_question": ai_message.tool_calls[0]["args"]["research_question"],
"messages": [ai_message, ToolMessage(
tool_call_id=ai_message.tool_calls[0]["id"],
content="Research question written."
)]
}
return {
"messages": response
}