Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: AI Agent v0 #145

Draft
wants to merge 28 commits into
base: staging
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b2733b1
add new agent view
Jan 9, 2025
a16821c
functional agent inference view
Jan 9, 2025
f0f2e86
inital commit with working agent + dropdown
Jan 20, 2025
31281b1
add feature to remove agent container when corresponding llm containe…
Jan 20, 2025
5d60de1
udpate env file
Jan 20, 2025
75ed5a7
dynamic port mapping for agent container - no longer fixed
Jan 20, 2025
3552b7e
added comments
Jan 20, 2025
d588f46
add comments
mvanniasingheTT Jan 20, 2025
0e32d7a
inital commit with working agent + dropdown
mvanniasingheTT Jan 20, 2025
5bb0e7f
add feature to remove agent container when corresponding llm containe…
mvanniasingheTT Jan 20, 2025
b1243df
udpate env file
mvanniasingheTT Jan 20, 2025
59c6e5d
dynamic port mapping for agent container - no longer fixed
mvanniasingheTT Jan 20, 2025
d36d277
added comments
mvanniasingheTT Jan 20, 2025
e0ea78e
add comments
mvanniasingheTT Jan 20, 2025
e6b13e0
Merge branch 'mvanniasinghe/ai_agent' of https://github.com/tenstorre…
mvanniasingheTT Jan 20, 2025
b734262
change agent ports to start at 8201 to not conflict with RAG + fix in…
mvanniasingheTT Jan 21, 2025
5cdc52c
add agent files
mvanniasingheTT Jan 21, 2025
2e96d99
change max tokens
mvanniasingheTT Jan 21, 2025
0c867a8
update docker image version for agent
mvanniasingheTT Jan 21, 2025
e1966d2
use final answer and [DONE] to terminate response
mvanniasingheTT Jan 21, 2025
242d8b5
update to break before complete output
mvanniasingheTT Jan 21, 2025
3db49fa
modify agent to use different prompt - improved perf
mvanniasingheTT Jan 23, 2025
064d586
remove uneeded comments
mvanniasingheTT Jan 23, 2025
b5f7ec5
improved prompt template + only output Final Answer
mvanniasingheTT Jan 23, 2025
5639bb5
remove old template
mvanniasingheTT Jan 23, 2025
d01de54
remove uneeded imports
mvanniasingheTT Jan 23, 2025
4c1757c
distinguish [DONE] from typical response
mvanniasingheTT Jan 23, 2025
cb93de5
change agent port mappings to start at 8201
mvanniasingheTT Jan 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/.env.default
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ VLLM_LLAMA31_ENV_FILE=""
JWT_SECRET=test-secret-456
DJANGO_SECRET_KEY=django-insecure-default
HF_TOKEN=<your-hf-token> # Get this from Hugging Face
TAVILY_API_KEY=<your-tavily-api-key>
46 changes: 46 additions & 0 deletions app/api/agent_control/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# install ubutntu base image
FROM ubuntu:20.04
ENV TZ=America/Los_Angeles
ARG DEBIAN_FRONTEND=noninteractive

# Update the package repository and install some default tools
RUN apt-get update && apt-get install -y \
vim \
nano \
software-properties-common \
git \
htop \
screen \
tmux \
unzip \
zip \
curl \
wget

# add deadsnakes for newer python versions
RUN add-apt-repository ppa:deadsnakes/ppa -y && apt-get update
# Install the specific version of Python
RUN apt-get install -y python3.11 python3.11-venv python3.11-dev

# Set Python3.11 as the default Python3
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1

# Ensure pip is installed and upgrade it
RUN python3 -m ensurepip --upgrade && \
python3 -m pip install --upgrade pip setuptools wheel

# Verify the Python version
RUN python3 --version

COPY requirements_agent_env.txt .
# install python dependencies
RUN pip install --no-cache-dir -r requirements_agent_env.txt

# Set the working directory
WORKDIR /app

# Copy files (optional)
COPY . /app

# Command to run when the container starts
CMD ["/bin/bash"]
9 changes: 9 additions & 0 deletions app/api/agent_control/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
```bash
python -m venv agent_env
source agent_env/bin/activate
pip install -r requirements_agent_env.txt
```

```
python agent.py
```
58 changes: 58 additions & 0 deletions app/api/agent_control/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from custom_llm import CustomLLM
from utils import poll_requests, setup_executer
from code_tool import CodeInterpreterFunctionTool
from langchain.memory import ConversationBufferMemory
from langchain_community.tools.tavily_search import TavilySearchResults
import asyncio
import os
import jwt
import json
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from fastapi.responses import StreamingResponse



app = FastAPI()
json_payload = json.loads('{"team_id": "tenstorrent", "token_id":"debug-test"}')
jwt_secret = os.getenv("JWT_SECRET")
encoded_jwt = jwt.encode(json_payload, jwt_secret, algorithm="HS256")

