Maga222006
commited on
Commit
·
7236cee
1
Parent(s):
b616ae5
MultiagentPersonalAssistant
Browse files- .idea/ai_toolkit.xml +6 -0
- .idea/vcs.xml +1 -1
- agent/__pycache__/coder_agent.cpython-312.pyc +0 -0
- agent/__pycache__/deep_research_agent.cpython-312.pyc +0 -0
- agent/__pycache__/file_preprocessing.cpython-312.pyc +0 -0
- agent/__pycache__/models.cpython-312.pyc +0 -0
- agent/__pycache__/multi_agent.cpython-312.pyc +0 -0
- agent/__pycache__/prompts.cpython-312.pyc +0 -0
- agent/__pycache__/smart_home_agent.cpython-312.pyc +0 -0
- agent/__pycache__/states.cpython-312.pyc +0 -0
- agent/__pycache__/tools.cpython-311.pyc +0 -0
- agent/__pycache__/tools.cpython-312.pyc +0 -0
- agent/coder_agent.py +0 -39
- agent/deep_research_agent.py +0 -39
- agent/prompts.py +0 -74
- agent/smart_home_agent.py +0 -30
- agent/states.py +0 -13
- agents/coder/agent.py +34 -0
- agents/coder/prompts.py +21 -0
- agents/coder/states.py +5 -0
- agents/coder/tools.py +239 -0
- {agent → agents}/models.py +5 -16
- {agent → agents}/multi_agent.py +19 -61
- agents/prompts.py +47 -0
- agents/states.py +22 -0
- agents/tools.py +74 -0
- {agent → agents/utils}/file_preprocessing.py +1 -1
- agent/tools.py → agents/utils/smart_home.py +2 -180
- app.py +2 -2
.idea/ai_toolkit.xml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="AI Toolkit Settings">
|
| 4 |
+
<option name="importsOfInterestPresent" value="true" />
|
| 5 |
+
</component>
|
| 6 |
+
</project>
|
.idea/vcs.xml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
<project version="4">
|
| 3 |
<component name="VcsDirectoryMappings">
|
| 4 |
-
<mapping directory="
|
| 5 |
</component>
|
| 6 |
</project>
|
|
|
|
| 1 |
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
<project version="4">
|
| 3 |
<component name="VcsDirectoryMappings">
|
| 4 |
+
<mapping directory="" vcs="Git" />
|
| 5 |
</component>
|
| 6 |
</project>
|
agent/__pycache__/coder_agent.cpython-312.pyc
DELETED
|
Binary file (2.02 kB)
|
|
|
agent/__pycache__/deep_research_agent.cpython-312.pyc
DELETED
|
Binary file (2.09 kB)
|
|
|
agent/__pycache__/file_preprocessing.cpython-312.pyc
DELETED
|
Binary file (3.77 kB)
|
|
|
agent/__pycache__/models.cpython-312.pyc
DELETED
|
Binary file (885 Bytes)
|
|
|
agent/__pycache__/multi_agent.cpython-312.pyc
DELETED
|
Binary file (10.3 kB)
|
|
|
agent/__pycache__/prompts.cpython-312.pyc
DELETED
|
Binary file (5.58 kB)
|
|
|
agent/__pycache__/smart_home_agent.cpython-312.pyc
DELETED
|
Binary file (1.61 kB)
|
|
|
agent/__pycache__/states.cpython-312.pyc
DELETED
|
Binary file (1.54 kB)
|
|
|
agent/__pycache__/tools.cpython-311.pyc
DELETED
|
Binary file (13.5 kB)
|
|
|
agent/__pycache__/tools.cpython-312.pyc
DELETED
|
Binary file (15.1 kB)
|
|
|
agent/coder_agent.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
| 1 |
-
from agent.prompts import coder_instructions, coder_system_message
|
| 2 |
-
from langchain_core.messages import HumanMessage, SystemMessage
|
| 3 |
-
from agent.models import llm_agents, llm_peripheral
|
| 4 |
-
from langgraph.prebuilt import create_react_agent
|
| 5 |
-
from langgraph.constants import START, END
|
| 6 |
-
from agent.states import PlanCodingTask
|
| 7 |
-
from langgraph.graph import StateGraph
|
| 8 |
-
from agent.tools import coder_tools
|
| 9 |
-
|
| 10 |
-
agent = create_react_agent(
|
| 11 |
-
llm_agents,
|
| 12 |
-
tools=coder_tools,
|
| 13 |
-
prompt=coder_instructions
|
| 14 |
-
)
|
| 15 |
-
|
| 16 |
-
def planning_node(state: dict):
|
| 17 |
-
planer = llm_peripheral.with_structured_output(PlanCodingTask)
|
| 18 |
-
plan = planer.invoke(state['messages'][-1].content)
|
| 19 |
-
state.update(plan)
|
| 20 |
-
return state
|
| 21 |
-
|
| 22 |
-
def code_agent(state: dict):
|
| 23 |
-
system_message = SystemMessage(coder_system_message(state))
|
| 24 |
-
state.update(agent.invoke({
|
| 25 |
-
'messages': [
|
| 26 |
-
system_message,
|
| 27 |
-
HumanMessage(state['task_description']),
|
| 28 |
-
]
|
| 29 |
-
}))
|
| 30 |
-
return state
|
| 31 |
-
|
| 32 |
-
graph = StateGraph(dict)
|
| 33 |
-
graph.add_node("planning_node", planning_node)
|
| 34 |
-
graph.add_node("code_agent", code_agent)
|
| 35 |
-
graph.add_edge(START, "planning_node")
|
| 36 |
-
graph.add_edge("planning_node", "code_agent")
|
| 37 |
-
graph.add_edge("code_agent", END)
|
| 38 |
-
|
| 39 |
-
coder_agent = graph.compile(name="coder_agent")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agent/deep_research_agent.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
| 1 |
-
from agent.prompts import deep_research_instructions, deep_research_system_message
|
| 2 |
-
from langchain_core.messages import HumanMessage, SystemMessage
|
| 3 |
-
from agent.models import llm_agents, llm_peripheral
|
| 4 |
-
from langgraph.prebuilt import create_react_agent
|
| 5 |
-
from agent.tools import deep_research_tools
|
| 6 |
-
from langgraph.constants import START, END
|
| 7 |
-
from langgraph.graph import StateGraph
|
| 8 |
-
from agent.states import PlanResearch
|
| 9 |
-
|
| 10 |
-
agent = create_react_agent(
|
| 11 |
-
llm_agents,
|
| 12 |
-
tools=deep_research_tools,
|
| 13 |
-
prompt=deep_research_instructions
|
| 14 |
-
)
|
| 15 |
-
|
| 16 |
-
def planning_node(state: dict):
|
| 17 |
-
planer = llm_peripheral.with_structured_output(PlanResearch)
|
| 18 |
-
plan = planer.invoke(state['messages'][-1].content)
|
| 19 |
-
state.update(plan)
|
| 20 |
-
return state
|
| 21 |
-
|
| 22 |
-
def research_agent(state: dict):
|
| 23 |
-
system_message = SystemMessage(deep_research_system_message(state))
|
| 24 |
-
state.update(agent.invoke({
|
| 25 |
-
'messages': [
|
| 26 |
-
system_message,
|
| 27 |
-
HumanMessage(state['messages'][-1].content),
|
| 28 |
-
]
|
| 29 |
-
}))
|
| 30 |
-
return state
|
| 31 |
-
|
| 32 |
-
graph = StateGraph(dict)
|
| 33 |
-
graph.add_node("planning_node", planning_node)
|
| 34 |
-
graph.add_node("research_agent", research_agent)
|
| 35 |
-
graph.add_edge(START, "planning_node")
|
| 36 |
-
graph.add_edge("planning_node", "research_agent")
|
| 37 |
-
graph.add_edge("research_agent", END)
|
| 38 |
-
|
| 39 |
-
deep_research_agent = graph.compile(name="deep_research_agent")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agent/prompts.py
DELETED
|
@@ -1,74 +0,0 @@
|
|
| 1 |
-
coder_instructions = f"""You are a coding expert. Follow these steps every time:\n
|
| 2 |
-
|
| 3 |
-
1. Use the 'web_search' tool to find how to solve the task or retrieve the package documentation and code examples.\n
|
| 4 |
-
2. Create a new GitHub repository for this task using 'create_repo' tool.\n
|
| 5 |
-
3. Use 'commit_file_to_repo' tool to save your solution code into the GitHub repository.\n
|
| 6 |
-
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
smart_home_instructions = f"""You are a smart home control expert. Follow these steps every time:\n
|
| 10 |
-
|
| 11 |
-
1. Use the 'get_device_list' tool to retrieve the list of available devices with their names, ids, and states.\n
|
| 12 |
-
2. Identify the target device(s) based on the user’s request.\n
|
| 13 |
-
3. Use the 'change_device_states' tool to update the states of the selected device(s).\n
|
| 14 |
-
"""
|
| 15 |
-
|
| 16 |
-
deep_research_instructions = f"""You are a deep research expert. Follow these steps every time:\n
|
| 17 |
-
|
| 18 |
-
1. Iterate over the given search queries.\n
|
| 19 |
-
2. Iterate over the following steps untill you have written factually correct report:\n
|
| 20 |
-
2.1. Write a report based on the search results.\n
|
| 21 |
-
2.2. Critically asses the report.\n
|
| 22 |
-
2.3. Conduct additional search using tools to find more for research.\n
|
| 23 |
-
"""
|
| 24 |
-
|
| 25 |
-
def supervisor_instructions(tools: list, agents: list):
|
| 26 |
-
return f"""You are the Supervisor Agent. Your job is to interpret the user’s request and delegate tasks to the available tools and agents to complete the task.
|
| 27 |
-
|
| 28 |
-
You have access to the following agents and tools:
|
| 29 |
-
- Tools: {[tool.name for tool in tools]}
|
| 30 |
-
- Agents: {[agent.name for agent in agents]}
|
| 31 |
-
|
| 32 |
-
1. Understand and break down user’s request into smaller sub-tasks.
|
| 33 |
-
2. Always delegate each sub-task to the best fitting agent/tool (e.g. always delegate any kind of coding or GitHub related task to ‘coder_agent’).
|
| 34 |
-
3. Coordinate tools/agents efficiently, ensuring minimal redundancy.
|
| 35 |
-
4. Validate results before presenting them.
|
| 36 |
-
5. Respond in Telegram Markdown.
|
| 37 |
-
- Use `*bold*`, `_italic_`, `[text](http://example.com)`, `` `inline code` ``, and ``` fenced code blocks ```.
|
| 38 |
-
- Never leave an opening `*`, `_`, `` ` ``, `[`, or ``` without its closing pair.
|
| 39 |
-
- Do not nest Markdown entities.
|
| 40 |
-
- Escape reserved characters (`* _ [ ] ( ) \ ``) if they are not part of a valid entity.
|
| 41 |
-
- Escaping inside entities is not allowed, so entity must be closed first and reopened again: use _snake_\__case_ for italic snake_case and *2*\**2=4* for bold 2*2=4.
|
| 42 |
-
|
| 43 |
-
Your top priority: satisfy the user’s query as effectively and efficiently as possible using the resources at your disposal, while formatting the final output in valid Markdown.
|
| 44 |
-
"""
|
| 45 |
-
|
| 46 |
-
def coder_system_message(state: dict):
|
| 47 |
-
return f"""Your job is to create a coding project based on the user query.
|
| 48 |
-
|
| 49 |
-
1. Create a new GitHub {"private" if state['private'] else "public"} repository, named '{state['repo_name']}' for this task using 'create_repo' tool.\n
|
| 50 |
-
2. Use the 'web_search' tool to research your task.\n
|
| 51 |
-
3. Commit files with the code to your repository.\n
|
| 52 |
-
4. Critically review your code for weak points using 'list_files' and 'read_file' tools.\n
|
| 53 |
-
5. Use 'web_search' for the latest docs and code examples.\n
|
| 54 |
-
6. Adjust the code after the carefully reviewing your code.\n
|
| 55 |
-
"""
|
| 56 |
-
def deep_research_system_message(state: dict):
|
| 57 |
-
return f"""Your job is to conduct deep research based on the user query.\n
|
| 58 |
-
|
| 59 |
-
1. Iterate over the following search queries: {state['search_queries']}\n
|
| 60 |
-
2. Write an extensive report based on web search results.\n
|
| 61 |
-
3. Critically review your report for weak points.\n
|
| 62 |
-
4. Conduct additional research using available tools.\t
|
| 63 |
-
5. Write an extensive report based on web search results.\n
|
| 64 |
-
6. Critically review your report and cicle over these steps until it is factually correct and very detailed.\n
|
| 65 |
-
"""
|
| 66 |
-
|
| 67 |
-
def smart_home_system_message(device_list: list):
|
| 68 |
-
return f"""Your job is to control the smart home.\nYou have access to the following devices:\n
|
| 69 |
-
{"\n\n".join(
|
| 70 |
-
f"{i+1}. {device['name']} (id: {device['id']})\nStates:\n" +
|
| 71 |
-
"\n".join(f"{state}" for state in device['states'])
|
| 72 |
-
for i, device in enumerate(device_list)
|
| 73 |
-
)}.\nUse the given tools ('get_device_list', 'change_device_states') to complete the user's tasks.
|
| 74 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agent/smart_home_agent.py
DELETED
|
@@ -1,30 +0,0 @@
|
|
| 1 |
-
from agent.prompts import smart_home_instructions, smart_home_system_message
|
| 2 |
-
from langchain_core.messages import HumanMessage, SystemMessage
|
| 3 |
-
from agent.models import llm_agents, llm_peripheral
|
| 4 |
-
from langgraph.prebuilt import create_react_agent
|
| 5 |
-
from langgraph.constants import START, END
|
| 6 |
-
from langgraph.graph import StateGraph
|
| 7 |
-
from agent.tools import smart_home_tools, get_device_list
|
| 8 |
-
|
| 9 |
-
agent = create_react_agent(
|
| 10 |
-
llm_agents,
|
| 11 |
-
tools=smart_home_tools,
|
| 12 |
-
prompt=smart_home_instructions
|
| 13 |
-
)
|
| 14 |
-
|
| 15 |
-
def home_control_agent(state: dict):
|
| 16 |
-
system_message = SystemMessage(smart_home_system_message(get_device_list.invoke({})))
|
| 17 |
-
state.update(agent.invoke({
|
| 18 |
-
'messages': [
|
| 19 |
-
system_message,
|
| 20 |
-
HumanMessage(state['messages'][-1].content),
|
| 21 |
-
]
|
| 22 |
-
}))
|
| 23 |
-
return state
|
| 24 |
-
|
| 25 |
-
graph = StateGraph(dict)
|
| 26 |
-
graph.add_node("home_control_agent", home_control_agent)
|
| 27 |
-
graph.add_edge(START, "home_control_agent")
|
| 28 |
-
graph.add_edge("home_control_agent", END)
|
| 29 |
-
|
| 30 |
-
smart_home_agent = graph.compile(name="smart_home_agent")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agent/states.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
| 1 |
-
from typing import TypedDict, List
|
| 2 |
-
from pydantic import Field
|
| 3 |
-
|
| 4 |
-
class SearchQuery(TypedDict):
|
| 5 |
-
query: str = Field(description="A single plain text search query string.")
|
| 6 |
-
|
| 7 |
-
class PlanResearch(TypedDict):
|
| 8 |
-
search_queries: List[SearchQuery] = Field(description="A list of search queries, to find all the info user asked for. Break user query down into smaller search queries.")
|
| 9 |
-
|
| 10 |
-
class PlanCodingTask(TypedDict):
|
| 11 |
-
repo_name: str = Field(description="The name of the GitHub repository for the project.")
|
| 12 |
-
private: bool = Field(description="Whether or not the repository is private.", default=False)
|
| 13 |
-
task_description: str = Field(description="A detailed description of the project for the coder to create.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
agents/coder/agent.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_core.messages import HumanMessage, SystemMessage
|
| 2 |
+
from agents.coder.prompts import prompt, system_message
|
| 3 |
+
from langgraph.prebuilt import create_react_agent
|
| 4 |
+
from agents.coder.states import CoderState
|
| 5 |
+
from agents.models import llm_sub_agents
|
| 6 |
+
from agents.coder.tools import tools
|
| 7 |
+
from langchain.tools import tool
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
+
import os
|
| 10 |
+
|
| 11 |
+
load_dotenv()
|
| 12 |
+
|
| 13 |
+
agent = create_react_agent(
|
| 14 |
+
llm_sub_agents,
|
| 15 |
+
tools=tools,
|
| 16 |
+
prompt=prompt,
|
| 17 |
+
state_schema=CoderState,
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
@tool
|
| 21 |
+
def coder_agent(project_name: str, task_description: str, private: bool=False):
|
| 22 |
+
"""
|
| 23 |
+
Coder Agent, used as a tool for any coding tasks, it creates project, tests it and saves to GitHub.
|
| 24 |
+
|
| 25 |
+
Args:
|
| 26 |
+
project_name (str): The name of the GitHub repository and directory for the project.
|
| 27 |
+
task_description (str): A detailed description of the project for the coder to create.
|
| 28 |
+
private (bool, optional): Whether the coder should be private or public. Defaults to False.
|
| 29 |
+
"""
|
| 30 |
+
path = f"agents/coder/projects/{project_name}"
|
| 31 |
+
messages = agent.invoke({'messages': [SystemMessage(system_message(project_name, private)), HumanMessage(content=task_description)], 'project_name': project_name, 'private': private})
|
| 32 |
+
if os.path.isdir(path):
|
| 33 |
+
os.rmdir(path)
|
| 34 |
+
return messages['messages'][-1].content
|
agents/coder/prompts.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
prompt = """
|
| 2 |
+
You are a coding expert.
|
| 3 |
+
Always follow these steps:
|
| 4 |
+
|
| 5 |
+
1. Create a new project for this task using 'create_project' or use existing project with 'download_repo' tool.\n
|
| 6 |
+
2. Use 'write_file' tool to save your solution code into the GitHub repository.\n
|
| 7 |
+
3. Test the code and review it until it works.
|
| 8 |
+
4. Tell the user you're done.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
def system_message(project_name: str, private: bool=False) -> str:
|
| 12 |
+
return f"""Your job is to create a coding project based on the user query.\n
|
| 13 |
+
|
| 14 |
+
1. Create a new GitHub {"private" if private else "public"} repository and project directory, named '{project_name}' for this task using 'create_project' tool or take existing GitHub repository to adjust using 'download_repo' tool if the user request implies adjusting the project instead of creating it from scratch. \n
|
| 15 |
+
2. Use 'tavily_search' tool to retrieve the code examples, documentation and tutorials for the task.\n
|
| 16 |
+
3. Write files with the code to your project using 'write_code' tool.\n
|
| 17 |
+
4. Test your code using 'run_cmd' tool.\n
|
| 18 |
+
5. Critically review your code for weak points using 'list_files' and 'read_file' tools.\n
|
| 19 |
+
6. Adjust the code after the carefully reviewing and testing your code.\n
|
| 20 |
+
7. Tell the user you're done.
|
| 21 |
+
"""
|
agents/coder/states.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langgraph.prebuilt.chat_agent_executor import AgentState
|
| 2 |
+
|
| 3 |
+
class CoderState(AgentState):
|
| 4 |
+
project_name: str
|
| 5 |
+
private: bool = False
|
agents/coder/tools.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_tavily import TavilySearch
|
| 2 |
+
from langchain_core.tools import tool
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
from github import Github
|
| 5 |
+
import subprocess
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
load_dotenv()
|
| 9 |
+
|
| 10 |
+
tavily_search = TavilySearch(
|
| 11 |
+
max_results=5,
|
| 12 |
+
topic="general",
|
| 13 |
+
include_answer=False
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
@tool
|
| 17 |
+
def create_project(project_name: str, private: bool = False):
|
| 18 |
+
"""Creates a project directory and GitHub repository with the given project_name."""
|
| 19 |
+
try:
|
| 20 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 21 |
+
user = github.get_user()
|
| 22 |
+
user.create_repo(project_name, private=private)
|
| 23 |
+
os.mkdir(f"agents/coder/projects/{project_name}")
|
| 24 |
+
return f"Repository '{project_name}' created successfully!"
|
| 25 |
+
except Exception as e:
|
| 26 |
+
return f"Error creating repository: {str(e)}"
|
| 27 |
+
|
| 28 |
+
@tool
|
| 29 |
+
def list_repos():
|
| 30 |
+
"""Lists all repositories owned by the authenticated GitHub user."""
|
| 31 |
+
try:
|
| 32 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 33 |
+
user = github.get_user()
|
| 34 |
+
repos = user.get_repos()
|
| 35 |
+
repo_names = [repo.name for repo in repos]
|
| 36 |
+
return f"Repositories: {', '.join(repo_names)}"
|
| 37 |
+
except Exception as e:
|
| 38 |
+
return f"Error listing repositories: {str(e)}"
|
| 39 |
+
|
| 40 |
+
@tool
|
| 41 |
+
def list_repo_branches(project_name: str):
|
| 42 |
+
"""Lists all branches in a GitHub repository."""
|
| 43 |
+
try:
|
| 44 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 45 |
+
user = github.get_user()
|
| 46 |
+
repo = user.get_repo(project_name)
|
| 47 |
+
branches = repo.get_branches()
|
| 48 |
+
branch_names = [branch.name for branch in branches]
|
| 49 |
+
return f"Branches in '{project_name}': {', '.join(branch_names)}"
|
| 50 |
+
except Exception as e:
|
| 51 |
+
return f"Error listing branches: {str(e)}"
|
| 52 |
+
|
| 53 |
+
@tool
|
| 54 |
+
def create_repo_branch(project_name: str, branch_name: str):
|
| 55 |
+
"""Creates a new branch in a GitHub repository."""
|
| 56 |
+
try:
|
| 57 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 58 |
+
user = github.get_user()
|
| 59 |
+
repo = user.get_repo(project_name)
|
| 60 |
+
main_branch = repo.get_branch("main")
|
| 61 |
+
repo.create_git_ref(ref=f"refs/heads/{branch_name}", sha=main_branch.commit.sha)
|
| 62 |
+
return f"Branch '{branch_name}' created successfully in '{project_name}'."
|
| 63 |
+
except Exception as e:
|
| 64 |
+
return f"Error creating branch: {str(e)}"
|
| 65 |
+
|
| 66 |
+
@tool
|
| 67 |
+
def write_file(project_name: str, file_path: str, file_contents: str):
|
| 68 |
+
"""Adds a new file to the project directory and GitHub repository or updates the existing one."""
|
| 69 |
+
|
| 70 |
+
try:
|
| 71 |
+
with open(f"agents/coder/projects/{project_name}/{file_path}", "w", encoding="utf-8") as f:
|
| 72 |
+
f.write(file_contents)
|
| 73 |
+
except Exception as e:
|
| 74 |
+
return f"Error with file operation: {str(e)}"
|
| 75 |
+
try:
|
| 76 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 77 |
+
user = github.get_user()
|
| 78 |
+
repo = user.get_repo(project_name)
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
# Check if file exists
|
| 82 |
+
file = repo.get_contents(file_path)
|
| 83 |
+
sha = file.sha
|
| 84 |
+
repo.update_file(file_path, "Updating file", file_contents, sha)
|
| 85 |
+
return f"File '{file_path}' updated successfully in '{project_name}'."
|
| 86 |
+
except:
|
| 87 |
+
repo.create_file(file_path, "Adding new file", file_contents)
|
| 88 |
+
return f"File '{file_path}' created successfully in '{project_name}'."
|
| 89 |
+
except Exception as e:
|
| 90 |
+
return f"Error with file operation: {str(e)}"
|
| 91 |
+
|
| 92 |
+
@tool
|
| 93 |
+
def delete_file(project_name: str, file_path: str):
|
| 94 |
+
"""Deletes a file from the GitHub repository."""
|
| 95 |
+
try:
|
| 96 |
+
if os.path.isfile(file_path):
|
| 97 |
+
os.remove(file_path)
|
| 98 |
+
except Exception as e:
|
| 99 |
+
return f"Error deleting file: {str(e)}"
|
| 100 |
+
try:
|
| 101 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 102 |
+
user = github.get_user()
|
| 103 |
+
repo = user.get_repo(project_name)
|
| 104 |
+
file = repo.get_contents(file_path)
|
| 105 |
+
sha = file.sha
|
| 106 |
+
repo.delete_file(file_path, "Deleting file", sha)
|
| 107 |
+
return f"File '{file_path}' deleted successfully from '{project_name}'."
|
| 108 |
+
except Exception as e:
|
| 109 |
+
return f"Error deleting file: {str(e)}"
|
| 110 |
+
|
| 111 |
+
@tool
|
| 112 |
+
def read_repo_file(project_name: str, file_path: str):
|
| 113 |
+
"""Reads the content of a file from a GitHub repository."""
|
| 114 |
+
try:
|
| 115 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 116 |
+
user = github.get_user()
|
| 117 |
+
repo = user.get_repo(project_name)
|
| 118 |
+
file = repo.get_contents(file_path)
|
| 119 |
+
return file.decoded_content.decode('utf-8')
|
| 120 |
+
except Exception as e:
|
| 121 |
+
return f"Error reading file: {str(e)}"
|
| 122 |
+
|
| 123 |
+
@tool
|
| 124 |
+
def read_file(project_name: str, file_path: str) -> str:
|
| 125 |
+
"""Reads the content of a file from a project directory."""
|
| 126 |
+
full_path = f"agents/coder/projects/{project_name}/{file_path}"
|
| 127 |
+
try:
|
| 128 |
+
with open(full_path, "r", encoding="utf-8") as f:
|
| 129 |
+
return f.read()
|
| 130 |
+
except Exception as e:
|
| 131 |
+
return f"Error reading file '{full_path}': {str(e)}"
|
| 132 |
+
|
| 133 |
+
@tool
|
| 134 |
+
def list_repo_files(project_name: str):
|
| 135 |
+
"""Lists all files in the GitHub repository."""
|
| 136 |
+
try:
|
| 137 |
+
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 138 |
+
user = github.get_user()
|
| 139 |
+
repo = user.get_repo(project_name)
|
| 140 |
+
files = repo.get_contents("")
|
| 141 |
+
file_list = [file.name for file in files]
|
| 142 |
+
return f"Files in '{project_name}': {', '.join(file_list)}"
|
| 143 |
+
except Exception as e:
|
| 144 |
+
return f"Error listing files: {str(e)}"
|
| 145 |
+
|
| 146 |
+
@tool
|
| 147 |
+
def list_files(project_name: str, directory: str = "") -> dict:
|
| 148 |
+
"""
|
| 149 |
+
Lists files in a project directory.
|
| 150 |
+
|
| 151 |
+
:param project_name: Name of the project.
|
| 152 |
+
:param directory: Optional subdirectory inside the project. By default, all project files are listed.
|
| 153 |
+
:return: List of files in the directory inside the project.
|
| 154 |
+
"""
|
| 155 |
+
base_path = os.path.join("agents/coder/projects", project_name, directory)
|
| 156 |
+
|
| 157 |
+
try:
|
| 158 |
+
if not os.path.exists(base_path):
|
| 159 |
+
return f"Directory '{base_path}' does not exist."
|
| 160 |
+
|
| 161 |
+
if not os.path.isdir(base_path):
|
| 162 |
+
return f"Path '{base_path}' is not a directory."
|
| 163 |
+
|
| 164 |
+
files = os.listdir(base_path)
|
| 165 |
+
return files
|
| 166 |
+
|
| 167 |
+
except Exception as e:
|
| 168 |
+
return f"Error listing files: {str(e)}"
|
| 169 |
+
|
| 170 |
+
@tool
|
| 171 |
+
def run_cmd(project_name: str, cmd: str, timeout: int = 30):
|
| 172 |
+
"""
|
| 173 |
+
Runs a shell command in the project_name directory.
|
| 174 |
+
"""
|
| 175 |
+
project_dir = os.path.join("agents/coder/projects", project_name)
|
| 176 |
+
|
| 177 |
+
if not os.path.isdir(project_dir):
|
| 178 |
+
return -1, "", f"Project directory not found: {project_dir}"
|
| 179 |
+
|
| 180 |
+
try:
|
| 181 |
+
res = subprocess.run(
|
| 182 |
+
cmd,
|
| 183 |
+
shell=True,
|
| 184 |
+
cwd=project_dir,
|
| 185 |
+
capture_output=True,
|
| 186 |
+
text=True,
|
| 187 |
+
timeout=timeout
|
| 188 |
+
)
|
| 189 |
+
return res.returncode, res.stdout, res.stderr
|
| 190 |
+
except subprocess.TimeoutExpired as e:
|
| 191 |
+
return -1, e.stdout or "", f"Command timed out after {timeout}s"
|
| 192 |
+
except Exception as e:
|
| 193 |
+
return -1, "", f"Error running command: {str(e)}"
|
| 194 |
+
|
| 195 |
+
@tool
|
| 196 |
+
def download_repo(project_name: str, repo_owner: str = None, branch: str = "main"):
|
| 197 |
+
"""
|
| 198 |
+
Downloads a GitHub repo (user/<project_name>) using PyGithub
|
| 199 |
+
and saves it into agents/coder/projects/{project_name}.
|
| 200 |
+
|
| 201 |
+
Args:
|
| 202 |
+
project_name (str): Repo name on GitHub and local folder name.
|
| 203 |
+
repo_owner (str): Repo owner on GitHub, by default = None (it's the user's repository).
|
| 204 |
+
branch (str): Branch to download (default 'main').
|
| 205 |
+
"""
|
| 206 |
+
gh = Github(os.getenv("GITHUB_TOKEN"))
|
| 207 |
+
user = gh.get_user()
|
| 208 |
+
if repo_owner:
|
| 209 |
+
repo = gh.get_repo(f"{repo_owner}/{project_name}")
|
| 210 |
+
else:
|
| 211 |
+
repo = user.get_repo(project_name)
|
| 212 |
+
|
| 213 |
+
dest_path = os.path.join('agents/coder/projects', project_name)
|
| 214 |
+
os.makedirs(dest_path, exist_ok=True)
|
| 215 |
+
|
| 216 |
+
branch_ref = repo.get_branch(branch)
|
| 217 |
+
commit_sha = branch_ref.commit.sha
|
| 218 |
+
|
| 219 |
+
# Get the tree (recursive = True to walk full repo)
|
| 220 |
+
tree = repo.get_git_tree(commit_sha, recursive=True).tree
|
| 221 |
+
|
| 222 |
+
for item in tree:
|
| 223 |
+
file_path = os.path.join(dest_path, item.path)
|
| 224 |
+
|
| 225 |
+
if item.type == "tree":
|
| 226 |
+
os.makedirs(file_path, exist_ok=True)
|
| 227 |
+
elif item.type == "blob":
|
| 228 |
+
blob = repo.get_git_blob(item.sha)
|
| 229 |
+
file_content = blob.content
|
| 230 |
+
import base64
|
| 231 |
+
decoded = base64.b64decode(file_content)
|
| 232 |
+
|
| 233 |
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
| 234 |
+
with open(file_path, "wb") as f:
|
| 235 |
+
f.write(decoded)
|
| 236 |
+
|
| 237 |
+
return f"Repo '{project_name}' downloaded into {dest_path}."
|
| 238 |
+
|
| 239 |
+
tools = [tavily_search, create_project, list_repos, list_repo_branches, create_repo_branch, write_file, delete_file, read_repo_file, read_file, list_repo_files, list_files, run_cmd, download_repo]
|
{agent → agents}/models.py
RENAMED
|
@@ -1,30 +1,19 @@
|
|
| 1 |
from langchain.chat_models import init_chat_model
|
| 2 |
from dotenv import load_dotenv
|
| 3 |
-
from openai import AsyncOpenAI
|
| 4 |
-
import os
|
| 5 |
|
| 6 |
load_dotenv()
|
| 7 |
-
groq_client = AsyncOpenAI(
|
| 8 |
-
base_url="https://api.groq.com/openai/v1",
|
| 9 |
-
api_key=os.getenv("GROQ_API_KEY"),
|
| 10 |
-
)
|
| 11 |
|
| 12 |
llm_supervisor = init_chat_model(
|
| 13 |
-
|
| 14 |
max_tokens=1000
|
| 15 |
)
|
| 16 |
|
| 17 |
llm_peripheral = init_chat_model(
|
| 18 |
-
|
| 19 |
max_tokens=4000
|
| 20 |
)
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
max_tokens=4000
|
| 25 |
-
)
|
| 26 |
-
|
| 27 |
-
llm_image = init_chat_model(
|
| 28 |
-
model="groq:meta-llama/llama-4-scout-17b-16e-instruct",
|
| 29 |
max_tokens=3000
|
| 30 |
-
)
|
|
|
|
| 1 |
from langchain.chat_models import init_chat_model
|
| 2 |
from dotenv import load_dotenv
|
|
|
|
|
|
|
| 3 |
|
| 4 |
load_dotenv()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
llm_supervisor = init_chat_model(
|
| 7 |
+
"groq:openai/gpt-oss-20b",
|
| 8 |
max_tokens=1000
|
| 9 |
)
|
| 10 |
|
| 11 |
llm_peripheral = init_chat_model(
|
| 12 |
+
"groq:gemma2-9b-it",
|
| 13 |
max_tokens=4000
|
| 14 |
)
|
| 15 |
|
| 16 |
+
llm_sub_agents = init_chat_model(
|
| 17 |
+
"groq:qwen/qwen3-32b",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
max_tokens=3000
|
| 19 |
+
)
|
{agent → agents}/multi_agent.py
RENAMED
|
@@ -1,35 +1,14 @@
|
|
| 1 |
from database_interaction.config import create_or_update_config, load_config_to_env, init_config_db
|
| 2 |
from database_interaction.user import get_user_by_id, create_or_update_user, init_user_db
|
| 3 |
from langchain_community.chat_message_histories import SQLChatMessageHistory
|
| 4 |
-
from langchain_core.messages import SystemMessage, AnyMessage, AIMessage
|
| 5 |
from langchain_core.messages.utils import count_tokens_approximately
|
|
|
|
| 6 |
from sqlalchemy.ext.asyncio import create_async_engine
|
| 7 |
-
from
|
| 8 |
from langmem.short_term import SummarizationNode
|
| 9 |
-
from
|
| 10 |
import os
|
| 11 |
|
| 12 |
-
class State(TypedDict):
|
| 13 |
-
message: AnyMessage
|
| 14 |
-
user_id: str
|
| 15 |
-
first_name: str
|
| 16 |
-
last_name: str
|
| 17 |
-
assistant_name: str
|
| 18 |
-
latitude: str
|
| 19 |
-
longitude: str
|
| 20 |
-
location: str
|
| 21 |
-
openweathermap_api_key: str
|
| 22 |
-
github_token: str
|
| 23 |
-
tavily_api_key: str
|
| 24 |
-
groq_api_key: str
|
| 25 |
-
tuya_access_id: str
|
| 26 |
-
tuya_access_key: str
|
| 27 |
-
tuya_username: str
|
| 28 |
-
tuya_password: str
|
| 29 |
-
tuya_country: str
|
| 30 |
-
clear_history: bool
|
| 31 |
-
messages: list
|
| 32 |
-
|
| 33 |
class Assistant:
|
| 34 |
def __init__(self, state: State):
|
| 35 |
self.state = state
|
|
@@ -77,12 +56,9 @@ class Assistant:
|
|
| 77 |
def compile_multi_agent_system(self):
|
| 78 |
"""Create and return the multi-agent system"""
|
| 79 |
try:
|
| 80 |
-
from
|
| 81 |
-
from
|
| 82 |
-
from
|
| 83 |
-
from agent.prompts import supervisor_instructions
|
| 84 |
-
from agent.models import llm_supervisor, llm_peripheral
|
| 85 |
-
from agent.tools import supervisor_tools
|
| 86 |
|
| 87 |
summarization_node = SummarizationNode(
|
| 88 |
token_counter=count_tokens_approximately,
|
|
@@ -92,21 +68,15 @@ class Assistant:
|
|
| 92 |
output_messages_key="llm_input_messages",
|
| 93 |
)
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
supervisor = create_supervisor(
|
| 98 |
model=llm_supervisor,
|
| 99 |
-
tools=
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
add_handoff_messages=False,
|
| 105 |
-
output_mode="full_history",
|
| 106 |
-
pre_model_hook=summarization_node
|
| 107 |
)
|
| 108 |
-
|
| 109 |
-
return supervisor.compile()
|
| 110 |
|
| 111 |
except Exception as e:
|
| 112 |
print(f"Error creating multi-agent system: {e}")
|
|
@@ -128,6 +98,7 @@ class Assistant:
|
|
| 128 |
|
| 129 |
async def run(self):
|
| 130 |
"""Process messages through the multi-agent system"""
|
|
|
|
| 131 |
try:
|
| 132 |
user_info = await get_user_by_id(user_id=self.state['user_id'])
|
| 133 |
if user_info.get('location'):
|
|
@@ -137,30 +108,17 @@ class Assistant:
|
|
| 137 |
if user_info.get('longitude'):
|
| 138 |
os.environ['LONGITUDE'] = str(user_info['longitude'])
|
| 139 |
|
| 140 |
-
system_msg = SystemMessage(
|
| 141 |
-
content=f"""
|
| 142 |
-
You are an intelligent assistant named {os.getenv('ASSISTANT_NAME', 'Assistant')}, helpful personal assistant built using a multi-agent system architecture. Your tools include web search, weather and time lookups, code execution, and GitHub integration. You work inside a Telegram interface and respond concisely, clearly, and informatively.
|
| 143 |
-
|
| 144 |
-
The user you are assisting is:
|
| 145 |
-
- **Name**: {user_info.get('first_name', 'Unknown') or 'Unknown'} {user_info.get('last_name', '') or ''}
|
| 146 |
-
- **User ID**: {self.state['user_id']}
|
| 147 |
-
- **Location**: {user_info.get('location', 'Unknown') or 'Unknown'}
|
| 148 |
-
- **Coordinates**: ({user_info.get('latitude', 'N/A') or 'N/A'}, {user_info.get('longitude', 'N/A') or 'N/A'})
|
| 149 |
-
|
| 150 |
-
You may use their location when answering weather or time-related queries. If the location is unknown, you may ask the user to share it.
|
| 151 |
-
|
| 152 |
-
Stay helpful, respectful, and relevant to the user's query.
|
| 153 |
-
""".strip()
|
| 154 |
-
)
|
| 155 |
|
| 156 |
await self.message_history.aadd_message(self.state['message'])
|
| 157 |
messages = await self.message_history.aget_messages()
|
| 158 |
|
| 159 |
-
self.state['messages'] = messages[-8:-1] + [
|
| 160 |
multi_agent_system = self.compile_multi_agent_system()
|
| 161 |
|
| 162 |
-
result = await multi_agent_system.ainvoke(
|
| 163 |
-
|
|
|
|
|
|
|
| 164 |
await self.message_history.aadd_message(result['messages'][-1])
|
| 165 |
return {"messages": result.get("messages", [])}
|
| 166 |
|
|
|
|
| 1 |
from database_interaction.config import create_or_update_config, load_config_to_env, init_config_db
|
| 2 |
from database_interaction.user import get_user_by_id, create_or_update_user, init_user_db
|
| 3 |
from langchain_community.chat_message_histories import SQLChatMessageHistory
|
|
|
|
| 4 |
from langchain_core.messages.utils import count_tokens_approximately
|
| 5 |
+
from langchain_core.messages import SystemMessage, AIMessage
|
| 6 |
from sqlalchemy.ext.asyncio import create_async_engine
|
| 7 |
+
from langgraph.prebuilt import create_react_agent
|
| 8 |
from langmem.short_term import SummarizationNode
|
| 9 |
+
from agents.states import State
|
| 10 |
import os
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
class Assistant:
|
| 13 |
def __init__(self, state: State):
|
| 14 |
self.state = state
|
|
|
|
| 56 |
def compile_multi_agent_system(self):
|
| 57 |
"""Create and return the multi-agent system"""
|
| 58 |
try:
|
| 59 |
+
from agents.models import llm_supervisor, llm_peripheral
|
| 60 |
+
from agents.prompts import prompt
|
| 61 |
+
from agents.tools import tools
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
summarization_node = SummarizationNode(
|
| 64 |
token_counter=count_tokens_approximately,
|
|
|
|
| 68 |
output_messages_key="llm_input_messages",
|
| 69 |
)
|
| 70 |
|
| 71 |
+
agent = create_react_agent(
|
|
|
|
|
|
|
| 72 |
model=llm_supervisor,
|
| 73 |
+
tools=tools,
|
| 74 |
+
prompt=prompt(tools),
|
| 75 |
+
state_schema=State,
|
| 76 |
+
version='v1',
|
| 77 |
+
pre_model_hook=summarization_node,
|
|
|
|
|
|
|
|
|
|
| 78 |
)
|
| 79 |
+
return agent
|
|
|
|
| 80 |
|
| 81 |
except Exception as e:
|
| 82 |
print(f"Error creating multi-agent system: {e}")
|
|
|
|
| 98 |
|
| 99 |
async def run(self):
|
| 100 |
"""Process messages through the multi-agent system"""
|
| 101 |
+
from agents.prompts import system_message
|
| 102 |
try:
|
| 103 |
user_info = await get_user_by_id(user_id=self.state['user_id'])
|
| 104 |
if user_info.get('location'):
|
|
|
|
| 108 |
if user_info.get('longitude'):
|
| 109 |
os.environ['LONGITUDE'] = str(user_info['longitude'])
|
| 110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
await self.message_history.aadd_message(self.state['message'])
|
| 113 |
messages = await self.message_history.aget_messages()
|
| 114 |
|
| 115 |
+
self.state['messages'] = messages[-8:-1] + [SystemMessage(system_message(user_info)), messages[-1]]
|
| 116 |
multi_agent_system = self.compile_multi_agent_system()
|
| 117 |
|
| 118 |
+
result = await multi_agent_system.ainvoke(
|
| 119 |
+
{"messages": self.state["messages"]},
|
| 120 |
+
generation_config=dict(response_modalities=["TEXT"])
|
| 121 |
+
)
|
| 122 |
await self.message_history.aadd_message(result['messages'][-1])
|
| 123 |
return {"messages": result.get("messages", [])}
|
| 124 |
|
agents/prompts.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from agents.utils.smart_home import get_device_list
|
| 2 |
+
from dotenv import load_dotenv
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
load_dotenv()
|
| 6 |
+
|
| 7 |
+
def prompt(tools: list):
|
| 8 |
+
tool_list = ", ".join([tool.name for tool in tools])
|
| 9 |
+
return f"""You are the helpful Agent. Your job is to interpret the user’s request and delegate tasks to the available tools and agents to complete the task.
|
| 10 |
+
|
| 11 |
+
You have access to the following agents and tools:
|
| 12 |
+
- Tools: {tool_list}
|
| 13 |
+
|
| 14 |
+
1. Understand and break down user’s request into smaller sub-tasks.
|
| 15 |
+
2. Always delegate each sub-task to the best fitting agent/tool (e.g. always delegate any kind of coding or GitHub related task to ‘coder_agent’).
|
| 16 |
+
3. Coordinate tools/agents efficiently, ensuring minimal redundancy.
|
| 17 |
+
4. Validate results before presenting them.
|
| 18 |
+
5. Respond in Telegram Markdown.
|
| 19 |
+
- Use `*bold*`, `_italic_`, `[text](http://example.com)`, `` `inline code` ``, and ``` fenced code blocks ```.
|
| 20 |
+
- Never leave an opening `*`, `_`, `` ` ``, `[`, or ``` without its closing pair.
|
| 21 |
+
- Do not nest Markdown entities.
|
| 22 |
+
- Escape reserved characters (`* _ [ ] ( ) \ ``) if they are not part of a valid entity.
|
| 23 |
+
- Escaping inside entities is not allowed, so entity must be closed first and reopened again: use _snake_\__case_ for italic snake_case and *2*\**2=4* for bold 2*2=4.
|
| 24 |
+
|
| 25 |
+
Your top priority: satisfy the user’s query as effectively and efficiently as possible using the resources at your disposal, while formatting the final output in valid Markdown.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def system_message(user_info: dict):
|
| 30 |
+
try:
|
| 31 |
+
device_list = "\n\n".join(f"{i + 1}. {device['name']} (id: {device['id']})\nStates:\n" + "\n".join(f"{state}" for state in device['states']) for i, device in enumerate(get_device_list()))
|
| 32 |
+
except Exception as e:
|
| 33 |
+
device_list = None
|
| 34 |
+
return f"""
|
| 35 |
+
You are an intelligent assistant named {os.getenv('ASSISTANT_NAME', 'Assistant')}, helpful personal assistant built using a multi-agent system architecture. Your tools include web search, weather and time lookups, code execution, and GitHub integration. You work inside a Telegram interface and respond concisely, clearly, and informatively.
|
| 36 |
+
|
| 37 |
+
The user you are assisting is:
|
| 38 |
+
- **Name**: {user_info.get('first_name', 'Unknown') or 'Unknown'} {user_info.get('last_name', '') or ''}
|
| 39 |
+
- **User ID**: {user_info.get('user_id', 'Unknown')}
|
| 40 |
+
- **Location**: {user_info.get('location', 'Unknown') or 'Unknown'}
|
| 41 |
+
- **Coordinates**: ({user_info.get('latitude', 'N/A') or 'N/A'}, {user_info.get('longitude', 'N/A') or 'N/A'})
|
| 42 |
+
- **Smart Home Devices**: {device_list or "Unknown"}
|
| 43 |
+
|
| 44 |
+
You may use their location when answering weather or time-related queries. If the location is unknown, you may ask the user to share it.
|
| 45 |
+
You may use the smart home devices if user asks you to.
|
| 46 |
+
Stay helpful, respectful, and relevant to the user's query.
|
| 47 |
+
"""
|
agents/states.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langgraph.prebuilt.chat_agent_executor import AgentState
|
| 2 |
+
from langchain_core.messages import AnyMessage
|
| 3 |
+
|
| 4 |
+
class State(AgentState):
|
| 5 |
+
message: AnyMessage
|
| 6 |
+
user_id: str
|
| 7 |
+
first_name: str
|
| 8 |
+
last_name: str
|
| 9 |
+
assistant_name: str
|
| 10 |
+
latitude: str
|
| 11 |
+
longitude: str
|
| 12 |
+
location: str
|
| 13 |
+
openweathermap_api_key: str
|
| 14 |
+
github_token: str
|
| 15 |
+
tavily_api_key: str
|
| 16 |
+
groq_api_key: str
|
| 17 |
+
tuya_access_id: str
|
| 18 |
+
tuya_access_key: str
|
| 19 |
+
tuya_username: str
|
| 20 |
+
tuya_password: str
|
| 21 |
+
tuya_country: str
|
| 22 |
+
clear_history: bool
|
agents/tools.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from langchain_community.tools.yahoo_finance_news import YahooFinanceNewsTool
|
| 2 |
+
from langchain_community.utilities import OpenWeatherMapAPIWrapper
|
| 3 |
+
from langchain_community.utilities import WikipediaAPIWrapper
|
| 4 |
+
from langchain_community.tools import WikipediaQueryRun
|
| 5 |
+
from langchain_tavily.tavily_search import TavilySearch
|
| 6 |
+
from agents.coder.agent import coder_agent
|
| 7 |
+
from agents.utils.smart_home import change_device_states
|
| 8 |
+
from timezonefinder import TimezoneFinder
|
| 9 |
+
from langchain_core.tools import tool
|
| 10 |
+
from dotenv import load_dotenv
|
| 11 |
+
from geopy import Nominatim
|
| 12 |
+
import datetime
|
| 13 |
+
import pytz
|
| 14 |
+
import os
|
| 15 |
+
|
| 16 |
+
load_dotenv()
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
tf = TimezoneFinder()
|
| 20 |
+
web_search = TavilySearch(
|
| 21 |
+
max_results=5,
|
| 22 |
+
topic="general",
|
| 23 |
+
include_answer=True
|
| 24 |
+
)
|
| 25 |
+
yahoo = YahooFinanceNewsTool()
|
| 26 |
+
geolocator = Nominatim(user_agent="my_geocoder")
|
| 27 |
+
wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
|
| 28 |
+
weekday_mapping = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
|
| 29 |
+
|
| 30 |
+
@tool
|
| 31 |
+
def current_time(location: str = None):
|
| 32 |
+
"""Get the current time for a location or current position."""
|
| 33 |
+
try:
|
| 34 |
+
if location:
|
| 35 |
+
location_data = geolocator.geocode(location)
|
| 36 |
+
if location_data:
|
| 37 |
+
timezone = pytz.timezone(tf.timezone_at(lat=location_data.latitude, lng=location_data.longitude))
|
| 38 |
+
location_name = location.capitalize()
|
| 39 |
+
else:
|
| 40 |
+
return f"Could not find location: {location}"
|
| 41 |
+
else:
|
| 42 |
+
# Use environment location if available
|
| 43 |
+
lat = os.getenv('LATITUDE')
|
| 44 |
+
lon = os.getenv('LONGITUDE')
|
| 45 |
+
if lat and lon:
|
| 46 |
+
timezone = pytz.timezone(tf.timezone_at(lat=float(lat), lng=float(lon)))
|
| 47 |
+
location_name = os.getenv('LOCATION', 'Current Location')
|
| 48 |
+
else:
|
| 49 |
+
timezone = pytz.UTC
|
| 50 |
+
location_name = 'UTC'
|
| 51 |
+
|
| 52 |
+
current_dt = datetime.datetime.now(timezone)
|
| 53 |
+
weekday = weekday_mapping[current_dt.weekday()]
|
| 54 |
+
return f"Location: {location_name}; Current Date and Time: {current_dt.strftime('%Y-%m-%d %H:%M')}, {weekday}."
|
| 55 |
+
except Exception as e:
|
| 56 |
+
return f"Error getting current time: {str(e)}"
|
| 57 |
+
|
| 58 |
+
@tool
|
| 59 |
+
def weather(location: str = None):
|
| 60 |
+
"""Get the current weather for a location or current position."""
|
| 61 |
+
try:
|
| 62 |
+
weather_wrapper = OpenWeatherMapAPIWrapper(
|
| 63 |
+
openweathermap_api_key=os.getenv('OPENWEATHERMAP_API_KEY')
|
| 64 |
+
)
|
| 65 |
+
if not location:
|
| 66 |
+
location = os.getenv('LOCATION', 'Unknown')
|
| 67 |
+
return weather_wrapper.run(location=location)
|
| 68 |
+
except Exception as e:
|
| 69 |
+
return f"Error getting weather: {str(e)}"
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
tools = [yahoo, web_search, current_time, weather, change_device_states, coder_agent]
|
| 73 |
+
|
| 74 |
+
|
{agent → agents/utils}/file_preprocessing.py
RENAMED
|
@@ -13,7 +13,7 @@ async def preprocess_file(file_name: str):
|
|
| 13 |
if "image" in mime_type:
|
| 14 |
return await preprocess_image(file_name)
|
| 15 |
elif "video" in mime_type:
|
| 16 |
-
|
| 17 |
elif "audio" in mime_type:
|
| 18 |
return await preprocess_audio(file_name)
|
| 19 |
else:
|
|
|
|
| 13 |
if "image" in mime_type:
|
| 14 |
return await preprocess_image(file_name)
|
| 15 |
elif "video" in mime_type:
|
| 16 |
+
pass
|
| 17 |
elif "audio" in mime_type:
|
| 18 |
return await preprocess_audio(file_name)
|
| 19 |
else:
|
agent/tools.py → agents/utils/smart_home.py
RENAMED
|
@@ -1,33 +1,9 @@
|
|
| 1 |
-
from
|
| 2 |
-
from langchain_community.utilities import OpenWeatherMapAPIWrapper
|
| 3 |
-
from langchain_community.utilities import WikipediaAPIWrapper
|
| 4 |
-
from langchain_community.tools import WikipediaQueryRun
|
| 5 |
-
from langchain_tavily.tavily_search import TavilySearch
|
| 6 |
-
from timezonefinder import TimezoneFinder
|
| 7 |
-
from langchain_core.tools import tool
|
| 8 |
from tuya_iot import TuyaOpenAPI
|
| 9 |
-
from dotenv import load_dotenv
|
| 10 |
-
from geopy import Nominatim
|
| 11 |
-
from github import Github
|
| 12 |
import phonenumbers
|
| 13 |
import pycountry
|
| 14 |
-
import datetime
|
| 15 |
-
import pytz
|
| 16 |
import os
|
| 17 |
|
| 18 |
-
load_dotenv()
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
tf = TimezoneFinder()
|
| 22 |
-
web_search = TavilySearch(
|
| 23 |
-
max_results=5,
|
| 24 |
-
topic="general",
|
| 25 |
-
include_answer=True
|
| 26 |
-
)
|
| 27 |
-
yahoo = YahooFinanceNewsTool()
|
| 28 |
-
geolocator = Nominatim(user_agent="my_geocoder")
|
| 29 |
-
wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
|
| 30 |
-
weekday_mapping = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
|
| 31 |
endpoint_map = {
|
| 32 |
"https://openapi.tuyacn.com": [86],
|
| 33 |
"https://openapi.tuyain.com": [91],
|
|
@@ -60,156 +36,7 @@ openapi = TuyaOpenAPI(os.getenv('TUYA_ENDPOINT'), os.getenv('TUYA_ACCESS_ID'), o
|
|
| 60 |
connection = openapi.connect(os.getenv('TUYA_USERNAME'), os.getenv('TUYA_PASSWORD'), os.getenv('TUYA_COUNTRY_CODE'), 'TuyaSmart')
|
| 61 |
if not connection['success']:
|
| 62 |
connection = openapi.connect(os.getenv('TUYA_USERNAME'), os.getenv('TUYA_PASSWORD'), os.getenv('TUYA_COUNTRY_CODE'), 'SmartLife')
|
| 63 |
-
@tool
|
| 64 |
-
def create_repo(repo_name: str, private: bool = False):
|
| 65 |
-
"""Creates a GitHub repository with the given repo_name."""
|
| 66 |
-
try:
|
| 67 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 68 |
-
user = github.get_user()
|
| 69 |
-
user.create_repo(repo_name, private=private)
|
| 70 |
-
return f"Repository '{repo_name}' created successfully!"
|
| 71 |
-
except Exception as e:
|
| 72 |
-
return f"Error creating repository: {str(e)}"
|
| 73 |
|
| 74 |
-
@tool
|
| 75 |
-
def commit_file_to_repo(repo_name: str, file_path: str, file_contents: str):
|
| 76 |
-
"""Adds a new file to the GitHub repository or updates the existing one."""
|
| 77 |
-
try:
|
| 78 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 79 |
-
user = github.get_user()
|
| 80 |
-
repo = user.get_repo(repo_name)
|
| 81 |
-
|
| 82 |
-
try:
|
| 83 |
-
# Check if file exists
|
| 84 |
-
file = repo.get_contents(file_path)
|
| 85 |
-
sha = file.sha
|
| 86 |
-
repo.update_file(file_path, "Updating file", file_contents, sha)
|
| 87 |
-
return f"File '{file_path}' updated successfully in '{repo_name}'."
|
| 88 |
-
except:
|
| 89 |
-
repo.create_file(file_path, "Adding new file", file_contents)
|
| 90 |
-
return f"File '{file_path}' created successfully in '{repo_name}'."
|
| 91 |
-
except Exception as e:
|
| 92 |
-
return f"Error with file operation: {str(e)}"
|
| 93 |
-
|
| 94 |
-
@tool
|
| 95 |
-
def read_file(repo_name: str, file_path: str):
|
| 96 |
-
"""Reads the content of a file from a GitHub repository."""
|
| 97 |
-
try:
|
| 98 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 99 |
-
user = github.get_user()
|
| 100 |
-
repo = user.get_repo(repo_name)
|
| 101 |
-
file = repo.get_contents(file_path)
|
| 102 |
-
return file.decoded_content.decode('utf-8')
|
| 103 |
-
except Exception as e:
|
| 104 |
-
return f"Error reading file: {str(e)}"
|
| 105 |
-
|
| 106 |
-
@tool
|
| 107 |
-
def list_repos():
|
| 108 |
-
"""Lists all repositories owned by the authenticated GitHub user."""
|
| 109 |
-
try:
|
| 110 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 111 |
-
user = github.get_user()
|
| 112 |
-
repos = user.get_repos()
|
| 113 |
-
repo_names = [repo.name for repo in repos]
|
| 114 |
-
return f"Repositories: {', '.join(repo_names)}"
|
| 115 |
-
except Exception as e:
|
| 116 |
-
return f"Error listing repositories: {str(e)}"
|
| 117 |
-
|
| 118 |
-
@tool
|
| 119 |
-
def list_files(repo_name: str):
|
| 120 |
-
"""Lists all files in the GitHub repository."""
|
| 121 |
-
try:
|
| 122 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 123 |
-
user = github.get_user()
|
| 124 |
-
repo = user.get_repo(repo_name)
|
| 125 |
-
files = repo.get_contents("")
|
| 126 |
-
file_list = [file.name for file in files]
|
| 127 |
-
return f"Files in '{repo_name}': {', '.join(file_list)}"
|
| 128 |
-
except Exception as e:
|
| 129 |
-
return f"Error listing files: {str(e)}"
|
| 130 |
-
|
| 131 |
-
@tool
|
| 132 |
-
def delete_file(repo_name: str, file_path: str):
|
| 133 |
-
"""Deletes a file from the GitHub repository."""
|
| 134 |
-
try:
|
| 135 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 136 |
-
user = github.get_user()
|
| 137 |
-
repo = user.get_repo(repo_name)
|
| 138 |
-
file = repo.get_contents(file_path)
|
| 139 |
-
sha = file.sha
|
| 140 |
-
repo.delete_file(file_path, "Deleting file", sha)
|
| 141 |
-
return f"File '{file_path}' deleted successfully from '{repo_name}'."
|
| 142 |
-
except Exception as e:
|
| 143 |
-
return f"Error deleting file: {str(e)}"
|
| 144 |
-
|
| 145 |
-
@tool
|
| 146 |
-
def list_branches(repo_name: str):
|
| 147 |
-
"""Lists all branches in a GitHub repository."""
|
| 148 |
-
try:
|
| 149 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 150 |
-
user = github.get_user()
|
| 151 |
-
repo = user.get_repo(repo_name)
|
| 152 |
-
branches = repo.get_branches()
|
| 153 |
-
branch_names = [branch.name for branch in branches]
|
| 154 |
-
return f"Branches in '{repo_name}': {', '.join(branch_names)}"
|
| 155 |
-
except Exception as e:
|
| 156 |
-
return f"Error listing branches: {str(e)}"
|
| 157 |
-
|
| 158 |
-
@tool
|
| 159 |
-
def create_branch(repo_name: str, branch_name: str):
|
| 160 |
-
"""Creates a new branch in a GitHub repository."""
|
| 161 |
-
try:
|
| 162 |
-
github = Github(os.getenv('GITHUB_TOKEN'))
|
| 163 |
-
user = github.get_user()
|
| 164 |
-
repo = user.get_repo(repo_name)
|
| 165 |
-
main_branch = repo.get_branch("main")
|
| 166 |
-
repo.create_git_ref(ref=f"refs/heads/{branch_name}", sha=main_branch.commit.sha)
|
| 167 |
-
return f"Branch '{branch_name}' created successfully in '{repo_name}'."
|
| 168 |
-
except Exception as e:
|
| 169 |
-
return f"Error creating branch: {str(e)}"
|
| 170 |
-
|
| 171 |
-
@tool
|
| 172 |
-
def current_time(location: str = None):
|
| 173 |
-
"""Get the current time for a location or current position."""
|
| 174 |
-
try:
|
| 175 |
-
if location:
|
| 176 |
-
location_data = geolocator.geocode(location)
|
| 177 |
-
if location_data:
|
| 178 |
-
timezone = pytz.timezone(tf.timezone_at(lat=location_data.latitude, lng=location_data.longitude))
|
| 179 |
-
location_name = location.capitalize()
|
| 180 |
-
else:
|
| 181 |
-
return f"Could not find location: {location}"
|
| 182 |
-
else:
|
| 183 |
-
# Use environment location if available
|
| 184 |
-
lat = os.getenv('LATITUDE')
|
| 185 |
-
lon = os.getenv('LONGITUDE')
|
| 186 |
-
if lat and lon:
|
| 187 |
-
timezone = pytz.timezone(tf.timezone_at(lat=float(lat), lng=float(lon)))
|
| 188 |
-
location_name = os.getenv('LOCATION', 'Current Location')
|
| 189 |
-
else:
|
| 190 |
-
timezone = pytz.UTC
|
| 191 |
-
location_name = 'UTC'
|
| 192 |
-
|
| 193 |
-
current_dt = datetime.datetime.now(timezone)
|
| 194 |
-
weekday = weekday_mapping[current_dt.weekday()]
|
| 195 |
-
return f"Location: {location_name}; Current Date and Time: {current_dt.strftime('%Y-%m-%d %H:%M')}, {weekday}."
|
| 196 |
-
except Exception as e:
|
| 197 |
-
return f"Error getting current time: {str(e)}"
|
| 198 |
-
|
| 199 |
-
@tool
|
| 200 |
-
def weather(location: str = None):
|
| 201 |
-
"""Get the current weather for a location or current position."""
|
| 202 |
-
try:
|
| 203 |
-
weather_wrapper = OpenWeatherMapAPIWrapper(
|
| 204 |
-
openweathermap_api_key=os.getenv('OPENWEATHERMAP_API_KEY')
|
| 205 |
-
)
|
| 206 |
-
if not location:
|
| 207 |
-
location = os.getenv('LOCATION', 'Unknown')
|
| 208 |
-
return weather_wrapper.run(location=location)
|
| 209 |
-
except Exception as e:
|
| 210 |
-
return f"Error getting weather: {str(e)}"
|
| 211 |
-
|
| 212 |
-
@tool
|
| 213 |
def get_device_list():
|
| 214 |
"""
|
| 215 |
Get the list of devices bound to the smart home.
|
|
@@ -232,9 +59,4 @@ def change_device_states(id:str, commands:list[dict]):
|
|
| 232 |
State format: {'code': ..., 'value': ...}
|
| 233 |
"""
|
| 234 |
response = openapi.post('/v1.0/devices/{}/commands'.format(id), {"commands": commands})
|
| 235 |
-
return "success" if response["success"] else "failure"
|
| 236 |
-
|
| 237 |
-
coder_tools = [web_search, create_repo, create_branch, commit_file_to_repo, read_file, list_files, list_repos, list_branches, delete_file]
|
| 238 |
-
supervisor_tools = [yahoo, web_search, current_time, weather]
|
| 239 |
-
deep_research_tools = [web_search, yahoo, wikipedia]
|
| 240 |
-
smart_home_tools = [get_device_list, change_device_states]
|
|
|
|
| 1 |
+
from langchain.tools import tool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from tuya_iot import TuyaOpenAPI
|
|
|
|
|
|
|
|
|
|
| 3 |
import phonenumbers
|
| 4 |
import pycountry
|
|
|
|
|
|
|
| 5 |
import os
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
endpoint_map = {
|
| 8 |
"https://openapi.tuyacn.com": [86],
|
| 9 |
"https://openapi.tuyain.com": [91],
|
|
|
|
| 36 |
connection = openapi.connect(os.getenv('TUYA_USERNAME'), os.getenv('TUYA_PASSWORD'), os.getenv('TUYA_COUNTRY_CODE'), 'TuyaSmart')
|
| 37 |
if not connection['success']:
|
| 38 |
connection = openapi.connect(os.getenv('TUYA_USERNAME'), os.getenv('TUYA_PASSWORD'), os.getenv('TUYA_COUNTRY_CODE'), 'SmartLife')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
def get_device_list():
|
| 41 |
"""
|
| 42 |
Get the list of devices bound to the smart home.
|
|
|
|
| 59 |
State format: {'code': ..., 'value': ...}
|
| 60 |
"""
|
| 61 |
response = openapi.post('/v1.0/devices/{}/commands'.format(id), {"commands": commands})
|
| 62 |
+
return "success" if response["success"] else "failure"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
-
from
|
| 2 |
from fastapi import FastAPI, UploadFile, File, HTTPException
|
| 3 |
from langchain_core.messages import HumanMessage
|
| 4 |
-
from
|
| 5 |
from typing import Any, Dict
|
| 6 |
from fastapi import Form
|
| 7 |
from pathlib import Path
|
|
|
|
| 1 |
+
from agents.utils.file_preprocessing import preprocess_file, preprocess_audio
|
| 2 |
from fastapi import FastAPI, UploadFile, File, HTTPException
|
| 3 |
from langchain_core.messages import HumanMessage
|
| 4 |
+
from agents.multi_agent import Assistant
|
| 5 |
from typing import Any, Dict
|
| 6 |
from fastapi import Form
|
| 7 |
from pathlib import Path
|