From 09f6a794281bd773c08153845d257b70e82c3a80 Mon Sep 17 00:00:00 2001 From: Reza Rahemtola Date: Tue, 10 Dec 2024 12:01:59 +0900 Subject: [PATCH] feat(agent): Working agent deployment --- libertai_client/commands/agent.py | 59 +++++++++++++++++++++++-------- libertai_client/config.py | 5 +-- libertai_client/utils/agent.py | 3 +- libertai_client/utils/system.py | 6 ++-- 4 files changed, 53 insertions(+), 20 deletions(-) diff --git a/libertai_client/commands/agent.py b/libertai_client/commands/agent.py index a23b595..13a6723 100644 --- a/libertai_client/commands/agent.py +++ b/libertai_client/commands/agent.py @@ -1,5 +1,6 @@ import os import zipfile +from enum import Enum from typing import Annotated import aiohttp @@ -23,28 +24,52 @@ def create_agent_zip(src_dir: str, zip_name: str): # Read and parse the .gitignore file - with open(get_full_path(src_dir, ".gitignore"), 'r') as gitignore_file: + with open(get_full_path(src_dir, ".gitignore"), "r") as gitignore_file: gitignore_patterns = gitignore_file.read() - spec = pathspec.PathSpec.from_lines('gitwildmatch', gitignore_patterns.splitlines() + AGENT_ZIP_BLACKLIST) + spec = pathspec.PathSpec.from_lines( + "gitwildmatch", gitignore_patterns.splitlines() + AGENT_ZIP_BLACKLIST + ) - with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as zipf: + with zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) as zipf: for root, _, files in os.walk(src_dir): for file in files: file_path = os.path.join(root, file) relative_path = os.path.relpath(file_path, src_dir) # Check if the file matches any .gitignore pattern - if not spec.match_file(relative_path) or relative_path in AGENT_ZIP_WHITELIST: + if ( + not spec.match_file(relative_path) + or relative_path in AGENT_ZIP_WHITELIST + ): zipf.write(file_path, arcname=relative_path) +class AgentPythonPackageManager(str, Enum): + poetry = "poetry" + pip = "pip" + + @app.command() -async def deploy(path: Annotated[str, typer.Option(help="Path to the root of your repository", prompt=True)] = ".", - python_version: Annotated[str, typer.Option(help="Version to deploy with", prompt=True)] = "3.11"): +async def deploy( + path: Annotated[ + str, typer.Option(help="Path to the root of your repository", prompt=True) + ] = ".", + python_version: Annotated[ + str, typer.Option(help="Version to deploy with", prompt=True) + ] = "3.11", + package_manager: Annotated[ + AgentPythonPackageManager, typer.Option(case_sensitive=False, prompt=True) + ] = AgentPythonPackageManager.pip.value, # type: ignore +): """ Deploy or redeploy an agent """ + # TODO: try to detect package manager, show detected value and ask user for the confirmation or change + # Same for python version + + # TODO: allow user to give a custom deployment script URL + try: libertai_env_path = get_full_path(path, ".env.libertai") libertai_config = parse_agent_config_env(dotenv_values(libertai_env_path)) @@ -52,25 +77,29 @@ async def deploy(path: Annotated[str, typer.Option(help="Path to the root of you err_console.print(f"[red]{error}") raise typer.Exit(1) - create_agent_zip(path, "/tmp/libertai-agent.zip") + agent_zip_path = "/tmp/libertai-agent.zip" + + create_agent_zip(path, agent_zip_path) data = aiohttp.FormData() - data.add_field('secret', libertai_config.secret) - data.add_field('python_version', python_version) - data.add_field('package_manager', "poetry") # TODO: detect/ask user - data.add_field('code', open('/tmp/libertai-agent.zip', 'rb'), filename='libertai-agent.zip') + data.add_field("secret", libertai_config.secret) + data.add_field("python_version", python_version) + data.add_field("package_manager", package_manager.value) + data.add_field("code", open(agent_zip_path, "rb"), filename="libertai-agent.zip") async with aiohttp.ClientSession() as session: - async with session.put(f"{config.AGENTS_BACKEND_URL}/agent/{libertai_config.id}", - headers={'accept': 'application/json'}, - data=data) as response: + async with session.put( + f"{config.AGENTS_BACKEND_URL}/agent/{libertai_config.id}", + headers={"accept": "application/json"}, + data=data, + ) as response: if response.status == 200: print("Request succeeded:", await response.text()) else: print(f"Request failed: {response.status}") print(await response.text()) - os.remove("/tmp/libertai-agent.zip") + os.remove(agent_zip_path) # with Progress(TextColumn(TEXT_PROGRESS_FORMAT), # SpinnerColumn(finished_text="✔ ")) as progress: diff --git a/libertai_client/config.py b/libertai_client/config.py index b4cec22..039f67a 100644 --- a/libertai_client/config.py +++ b/libertai_client/config.py @@ -5,8 +5,9 @@ class _Config: AGENTS_BACKEND_URL: str def __init__(self): - self.AGENTS_BACKEND_URL = os.getenv("LIBERTAI_CLIENT_AGENTS_BACKEND_URL", - "https://agent.api.libertai.io") + self.AGENTS_BACKEND_URL = os.getenv( + "LIBERTAI_CLIENT_AGENTS_BACKEND_URL", "https://agent.api.libertai.io" + ) config = _Config() diff --git a/libertai_client/utils/agent.py b/libertai_client/utils/agent.py index 84dd497..cbe10d3 100644 --- a/libertai_client/utils/agent.py +++ b/libertai_client/utils/agent.py @@ -7,6 +7,7 @@ def parse_agent_config_env(env: dict[str, str | None]) -> AgentConfig: if agent_id is None or agent_secret is None: raise EnvironmentError( - f"Missing {'LIBERTAI_AGENT_ID' if agent_id is None else 'LIBERTAI_AGENT_SECRET'} variable in your project's .env.libertai") + f"Missing {'LIBERTAI_AGENT_ID' if agent_id is None else 'LIBERTAI_AGENT_SECRET'} variable in your project's .env.libertai" + ) return AgentConfig(id=agent_id, secret=agent_secret) diff --git a/libertai_client/utils/system.py b/libertai_client/utils/system.py index f94eb6a..3b40137 100644 --- a/libertai_client/utils/system.py +++ b/libertai_client/utils/system.py @@ -5,7 +5,9 @@ def __validate_path(path: str, with_file: bool = False) -> str: is_valid_path = os.path.exists(path) if not is_valid_path: - raise FileNotFoundError(f"{'File' if with_file else 'Folder'} '{path}' doesn't exist.") + raise FileNotFoundError( + f"{'File' if with_file else 'Folder'} '{path}' doesn't exist." + ) return path @@ -15,5 +17,5 @@ def get_full_path(folder_path: str, file: str | None = None) -> str: path = os.path.abspath(folder_path) return __validate_path(path, with_file=False) - path = os.path.abspath(f'{folder_path}/{file}') + path = os.path.abspath(f"{folder_path}/{file}") return __validate_path(path, with_file=True)