From d8e7afacc5a839fe210c3e936546c6f92d8961d2 Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 13:36:23 -0800 Subject: [PATCH 1/9] serve --- agentstack/cli/__init__.py | 2 +- agentstack/cli/cli.py | 12 +++++- agentstack/deploy/Dockerfile | 36 ++++++++++++++++++ agentstack/deploy/serve.py | 73 ++++++++++++++++++++++++++++++++++++ agentstack/main.py | 7 +++- agentstack/utils.py | 1 - 6 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 agentstack/deploy/Dockerfile create mode 100644 agentstack/deploy/serve.py diff --git a/agentstack/cli/__init__.py b/agentstack/cli/__init__.py index 3c35ec37..8a40e493 100644 --- a/agentstack/cli/__init__.py +++ b/agentstack/cli/__init__.py @@ -1 +1 @@ -from .cli import init_project_builder, list_tools +from .cli import init_project_builder, list_tools, serve_project diff --git a/agentstack/cli/cli.py b/agentstack/cli/cli.py index 7277db32..30985f60 100644 --- a/agentstack/cli/cli.py +++ b/agentstack/cli/cli.py @@ -13,7 +13,7 @@ from .agentstack_data import FrameworkData, ProjectMetadata, ProjectStructure, CookiecutterData from agentstack.logger import log from .. import generation -from ..utils import open_json_file, term_color, is_snake_case +from ..utils import open_json_file, term_color, is_snake_case, verify_agentstack_project def init_project_builder(slug_name: Optional[str] = None, skip_wizard: bool = False): @@ -344,4 +344,12 @@ def list_tools(): except json.JSONDecodeError: print("Error: tools.json contains invalid JSON.") except Exception as e: - print(f"An unexpected error occurred: {e}") \ No newline at end of file + print(f"An unexpected error occurred: {e}") + + +def serve_project(): + verify_agentstack_project() + + with importlib.resources.path('agentstack.deploy', 'Dockerfile') as path: + os.system(f"docker build -t agent-service -f {path} .") + os.system("docker run --name agentstack-local -p 6969:6969 agent-service") diff --git a/agentstack/deploy/Dockerfile b/agentstack/deploy/Dockerfile new file mode 100644 index 00000000..356122e5 --- /dev/null +++ b/agentstack/deploy/Dockerfile @@ -0,0 +1,36 @@ +# Dockerfile +FROM python:3.11-slim + +WORKDIR /app + +# install git - TODO: remove after testing +RUN apt-get update && \ + apt-get install -y git && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Copy requirements first to leverage Docker cache +COPY pyproject.toml . +RUN pip install --no-cache-dir poetry +RUN pip install psutil + +RUN #pip install agentstack +RUN pip install git+https://github.com/bboynton97/AgentStack.git +#RUN ls /usr/local/lib/python3.11/site-packages +#RUN ls /usr/local/lib/python3.11/site-packages/agentstack +#RUN ls /usr/local/lib/python3.11/site-packages/agentstack/deploy +#COPY /usr/local/lib/python3.11/site-packages/agentstack/deploy/serve.py . + +RUN #pip uninstall -y agentstack + +#RUN poetry install + +# Copy the rest of the application +COPY . . + +# Expose the port the app runs on +EXPOSE 6969 + +# Command to run the application +#CMD ["python", "serve.py"] +CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py new file mode 100644 index 00000000..145ebb8b --- /dev/null +++ b/agentstack/deploy/serve.py @@ -0,0 +1,73 @@ +# app.py +import json + +from flask import Flask, request, jsonify +import requests +from agent_script import run_agent # Your existing agent script +from typing import Dict, Any +import os +from .src.main import run + +app = Flask(__name__) + + +def call_webhook(webhook_url: str, data: Dict[str, Any]) -> None: + """Send results to the specified webhook URL.""" + try: + response = requests.post(webhook_url, json=data) + response.raise_for_status() + except requests.exceptions.RequestException as e: + app.logger.error(f"Webhook call failed: {str(e)}") + raise + + +@app.route('/process', methods=['POST']) +def process_agent(): + try: + # Extract data and webhook URL from request + request_data = request.get_json() + if not request_data or 'webhook_url' not in request_data: + return jsonify({'error': 'Missing webhook_url in request'}), 400 + + webhook_url = request_data.pop('webhook_url') + + # Run the agent process with the provided data + # result = WebresearcherCrew().crew().kickoff(inputs=request_data) + # inputs = json.stringify(request_data) + # os.system(f"python src/main.py {inputs}") + result = run(request_data) + + # Call the webhook with the results + call_webhook(webhook_url, { + 'status': 'success', + 'result': result + }) + + return jsonify({ + 'status': 'success', + 'message': 'Agent process completed and webhook called' + }) + + except Exception as e: + error_message = str(e) + app.logger.error(f"Error processing request: {error_message}") + + # Attempt to call webhook with error information + if webhook_url: + try: + call_webhook(webhook_url, { + 'status': 'error', + 'error': error_message + }) + except: + pass # Webhook call failed, but we still want to return the error to the caller + + return jsonify({ + 'status': 'error', + 'error': error_message + }), 500 + + +if __name__ == '__main__': + port = int(os.environ.get('PORT', 6969)) + app.run(host='0.0.0.0', port=port) \ No newline at end of file diff --git a/agentstack/main.py b/agentstack/main.py index fdc61804..2ac948ed 100644 --- a/agentstack/main.py +++ b/agentstack/main.py @@ -1,7 +1,7 @@ import argparse import sys -from agentstack.cli import init_project_builder, list_tools +from agentstack.cli import init_project_builder, list_tools, serve_project from agentstack.utils import get_version import agentstack.generation as generation @@ -66,6 +66,9 @@ def main(): tools_remove_parser = tools_subparsers.add_parser('remove', aliases=['r'], help='Remove a tool') tools_remove_parser.add_argument('name', help='Name of the tool to remove') + # 'deploy' command + serve_parser = subparsers.add_parser('serve', aliases=['s'], help='Serve your agent') + # Parse arguments args = parser.parse_args() @@ -97,6 +100,8 @@ def main(): generation.remove_tool(args.name) else: tools_parser.print_help() + if args.command in ['serve', 's']: + serve_project() else: parser.print_help() diff --git a/agentstack/utils.py b/agentstack/utils.py index 822208d5..2f2a929a 100644 --- a/agentstack/utils.py +++ b/agentstack/utils.py @@ -78,7 +78,6 @@ def term_color(text: str, color: str) -> str: return text - def is_snake_case(string: str): return bool(re.match('^[a-z0-9_]+$', string)) From 7a60fc01b512db5d891fabadea9410a5e5dc4c59 Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 15:37:56 -0800 Subject: [PATCH 2/9] fix serve file and dockerfile --- agentstack/cli/cli.py | 3 +++ agentstack/deploy/Dockerfile | 18 ++++++++++-------- agentstack/deploy/serve.py | 5 ++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/agentstack/cli/cli.py b/agentstack/cli/cli.py index 30985f60..376a97f0 100644 --- a/agentstack/cli/cli.py +++ b/agentstack/cli/cli.py @@ -350,6 +350,9 @@ def list_tools(): def serve_project(): verify_agentstack_project() + # TODO: only silence output conditionally - maybe a debug or verbose option + os.system("docker stop agentstack-local > /dev/null 2>&1") + os.system("docker rm agentstack-local > /dev/null 2>&1") with importlib.resources.path('agentstack.deploy', 'Dockerfile') as path: os.system(f"docker build -t agent-service -f {path} .") os.system("docker run --name agentstack-local -p 6969:6969 agent-service") diff --git a/agentstack/deploy/Dockerfile b/agentstack/deploy/Dockerfile index 356122e5..ddff1daf 100644 --- a/agentstack/deploy/Dockerfile +++ b/agentstack/deploy/Dockerfile @@ -15,15 +15,15 @@ RUN pip install --no-cache-dir poetry RUN pip install psutil RUN #pip install agentstack -RUN pip install git+https://github.com/bboynton97/AgentStack.git -#RUN ls /usr/local/lib/python3.11/site-packages -#RUN ls /usr/local/lib/python3.11/site-packages/agentstack -#RUN ls /usr/local/lib/python3.11/site-packages/agentstack/deploy -#COPY /usr/local/lib/python3.11/site-packages/agentstack/deploy/serve.py . +RUN pip install git+https://github.com/bboynton97/AgentStack.git@deploy +RUN cp /usr/local/lib/python3.11/site-packages/agentstack/deploy/serve.py ./src -RUN #pip uninstall -y agentstack +RUN pip uninstall -y agentstack -#RUN poetry install +RUN apt-get update && apt-get install -y gcc +RUN POETRY_VIRTUALENVS_CREATE=false +RUN poetry config virtualenvs.create false && poetry install +RUN pip install flask # Copy the rest of the application COPY . . @@ -31,6 +31,8 @@ COPY . . # Expose the port the app runs on EXPOSE 6969 +WORKDIR . + # Command to run the application -#CMD ["python", "serve.py"] +#CMD ["python", "src/serve.py"] CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py index 145ebb8b..d48a811f 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/deploy/serve.py @@ -3,10 +3,9 @@ from flask import Flask, request, jsonify import requests -from agent_script import run_agent # Your existing agent script from typing import Dict, Any import os -from .src.main import run +from src.main import run app = Flask(__name__) @@ -70,4 +69,4 @@ def process_agent(): if __name__ == '__main__': port = int(os.environ.get('PORT', 6969)) - app.run(host='0.0.0.0', port=port) \ No newline at end of file + app.run(host='0.0.0.0', port=port) From 7f88cc034b3655fda6ce9f0de630c73dd1c449a5 Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 15:52:00 -0800 Subject: [PATCH 3/9] serve from inside source --- agentstack/deploy/Dockerfile | 10 ++++------ agentstack/deploy/serve.py | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/agentstack/deploy/Dockerfile b/agentstack/deploy/Dockerfile index ddff1daf..de9cf72c 100644 --- a/agentstack/deploy/Dockerfile +++ b/agentstack/deploy/Dockerfile @@ -12,10 +12,11 @@ RUN apt-get update && \ # Copy requirements first to leverage Docker cache COPY pyproject.toml . RUN pip install --no-cache-dir poetry -RUN pip install psutil +RUN pip install psutil flask RUN #pip install agentstack RUN pip install git+https://github.com/bboynton97/AgentStack.git@deploy +RUN mkdir src RUN cp /usr/local/lib/python3.11/site-packages/agentstack/deploy/serve.py ./src RUN pip uninstall -y agentstack @@ -23,7 +24,6 @@ RUN pip uninstall -y agentstack RUN apt-get update && apt-get install -y gcc RUN POETRY_VIRTUALENVS_CREATE=false RUN poetry config virtualenvs.create false && poetry install -RUN pip install flask # Copy the rest of the application COPY . . @@ -31,8 +31,6 @@ COPY . . # Expose the port the app runs on EXPOSE 6969 -WORKDIR . - # Command to run the application -#CMD ["python", "src/serve.py"] -CMD ["sleep", "infinity"] \ No newline at end of file +CMD ["python", "src/serve.py"] +#CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py index d48a811f..d40a3c85 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/deploy/serve.py @@ -5,7 +5,7 @@ import requests from typing import Dict, Any import os -from src.main import run +from main import run app = Flask(__name__) From b06783713c367ea359f530dbe19299f911b3df90 Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 15:57:58 -0800 Subject: [PATCH 4/9] load dotenv --- agentstack/deploy/serve.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py index d40a3c85..990a9600 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/deploy/serve.py @@ -1,11 +1,11 @@ # app.py -import json - from flask import Flask, request, jsonify import requests from typing import Dict, Any import os from main import run +from dotenv import load_dotenv +load_dotenv() app = Flask(__name__) From 606fd44117d4f653cc2fa6ac75966ef792702aab Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 16:14:25 -0800 Subject: [PATCH 5/9] load dotenv from one up --- agentstack/deploy/Dockerfile | 6 ++++-- agentstack/deploy/serve.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/agentstack/deploy/Dockerfile b/agentstack/deploy/Dockerfile index de9cf72c..43c08fae 100644 --- a/agentstack/deploy/Dockerfile +++ b/agentstack/deploy/Dockerfile @@ -9,6 +9,8 @@ RUN apt-get update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* +RUN echo "hi mom" + # Copy requirements first to leverage Docker cache COPY pyproject.toml . RUN pip install --no-cache-dir poetry @@ -32,5 +34,5 @@ COPY . . EXPOSE 6969 # Command to run the application -CMD ["python", "src/serve.py"] -#CMD ["sleep", "infinity"] \ No newline at end of file +#CMD ["python", "src/serve.py"] +CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py index 990a9600..67d27711 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/deploy/serve.py @@ -5,7 +5,7 @@ import os from main import run from dotenv import load_dotenv -load_dotenv() +load_dotenv(dotenv_path="../") app = Flask(__name__) From a240d5dba6db010d516040d64cc5969f055003d3 Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 16:26:04 -0800 Subject: [PATCH 6/9] health endpoint --- agentstack/deploy/Dockerfile | 6 +++--- agentstack/deploy/serve.py | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/agentstack/deploy/Dockerfile b/agentstack/deploy/Dockerfile index 43c08fae..8406ce3f 100644 --- a/agentstack/deploy/Dockerfile +++ b/agentstack/deploy/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* -RUN echo "hi mom" +RUN echo "hi dad" # Copy requirements first to leverage Docker cache COPY pyproject.toml . @@ -34,5 +34,5 @@ COPY . . EXPOSE 6969 # Command to run the application -#CMD ["python", "src/serve.py"] -CMD ["sleep", "infinity"] \ No newline at end of file +CMD ["python", "src/serve.py"] +#CMD ["sleep", "infinity"] \ No newline at end of file diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py index 67d27711..d76c0e15 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/deploy/serve.py @@ -20,6 +20,11 @@ def call_webhook(webhook_url: str, data: Dict[str, Any]) -> None: raise +@app.route("/health", methods=["GET"]) +def health(): + return "Agent Server Up" + + @app.route('/process', methods=['POST']) def process_agent(): try: From b9d994214a83ed5f4ce1c48e1bb3f530564519ba Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 16:27:57 -0800 Subject: [PATCH 7/9] env path --- agentstack/deploy/serve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py index d76c0e15..8e264d07 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/deploy/serve.py @@ -5,7 +5,7 @@ import os from main import run from dotenv import load_dotenv -load_dotenv(dotenv_path="../") +load_dotenv(dotenv_path="../.env") app = Flask(__name__) From e25b51a2f77d0b1418f12bd02973bfa90eb8aa1c Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 19 Nov 2024 16:48:17 -0800 Subject: [PATCH 8/9] loadenv before import --- agentstack/deploy/Dockerfile | 5 +---- agentstack/deploy/serve.py | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/agentstack/deploy/Dockerfile b/agentstack/deploy/Dockerfile index 8406ce3f..1795b78c 100644 --- a/agentstack/deploy/Dockerfile +++ b/agentstack/deploy/Dockerfile @@ -9,8 +9,6 @@ RUN apt-get update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* -RUN echo "hi dad" - # Copy requirements first to leverage Docker cache COPY pyproject.toml . RUN pip install --no-cache-dir poetry @@ -34,5 +32,4 @@ COPY . . EXPOSE 6969 # Command to run the application -CMD ["python", "src/serve.py"] -#CMD ["sleep", "infinity"] \ No newline at end of file +CMD ["python", "src/serve.py"] \ No newline at end of file diff --git a/agentstack/deploy/serve.py b/agentstack/deploy/serve.py index 8e264d07..ac0c32d3 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/deploy/serve.py @@ -1,11 +1,12 @@ # app.py +from dotenv import load_dotenv +load_dotenv(dotenv_path="/app/.env") + from flask import Flask, request, jsonify import requests from typing import Dict, Any import os from main import run -from dotenv import load_dotenv -load_dotenv(dotenv_path="../.env") app = Flask(__name__) From ef3a3fd52a1fac30ec8a737e2810968b78b3d8f3 Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Tue, 7 Jan 2025 20:38:17 +0000 Subject: [PATCH 9/9] serve working --- agentstack/cli/__init__.py | 2 +- agentstack/cli/cli.py | 6 +++--- agentstack/main.py | 4 ---- agentstack/{deploy => serve}/Dockerfile | 0 agentstack/{deploy => serve}/serve.py | 5 +++++ 5 files changed, 9 insertions(+), 8 deletions(-) rename agentstack/{deploy => serve}/Dockerfile (100%) rename agentstack/{deploy => serve}/serve.py (91%) diff --git a/agentstack/cli/__init__.py b/agentstack/cli/__init__.py index 481991b0..9a369d0b 100644 --- a/agentstack/cli/__init__.py +++ b/agentstack/cli/__init__.py @@ -1,3 +1,3 @@ -from .cli import init_project_builder, configure_default_model, export_template, list_tools +from .cli import init_project_builder, configure_default_model, export_template, serve_project from .tools import list_tools, add_tool from .run import run_project diff --git a/agentstack/cli/cli.py b/agentstack/cli/cli.py index cc909e36..e4dc4d4c 100644 --- a/agentstack/cli/cli.py +++ b/agentstack/cli/cli.py @@ -1,3 +1,4 @@ +import importlib from typing import Optional import os import sys @@ -19,16 +20,15 @@ from agentstack.logger import log from agentstack import conf from agentstack.conf import ConfigFile -from agentstack.utils import get_package_path, open_json_file, term_color, is_snake_case, get_framework, validator_not_empty +from agentstack.utils import get_package_path, get_framework, validator_not_empty from agentstack.generation.files import ProjectFile from agentstack import frameworks -from agentstack import packaging, generation +from agentstack import generation from agentstack import inputs from agentstack.agents import get_all_agents from agentstack.tasks import get_all_tasks from agentstack.proj_templates import TemplateConfig from agentstack.exceptions import ValidationError -from agentstack.generation.files import ConfigFile from agentstack.utils import open_json_file, term_color, is_snake_case, verify_agentstack_project PREFERRED_MODELS = [ diff --git a/agentstack/main.py b/agentstack/main.py index 9148bf0e..8593c7cd 100644 --- a/agentstack/main.py +++ b/agentstack/main.py @@ -1,9 +1,5 @@ import sys from agentstack.cli import init_project_builder, list_tools, configure_default_model, serve_project -from agentstack.telemetry import track_cli_command -from agentstack.utils import get_version, get_framework -import agentstack.generation as generation -from agentstack.update import check_for_updates import argparse import webbrowser diff --git a/agentstack/deploy/Dockerfile b/agentstack/serve/Dockerfile similarity index 100% rename from agentstack/deploy/Dockerfile rename to agentstack/serve/Dockerfile diff --git a/agentstack/deploy/serve.py b/agentstack/serve/serve.py similarity index 91% rename from agentstack/deploy/serve.py rename to agentstack/serve/serve.py index ac0c32d3..587bc01e 100644 --- a/agentstack/deploy/serve.py +++ b/agentstack/serve/serve.py @@ -75,4 +75,9 @@ def process_agent(): if __name__ == '__main__': port = int(os.environ.get('PORT', 6969)) + + print("🚧 Running your agent on a development server") + print(f"Send agent requests to http://localhost:{port}") + print("Learn more about agent requests at https://docs.agentstack.sh/") # TODO: add docs for this + app.run(host='0.0.0.0', port=port)