class RequestPayload(BaseModel):
message: str
thread_id: str


llm_container_name = os.getenv("LLM_CONTAINER_NAME")
llm = CustomLLM(server_url=f"http://{llm_container_name}:7000/v1/chat/completions", encoded_jwt=encoded_jwt,
streaming=True)
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
os.environ["TAVILY_API_KEY"] = os.getenv("TAVILY_API_KEY")

search = TavilySearchResults(
max_results=2,
include_answer=True,
include_raw_content=True)

# TODO: enable code agent
# os.environ["E2B_API_KEY"] = os.getenv("E2B_API_KEY")
# code_interpreter = CodeInterpreterFunctionTool()
# code_interpreter_tool = code_interpreter.to_langchain_tool()
tools = [search]
agent_executer = setup_executer(llm, memory, tools)
config = {"configurable": {"thread_id": "abc-123"}}
# asyncio.run(poll_requests(agent_executer, config, tools, memory)) # TODO: enable to run without server

@app.post("/poll_requests")
async def handle_requests(payload: RequestPayload):
config = {"configurable": {"thread_id": payload.thread_id}}
try:
# use await to prevent handle_requests from blocking, allow other tasks to execute
return StreamingResponse(poll_requests(agent_executer, config, tools, memory, payload.message), media_type="text/plain")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

# health check
@app.get("/")
def read_root():
return {"message": "Server is running"}
106 changes: 106 additions & 0 deletions app/api/agent_control/code_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from pydantic import BaseModel, Field
from typing import List, Sequence, Tuple, Any
from langchain_core.messages import BaseMessage
from langchain.agents.output_parsers.tools import ToolAgentAction
from e2b_code_interpreter import Sandbox
from langchain_core.tools import Tool
from langchain_core.messages import ToolMessage


import os
import json

def format_to_tool_messages(
intermediate_steps: Sequence[Tuple[ToolAgentAction, dict]],
) -> List[BaseMessage]:
messages = []
for agent_action, observation in intermediate_steps:
if agent_action.tool == CodeInterpreterFunctionTool.tool_name:
new_messages = CodeInterpreterFunctionTool.format_to_tool_message(
agent_action,
observation,
)
messages.extend([new for new in new_messages if new not in messages])
else:
# Handle other tools
print("Not handling tool: ", agent_action.tool)

return messages

class LangchainCodeInterpreterToolInput(BaseModel):
code: str = Field(description="Python code to execute.")


class CodeInterpreterFunctionTool:
"""
This class calls arbitrary code against a Python Jupyter notebook.
It requires an E2B_API_KEY to create a sandbox.
"""

tool_name: str = "code_interpreter"

def __init__(self):
# Instantiate the E2B sandbox - this is a long lived object
# that's pinging E2B cloud to keep the sandbox alive.
if "E2B_API_KEY" not in os.environ:
raise Exception(
"Code Interpreter tool called while E2B_API_KEY environment variable is not set. Please get your E2B api key here https://e2b.dev/docs and set the E2B_API_KEY environment variable."
)
self.code_interpreter = Sandbox(timeout=1800)

def call(self, parameters: dict, **kwargs: Any):
code = parameters.get("code", "")
if code.startswith("```"):
code = code[3:]
if code.endswith("[DONE]"):
# TODO: check if this needs to be parsed
pass
if code.endswith("```"):
code = code[:-3]
elif code.endswith("```\n"):
code = code[:-4]
print(f"***Code Interpreting...\n{code}\n====")
execution = self.code_interpreter.run_code(code)
return {
"results": execution.results,
"stdout": execution.logs.stdout,
"stderr": execution.logs.stderr,
"error": execution.error,
}

def close(self):
self.code_interpreter.kill()

# langchain does not return a dict as a parameter, only a code string
def langchain_call(self, code: str):
return self.call({"code": code})

def to_langchain_tool(self) -> Tool:
tool = Tool(
name=self.tool_name,
description="Execute python code in a Jupyter notebook cell and returns any rich data (eg charts), stdout, stderr, and error.",
func=self.langchain_call,
)
tool.args_schema = LangchainCodeInterpreterToolInput
return tool

@staticmethod
def format_to_tool_message(
agent_action: ToolAgentAction,
observation: dict,
) -> List[BaseMessage]:
"""
Format the output of the CodeInterpreter tool to be returned as a ToolMessage.
"""
new_messages = list(agent_action.message_log)

# TODO: Add info about the results for the LLM
content = json.dumps(
{k: v for k, v in observation.items() if k not in ("results")}, indent=2
)
print(observation, agent_action, content)
new_messages.append(
ToolMessage(content=content, tool_call_id=agent_action.tool_call_id)
)

return new_messages
Loading
Loading