From 0c5d00600666cfc1814eee075414b6737623a5e7 Mon Sep 17 00:00:00 2001 From: Joshua Carroll Date: Mon, 2 Sep 2024 00:48:59 -0700 Subject: [PATCH] Switch repo to src/ layout (#27) * Move all the Python code to src/ * Convert Dockerfiles to use src/ and pyproject.toml * Various bug fixes * Update README --- README.md | 43 ++++++++++++---------- docker/Dockerfile.app | 10 ++--- docker/Dockerfile.service | 12 +++--- pyproject.toml | 3 +- requirements.txt | 1 + run_service.py | 7 ---- {agent => src/agent}/__init__.py | 0 {agent => src/agent}/langgraph.json | 0 {agent => src/agent}/llama_guard.py | 0 {agent => src/agent}/requirements.txt | 0 {agent => src/agent}/research_assistant.py | 0 {agent => src/agent}/tools.py | 0 {client => src/client}/__init__.py | 0 {client => src/client}/client.py | 19 +++++----- run_client.py => src/run_client.py | 0 src/run_service.py | 9 +++++ {schema => src/schema}/__init__.py | 0 {schema => src/schema}/schema.py | 0 {schema => src/schema}/test_schema.py | 0 {service => src/service}/__init__.py | 0 {service => src/service}/service.py | 0 {service => src/service}/test_service.py | 0 streamlit_app.py => src/streamlit_app.py | 0 23 files changed, 57 insertions(+), 47 deletions(-) delete mode 100644 run_service.py rename {agent => src/agent}/__init__.py (100%) rename {agent => src/agent}/langgraph.json (100%) rename {agent => src/agent}/llama_guard.py (100%) rename {agent => src/agent}/requirements.txt (100%) rename {agent => src/agent}/research_assistant.py (100%) rename {agent => src/agent}/tools.py (100%) rename {client => src/client}/__init__.py (100%) rename {client => src/client}/client.py (95%) rename run_client.py => src/run_client.py (100%) create mode 100644 src/run_service.py rename {schema => src/schema}/__init__.py (100%) rename {schema => src/schema}/schema.py (100%) rename {schema => src/schema}/test_schema.py (100%) rename {service => src/service}/__init__.py (100%) rename {service => src/service}/service.py (100%) rename {service => src/service}/test_service.py (100%) rename streamlit_app.py => src/streamlit_app.py (100%) diff --git a/README.md b/README.md index 1b6f7c5..e317f54 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,16 @@ This project offers a template for you to easily build and run your own agents u Run directly in python ```sh +# An OPENAI_API_KEY is required echo 'OPENAI_API_KEY=your_openai_api_key' >> .env -pip install -r requirements.txt -python run_service.py + +# uv is recommended but pip also works +pip install uv +uv pip install -r pyproject.toml +python src/run_service.py # In another shell -streamlit run streamlit_app.py +streamlit run src/streamlit_app.py ``` Run with docker @@ -54,12 +58,12 @@ docker compose watch The repository is structured as follows: -- `agent/research_assistant.py`: Defines the LangGraph agent -- `agent/llama_guard.py`: Defines the LlamaGuard content moderation -- `schema/schema.py`: Defines the service schema -- `service/service.py`: FastAPI service to serve the agent -- `client/client.py`: Client to interact with the agent service -- `streamlit_app.py`: Streamlit app providing a chat interface +- `src/agent/research_assistant.py`: Defines the LangGraph agent +- `src/agent/llama_guard.py`: Defines the LlamaGuard content moderation +- `src/schema/schema.py`: Defines the service schema +- `src/service/service.py`: FastAPI service to serve the agent +- `src/client/client.py`: Client to interact with the agent service +- `src/streamlit_app.py`: Streamlit app providing a chat interface ## Why LangGraph? @@ -121,7 +125,7 @@ For local development, we recommend using [docker compose watch](https://docs.do 3. The services will now automatically update when you make changes to your code: - Changes in the relevant python files and directories will trigger updates for the relevantservices. - - NOTE: If you make changes to the `requirements.txt` file, you will need to rebuild the services by running `docker compose up --build`. + - NOTE: If you make changes to the `pyproject.toml` file, you will need to rebuild the services by running `docker compose up --build`. 4. Access the Streamlit app by navigating to `http://localhost:8501` in your web browser. @@ -137,19 +141,20 @@ You can also run the agent service and the Streamlit app locally without Docker, 1. Create a virtual environment and install dependencies: ``` - python -m venv venv - source venv/bin/activate - pip install -r requirements.txt + pip install uv + uv venv + source .venv/bin/activate + uv pip install -r pyproject.toml ``` 2. Run the FastAPI server: ``` - python run_service.py + python src/run_service.py ``` 3. In a separate terminal, run the Streamlit app: ``` - streamlit run streamlit_app.py + streamlit run src/streamlit_app.py ``` 4. Open your browser and navigate to the URL provided by Streamlit (usually `http://localhost:8501`). @@ -182,14 +187,14 @@ Currently the tests need to be run using the local development without Docker se To customize the agent for your own use case: -1. Modify the `agent/research_assistant.py` file to change the agent's behavior and tools. Or, build a new agent from scratch. -2. Adjust the Streamlit interface in `streamlit_app.py` to match your agent's capabilities. +1. Modify the `src/agent/research_assistant.py` file to change the agent's behavior and tools. Or, build a new agent from scratch. +2. Adjust the Streamlit interface in `src/streamlit_app.py` to match your agent's capabilities. ## Building other apps on the AgentClient -The repo includes a generic `client.AgentClient` that can be used to interact with the agent service. This client is designed to be flexible and can be used to build other apps on top of the agent. It supports both synchronous and asynchronous invocations, and streaming and non-streaming requests. +The repo includes a generic `src/client/client.AgentClient` that can be used to interact with the agent service. This client is designed to be flexible and can be used to build other apps on top of the agent. It supports both synchronous and asynchronous invocations, and streaming and non-streaming requests. -See the `run_client.py` file for full examples of how to use the `AgentClient`. A quick example: +See the `src/run_client.py` file for full examples of how to use the `AgentClient`. A quick example: ```python from client import AgentClient diff --git a/docker/Dockerfile.app b/docker/Dockerfile.app index 7d193ab..95ff801 100644 --- a/docker/Dockerfile.app +++ b/docker/Dockerfile.app @@ -2,12 +2,12 @@ FROM python:3.12.3-slim WORKDIR /app -COPY requirements.txt . +COPY pyproject.toml . RUN pip install --no-cache-dir uv -RUN uv pip install --system --no-cache -r requirements.txt +RUN uv pip install --system --no-cache -r pyproject.toml -COPY client/ ./client/ -COPY schema/ ./schema/ -COPY streamlit_app.py . +COPY src/client/ ./client/ +COPY src/schema/ ./schema/ +COPY src/streamlit_app.py . CMD ["streamlit", "run", "streamlit_app.py"] diff --git a/docker/Dockerfile.service b/docker/Dockerfile.service index fc2e189..def97eb 100644 --- a/docker/Dockerfile.service +++ b/docker/Dockerfile.service @@ -2,13 +2,13 @@ FROM python:3.12.3-slim WORKDIR /app -COPY requirements.txt . +COPY pyproject.toml . RUN pip install --no-cache-dir uv -RUN uv pip install --system --no-cache -r requirements.txt +RUN uv pip install --system --no-cache -r pyproject.toml -COPY agent/ ./agent/ -COPY schema/ ./schema/ -COPY service/ ./service/ -COPY run_service.py . +COPY src/agent/ ./agent/ +COPY src/schema/ ./schema/ +COPY src/service/ ./service/ +COPY src/run_service.py . CMD ["python", "run_service.py"] diff --git a/pyproject.toml b/pyproject.toml index 0f3b48e..bcc9e8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", ] -requires-python = ">=3.9, <= 3.12.3" +requires-python = ">=3.9, <=3.12.3" # NOTE: FastAPI < 0.100.0 and Pydantic v1 is required until langchain has full pydantic v2 compatibility # https://python.langchain.com/v0.1/docs/guides/development/pydantic_compatibility/ @@ -36,6 +36,7 @@ dependencies = [ "pydantic ~=1.10.17", "pyowm ~=3.3.0", "python-dotenv ~=1.0.1", + "setuptools ~=74.0.0", "streamlit ~=1.37.0", "uvicorn ~=0.30.5", ] diff --git a/requirements.txt b/requirements.txt index 13b2f1f..9b553ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,5 +19,6 @@ numexpr~=2.10.1 pydantic~=1.10.17 pyowm~=3.3.0 python-dotenv~=1.0.1 +setuptools~=74.0.0 streamlit~=1.37.0 uvicorn~=0.30.5 diff --git a/run_service.py b/run_service.py deleted file mode 100644 index a9c6bcd..0000000 --- a/run_service.py +++ /dev/null @@ -1,7 +0,0 @@ -from dotenv import load_dotenv -import uvicorn - -from service import app - -load_dotenv() -uvicorn.run(app, host="0.0.0.0", port=80) diff --git a/agent/__init__.py b/src/agent/__init__.py similarity index 100% rename from agent/__init__.py rename to src/agent/__init__.py diff --git a/agent/langgraph.json b/src/agent/langgraph.json similarity index 100% rename from agent/langgraph.json rename to src/agent/langgraph.json diff --git a/agent/llama_guard.py b/src/agent/llama_guard.py similarity index 100% rename from agent/llama_guard.py rename to src/agent/llama_guard.py diff --git a/agent/requirements.txt b/src/agent/requirements.txt similarity index 100% rename from agent/requirements.txt rename to src/agent/requirements.txt diff --git a/agent/research_assistant.py b/src/agent/research_assistant.py similarity index 100% rename from agent/research_assistant.py rename to src/agent/research_assistant.py diff --git a/agent/tools.py b/src/agent/tools.py similarity index 100% rename from agent/tools.py rename to src/agent/tools.py diff --git a/client/__init__.py b/src/client/__init__.py similarity index 100% rename from client/__init__.py rename to src/client/__init__.py diff --git a/client/client.py b/src/client/client.py similarity index 95% rename from client/client.py rename to src/client/client.py index d516f4e..5e5542a 100644 --- a/client/client.py +++ b/src/client/client.py @@ -212,12 +212,13 @@ async def acreate_feedback( See: https://api.smith.langchain.com/redoc#tag/feedback/operation/create_feedback_api_v1_feedback_post """ request = Feedback(run_id=run_id, key=key, score=score, kwargs=kwargs) - response = await self.async_client.post( - f"{self.base_url}/feedback", - json=request.dict(), - headers=self._headers, - timeout=self.timeout, - ) - if response.status_code != 200: - raise Exception(f"Error: {response.status_code} - {response.text}") - response.json() + async with httpx.AsyncClient() as client: + response = await client.post( + f"{self.base_url}/feedback", + json=request.dict(), + headers=self._headers, + timeout=self.timeout, + ) + if response.status_code != 200: + raise Exception(f"Error: {response.status_code} - {response.text}") + response.json() diff --git a/run_client.py b/src/run_client.py similarity index 100% rename from run_client.py rename to src/run_client.py diff --git a/src/run_service.py b/src/run_service.py new file mode 100644 index 0000000..23e591c --- /dev/null +++ b/src/run_service.py @@ -0,0 +1,9 @@ +from dotenv import load_dotenv +import uvicorn + +load_dotenv() + +if __name__ == "__main__": + from service import app + + uvicorn.run(app, host="0.0.0.0", port=80) diff --git a/schema/__init__.py b/src/schema/__init__.py similarity index 100% rename from schema/__init__.py rename to src/schema/__init__.py diff --git a/schema/schema.py b/src/schema/schema.py similarity index 100% rename from schema/schema.py rename to src/schema/schema.py diff --git a/schema/test_schema.py b/src/schema/test_schema.py similarity index 100% rename from schema/test_schema.py rename to src/schema/test_schema.py diff --git a/service/__init__.py b/src/service/__init__.py similarity index 100% rename from service/__init__.py rename to src/service/__init__.py diff --git a/service/service.py b/src/service/service.py similarity index 100% rename from service/service.py rename to src/service/service.py diff --git a/service/test_service.py b/src/service/test_service.py similarity index 100% rename from service/test_service.py rename to src/service/test_service.py diff --git a/streamlit_app.py b/src/streamlit_app.py similarity index 100% rename from streamlit_app.py rename to src/streamlit_app.py