jarvis/tools.py

183 lines
5.4 KiB
Python
Raw Normal View History

2024-10-06 21:23:55 -04:00
import subprocess
import tempfile
import time
2024-09-29 12:18:44 -04:00
import duckduckgo_search
import requests
from markdownify import markdownify as md
2024-10-06 21:23:55 -04:00
from readability.readability import Document
2024-10-01 19:31:57 -04:00
2024-09-29 12:18:44 -04:00
class Tool:
def __init__(self, name: str, description: str, arguments: dict, returns: str):
self.name = name
self.description = description
self.arguments = arguments
self.returns = returns
def execute(self, arguments: dict) -> str:
pass
class ToolManager:
def __init__(self):
self.tools = []
def add_tool(self, tool: Tool):
self.tools.append(tool)
def get_tool(self, name: str) -> Tool:
for tool in self.tools:
if tool.name == name:
return tool
return None
2024-10-06 21:23:55 -04:00
2024-09-29 12:18:44 -04:00
def get_tools_and_descriptions_for_prompt(self):
return "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
2024-10-06 21:23:55 -04:00
2024-09-29 12:18:44 -04:00
def get_tools_for_ollama_dict(self):
2024-10-06 21:23:55 -04:00
return [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.arguments,
},
}
for tool in self.tools
]
2024-09-29 12:18:44 -04:00
class DefaultToolManager(ToolManager):
def __init__(self):
super().__init__()
self.add_tool(SearchTool())
self.add_tool(GetReadablePageContentsTool())
self.add_tool(CalculatorTool())
self.add_tool(PythonCodeTool())
class SearchTool(Tool):
def __init__(self):
2024-10-06 21:23:55 -04:00
super().__init__(
"search_web",
"Search the internet for information",
{
"type": "object",
"properties": {
"query": {"type": "string", "description": "The search query"}
},
},
"results:list[string]",
)
2024-09-29 12:18:44 -04:00
def execute(self, arg: dict) -> str:
2024-10-06 21:23:55 -04:00
res = duckduckgo_search.DDGS().text(arg["query"], max_results=5)
return "\n\n".join([f"{r['title']}\n{r['body']}\n{r['href']}" for r in res])
2024-09-29 12:18:44 -04:00
def get_readable_page_contents(url: str) -> str:
try:
response = requests.get(url)
response.raise_for_status()
doc = Document(response.content)
content = doc.summary()
return md(content)
except Exception as e:
return f"Error fetching readable content: {str(e)}"
class GetReadablePageContentsTool(Tool):
def __init__(self):
2024-10-06 21:23:55 -04:00
super().__init__(
"get_readable_page_contents",
"Get the contents of a web page in a readable format",
{
"type": "object",
"properties": {
"url": {"type": "string", "description": "The url of the web page"}
},
},
"contents:string",
)
2024-09-29 12:18:44 -04:00
def execute(self, arg: dict) -> str:
2024-10-06 21:23:55 -04:00
return get_readable_page_contents(arg["url"])
2024-09-29 12:18:44 -04:00
class CalculatorTool(Tool):
def __init__(self):
2024-10-06 21:23:55 -04:00
super().__init__(
"calculator",
"Perform a calculation using python's eval function",
{
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The mathematical expression to evaluate, should be a python mathematical expression",
}
},
},
"result:string",
)
2024-09-29 12:18:44 -04:00
def execute(self, arg: dict) -> str:
try:
return str(eval(arg["expression"]))
except Exception as e:
return f"Error executing code: {str(e)}"
2024-09-29 12:18:44 -04:00
class PythonCodeTool(Tool):
def __init__(self):
2024-10-06 21:23:55 -04:00
super().__init__(
"python_code",
"Execute python code using a temporary file and a subprocess. You must print results to stdout.",
{
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The python code to execute, can be multiline",
}
},
},
"result:string",
)
2024-09-29 12:18:44 -04:00
def execute(self, arg: dict) -> str:
try:
2024-10-06 21:23:55 -04:00
with tempfile.NamedTemporaryFile(
suffix=".py", mode="w", delete=False
) as temp_file:
temp_file.write(arg["code"])
2024-10-01 19:31:57 -04:00
temp_file.flush()
start_time = time.time()
2024-10-06 21:23:55 -04:00
process = subprocess.Popen(
["python", temp_file.name],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
2024-10-01 19:31:57 -04:00
stdout, stderr = process.communicate(timeout=10) # 10 second timeout
end_time = time.time()
execution_time = end_time - start_time
result = {
2024-10-06 21:23:55 -04:00
"stdout": stdout,
"stderr": stderr,
"return_value": process.returncode,
"execution_time": execution_time,
2024-10-01 19:31:57 -04:00
}
2024-09-29 12:18:44 -04:00
except subprocess.TimeoutExpired:
process.kill()
return "Error: Code execution timed out after 10 seconds"
except Exception as e:
return f"Error executing code: {str(e)}"
2024-10-06 21:23:55 -04:00
return "\n".join([f"{k}:\n{v}" for k, v in result.items()])