From 324871966713599e56b658435c443ad97d3ca5ee Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Mon, 16 Dec 2024 16:01:37 -0800 Subject: [PATCH 1/4] Initialize project now creates a virtual enviroment for you. All package management done with `uv`. TODO: `packaging.remove` and `packaging.upgrade` still need to be implemented. --- agentstack/cli/__init__.py | 3 +- agentstack/cli/cli.py | 51 +++---- agentstack/cli/init.py | 70 ++++++++++ agentstack/cli/run.py | 1 + agentstack/exceptions.py | 9 ++ agentstack/main.py | 4 +- agentstack/packaging.py | 131 ++++++++++++++++-- .../pyproject.toml | 23 ++- pyproject.toml | 1 + 9 files changed, 239 insertions(+), 54 deletions(-) create mode 100644 agentstack/cli/init.py diff --git a/agentstack/cli/__init__.py b/agentstack/cli/__init__.py index 32c08ec..d4d83bb 100644 --- a/agentstack/cli/__init__.py +++ b/agentstack/cli/__init__.py @@ -1,3 +1,4 @@ -from .cli import init_project_builder, configure_default_model, export_template +from .cli import init_project_builder, configure_default_model, export_template, welcome_message +from .init import init_project from .tools import list_tools, add_tool from .run import run_project \ No newline at end of file diff --git a/agentstack/cli/cli.py b/agentstack/cli/cli.py index 0c085d5..8837cd9 100644 --- a/agentstack/cli/cli.py +++ b/agentstack/cli/cli.py @@ -88,7 +88,6 @@ def init_project_builder( tools = [tools.model_dump() for tools in template_data.tools] elif use_wizard: - welcome_message() project_details = ask_project_details(slug_name) welcome_message() framework = ask_framework() @@ -96,7 +95,6 @@ def init_project_builder( tools = ask_tools() else: - welcome_message() # the user has started a new project; let's give them something to work with default_project = TemplateConfig.from_template_name('hello_alex') project_details = { @@ -117,9 +115,6 @@ def init_project_builder( log.debug(f"project_details: {project_details}" f"framework: {framework}" f"design: {design}") insert_template(project_details, framework, design, template_data) - # we have an agentstack.json file in the directory now - conf.set_path(project_details['name']) - for tool_data in tools: generation.add_tool(tool_data['name'], agents=tool_data['agents']) @@ -395,14 +390,14 @@ def insert_template( f'{template_path}/{"{{cookiecutter.project_metadata.project_slug}}"}/.env', ) - if os.path.isdir(project_details['name']): - print( - term_color( - f"Directory {template_path} already exists. Please check this and try again", - "red", - ) - ) - sys.exit(1) + # if os.path.isdir(project_details['name']): + # print( + # term_color( + # f"Directory {template_path} already exists. Please check this and try again", + # "red", + # ) + # ) + # sys.exit(1) cookiecutter(str(template_path), no_input=True, extra_context=None) @@ -420,21 +415,21 @@ def insert_template( # os.system("poetry install") # os.system("cls" if os.name == "nt" else "clear") # TODO: add `agentstack docs` command - print( - "\n" - "🚀 \033[92mAgentStack project generated successfully!\033[0m\n\n" - " Next, run:\n" - f" cd {project_metadata.project_slug}\n" - " python -m venv .venv\n" - " source .venv/bin/activate\n\n" - " Make sure you have the latest version of poetry installed:\n" - " pip install -U poetry\n\n" - " You'll need to install the project's dependencies with:\n" - " poetry install\n\n" - " Finally, try running your agent with:\n" - " agentstack run\n\n" - " Run `agentstack quickstart` or `agentstack docs` for next steps.\n" - ) + # print( + # "\n" + # "🚀 \033[92mAgentStack project generated successfully!\033[0m\n\n" + # " Next, run:\n" + # f" cd {project_metadata.project_slug}\n" + # " python -m venv .venv\n" + # " source .venv/bin/activate\n\n" + # " Make sure you have the latest version of poetry installed:\n" + # " pip install -U poetry\n\n" + # " You'll need to install the project's dependencies with:\n" + # " poetry install\n\n" + # " Finally, try running your agent with:\n" + # " agentstack run\n\n" + # " Run `agentstack quickstart` or `agentstack docs` for next steps.\n" + # ) def export_template(output_filename: str): diff --git a/agentstack/cli/init.py b/agentstack/cli/init.py new file mode 100644 index 0000000..6ce201a --- /dev/null +++ b/agentstack/cli/init.py @@ -0,0 +1,70 @@ +import os, sys +from typing import Optional +from pathlib import Path +from agentstack import conf +from agentstack import packaging +from agentstack.cli import welcome_message, init_project_builder +from agentstack.utils import term_color + + +# TODO move the rest of the CLI init tooling into this file + + +def require_uv(): + try: + uv_bin = packaging.get_uv_bin() + assert os.path.exists(uv_bin) + except (AssertionError, ImportError): + print(term_color("Error: uv is not installed.", 'red')) + print("Full installation instructions at: https://docs.astral.sh/uv/getting-started/installation") + match sys.platform: + case 'linux' | 'darwin': + print("Hint: run `curl -LsSf https://astral.sh/uv/install.sh | sh`") + case _: + pass + sys.exit(1) + + +def init_project( + slug_name: Optional[str] = None, + template: Optional[str] = None, + use_wizard: bool = False, +): + """ + Initialize a new project in the current directory. + + - create a new virtual environment + - copy project skeleton + - install dependencies + """ + welcome_message() + + # conf.PATH may have been set by the argument parser, but if not, use the slug_name + if slug_name: + conf.set_path(conf.PATH / slug_name) + else: + print("Error: No project directory specified.") + print("Run `agentstack init or use the --path flag.") + sys.exit(1) + + if os.path.exists(conf.PATH): + print(f"Error: Directory already exists: {conf.PATH}") + sys.exit(1) + + print(term_color("🦾 Creating a new AgentStack project...", 'blue')) + print(f"Using project directory: {conf.PATH.absolute()}") + # copy the project skeleton, create a virtual environment, and install dependencies + init_project_builder(slug_name, template, use_wizard) + packaging.create_venv() + packaging.install('.') + + print( + "\n" + "🚀 \033[92mAgentStack project generated successfully!\033[0m\n\n" + " To get started, activate the virtual environment with:\n" + f" cd {conf.PATH}\n" + " source .venv/bin/activate\n\n" + " Run your new agent with:\n" + " agentstack run\n\n" + " Or, run `agentstack quickstart` or `agentstack docs` for more next steps.\n" + ) diff --git a/agentstack/cli/run.py b/agentstack/cli/run.py index 17c48b4..c05ccd3 100644 --- a/agentstack/cli/run.py +++ b/agentstack/cli/run.py @@ -83,6 +83,7 @@ def _import_project_module(path: Path): assert spec is not None # appease type checker assert spec.loader is not None # appease type checker + print('dev version') project_module = importlib.util.module_from_spec(spec) sys.path.append(str((path / MAIN_FILENAME).parent)) spec.loader.exec_module(project_module) diff --git a/agentstack/exceptions.py b/agentstack/exceptions.py index c0e9556..65a433e 100644 --- a/agentstack/exceptions.py +++ b/agentstack/exceptions.py @@ -5,3 +5,12 @@ class ValidationError(Exception): """ pass + + +class EnvironmentError(Exception): + """ + Raised when an error occurs in the execution environment ie. a command is + not present or the environment is not configured as expected. + """ + + pass diff --git a/agentstack/main.py b/agentstack/main.py index eac6482..24d6977 100644 --- a/agentstack/main.py +++ b/agentstack/main.py @@ -4,7 +4,7 @@ from agentstack import conf from agentstack.cli import ( - init_project_builder, + init_project, add_tool, list_tools, configure_default_model, @@ -162,7 +162,7 @@ def main(): elif args.command in ["templates"]: webbrowser.open("https://docs.agentstack.sh/quickstart") elif args.command in ["init", "i"]: - init_project_builder(args.slug_name, args.template, args.wizard) + init_project(args.slug_name, args.template, args.wizard) elif args.command in ["run", "r"]: run_project(command=args.function, debug=args.debug, cli_args=extra_args) elif args.command in ['generate', 'g']: diff --git a/agentstack/packaging.py b/agentstack/packaging.py index fb0e3cb..729a28c 100644 --- a/agentstack/packaging.py +++ b/agentstack/packaging.py @@ -1,18 +1,131 @@ -import os -from typing import Optional +import os, sys +from typing import Optional, Callable +from pathlib import Path +import re +import subprocess +import select +from agentstack import conf +from agentstack.exceptions import EnvironmentError -PACKAGING_CMD = "poetry" +DEFAULT_PYTHON_VERSION = "3.12" +VENV_DIR_NAME: Path = Path(".venv") -def install(package: str, path: Optional[str] = None): - if path: - os.chdir(path) - os.system(f"{PACKAGING_CMD} add {package}") + +def install(package: str): + """ + Install a package with `uv`. + Filter output to only show useful progress messages. + """ + RE_USEFUL_PROGRESS = re.compile(r'^(Resolved|Prepared|Installed|Audited)') + + def on_progress(line: str): + # only print these four messages: + # Resolved 78 packages in 225ms + # Prepared 12 packages in 915ms + # Installed 78 packages in 65ms + # Audited 1 package in 28ms + if RE_USEFUL_PROGRESS.match(line): + print(line.strip()) + + def on_error(line: str): + print(f"uv: [error]\n {line.strip()}") + + # explicitly specify the --python executable to use so that the packages + # are installed into the correct virtual environment + _wrap_command_with_callbacks( + [get_uv_bin(), 'pip', 'install', '--python', '.venv/bin/python', package], + on_progress=on_progress, + on_error=on_error, + ) def remove(package: str): - os.system(f"{PACKAGING_CMD} remove {package}") + raise NotImplementedError("TODO `packaging.remove`") def upgrade(package: str): - os.system(f"{PACKAGING_CMD} add {package}") + raise NotImplementedError("TODO `packaging.upgrade`") + + +def create_venv(python_version: str = DEFAULT_PYTHON_VERSION): + """Intialize a virtual environment in the project directory of one does not exist.""" + if os.path.exists(conf.PATH / VENV_DIR_NAME): + return # venv already exists + + RE_USEFUL_PROGRESS = re.compile(r'^(Using|Creating)') + + def on_progress(line: str): + if RE_USEFUL_PROGRESS.match(line): + print(line.strip()) + + def on_error(line: str): + print(f"uv: [error]\n {line.strip()}") + + _wrap_command_with_callbacks( + [get_uv_bin(), 'venv', '--python', python_version], + on_progress=on_progress, + on_error=on_error, + ) + + +def get_uv_bin() -> str: + """Find the path to the uv binary.""" + try: + import uv + + return uv.find_uv_bin() + except ImportError as e: + raise e + + +def _setup_env() -> dict[str, str]: + """Copy the current environment and add the virtual environment path for use by a subprocess.""" + env = os.environ.copy() + env.setdefault("VIRTUAL_ENV", VENV_DIR_NAME) + env["UV_INTERNAL__PARENT_INTERPRETER"] = sys.executable + return env + + +def _wrap_command_with_callbacks( + command: list[str], + on_progress: Callable[[str], None] = lambda x: None, + on_complete: Callable[[str], None] = lambda x: None, + on_error: Callable[[str], None] = lambda x: None, +) -> None: + """Run a command with progress callbacks.""" + try: + all_lines = '' + process = subprocess.Popen( + command, + cwd=conf.PATH.absolute(), + env=_setup_env(), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + assert process.stdout and process.stderr # appease type checker + + readable = [process.stdout, process.stderr] + while readable: + ready, _, _ = select.select(readable, [], []) + for fd in ready: + line = fd.readline() + if not line: + readable.remove(fd) + continue + + on_progress(line) + all_lines += line + + if process.wait() == 0: # return code: success + on_complete(all_lines) + else: + on_error(all_lines) + except Exception as e: + on_error(str(e)) + finally: + try: + process.terminate() + except: + pass diff --git a/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml b/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml index a8d8807..f931a97 100644 --- a/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml +++ b/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml @@ -1,18 +1,13 @@ -[tool.poetry] +[project] name = "{{cookiecutter.project_metadata.project_name}}" version = "{{cookiecutter.project_metadata.version}}" description = "{{cookiecutter.project_metadata.description}}" -authors = ["{{cookiecutter.project_metadata.author_name}}"] -license = "{{cookiecutter.project_metadata.license}}" -package-mode = false +authors = [ + { name = "{{cookiecutter.project_metadata.author_name}}" } +] +license = { text = "{{cookiecutter.project_metadata.license}}" } +requires-python = ">=3.10" -[tool.poetry.dependencies] -python = ">=3.10,<=3.13" -agentstack = {extras = ["{{cookiecutter.framework}}"], version="{{cookiecutter.project_metadata.agentstack_version}}"} - -[project.scripts] -{{cookiecutter.project_metadata.project_name}} = "{{cookiecutter.project_metadata.project_name}}.main:run" -run_crew = "{{cookiecutter.project_metadata.project_name}}.main:run" -train = "{{cookiecutter.project_metadata.project_name}}.main:train" -replay = "{{cookiecutter.project_metadata.project_name}}.main:replay" -test = "{{cookiecutter.project_metadata.project_name}}.main:test" \ No newline at end of file +dependencies = [ + "agentstack[{{cookiecutter.framework}}]=={{cookiecutter.project_metadata.agentstack_version}}", +] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 69f3c30..a06404e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ dependencies = [ "requests>=2.32", "appdirs>=1.4.4", "python-dotenv>=1.0.1", + "uv>=0.5.6", ] [project.optional-dependencies] From c55dfcecf33c029f767cffd2a2b0d125c6dc9978 Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Tue, 17 Dec 2024 16:16:49 -0800 Subject: [PATCH 2/4] Implement packaging update and remove, move project dependency installs to packaging.install_project and provide a gernalizable install command. --- agentstack/cli/init.py | 12 +-- agentstack/packaging.py | 82 ++++++++++++++----- .../pyproject.toml | 2 +- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/agentstack/cli/init.py b/agentstack/cli/init.py index 6ce201a..6fd546d 100644 --- a/agentstack/cli/init.py +++ b/agentstack/cli/init.py @@ -37,26 +37,28 @@ def init_project( - copy project skeleton - install dependencies """ - welcome_message() + require_uv() - # conf.PATH may have been set by the argument parser, but if not, use the slug_name + # TODO prevent the user from passing the --path arguent to init if slug_name: conf.set_path(conf.PATH / slug_name) else: print("Error: No project directory specified.") - print("Run `agentstack init or use the --path flag.") + print("Run `agentstack init `") sys.exit(1) - if os.path.exists(conf.PATH): + if os.path.exists(conf.PATH): # cookiecutter requires the directory to not exist print(f"Error: Directory already exists: {conf.PATH}") sys.exit(1) + welcome_message() print(term_color("🦾 Creating a new AgentStack project...", 'blue')) print(f"Using project directory: {conf.PATH.absolute()}") + # copy the project skeleton, create a virtual environment, and install dependencies init_project_builder(slug_name, template, use_wizard) packaging.create_venv() - packaging.install('.') + packaging.install_project() print( "\n" diff --git a/agentstack/packaging.py b/agentstack/packaging.py index 729a28c..b472a51 100644 --- a/agentstack/packaging.py +++ b/agentstack/packaging.py @@ -5,47 +5,89 @@ import subprocess import select from agentstack import conf -from agentstack.exceptions import EnvironmentError DEFAULT_PYTHON_VERSION = "3.12" VENV_DIR_NAME: Path = Path(".venv") +# filter uv output by these words to only show useful progress messages +RE_UV_PROGRESS = re.compile(r'^(Resolved|Prepared|Installed|Uninstalled|Audited)') + + +# When calling `uv` we explicitly specify the --python executable to use so that +# the packages are installed into the correct virtual environment. +# In testing, when this was not set, packages could end up in the pyenv's +# site-packages directory; it's possible an environemnt variable can control this. + def install(package: str): - """ - Install a package with `uv`. - Filter output to only show useful progress messages. - """ - RE_USEFUL_PROGRESS = re.compile(r'^(Resolved|Prepared|Installed|Audited)') + """Install a package with `uv` and add it to pyproject.toml.""" def on_progress(line: str): - # only print these four messages: - # Resolved 78 packages in 225ms - # Prepared 12 packages in 915ms - # Installed 78 packages in 65ms - # Audited 1 package in 28ms - if RE_USEFUL_PROGRESS.match(line): + if RE_UV_PROGRESS.match(line): print(line.strip()) def on_error(line: str): print(f"uv: [error]\n {line.strip()}") - # explicitly specify the --python executable to use so that the packages - # are installed into the correct virtual environment _wrap_command_with_callbacks( - [get_uv_bin(), 'pip', 'install', '--python', '.venv/bin/python', package], + [get_uv_bin(), 'add', '--python', '.venv/bin/python', package], + on_progress=on_progress, + on_error=on_error, + ) + + +def install_project(): + """Install all dependencies for the user's project.""" + + def on_progress(line: str): + if RE_UV_PROGRESS.match(line): + print(line.strip()) + + def on_error(line: str): + print(f"uv: [error]\n {line.strip()}") + + _wrap_command_with_callbacks( + [get_uv_bin(), 'pip', 'install', '--python', '.venv/bin/python', '.'], on_progress=on_progress, on_error=on_error, ) def remove(package: str): - raise NotImplementedError("TODO `packaging.remove`") + """Uninstall a package with `uv`.""" + + # TODO it may be worth considering removing unused sub-dependencies as well + def on_progress(line: str): + if RE_UV_PROGRESS.match(line): + print(line.strip()) + + def on_error(line: str): + print(f"uv: [error]\n {line.strip()}") + + _wrap_command_with_callbacks( + [get_uv_bin(), 'remove', '--python', '.venv/bin/python', package], + on_progress=on_progress, + on_error=on_error, + ) def upgrade(package: str): - raise NotImplementedError("TODO `packaging.upgrade`") + """Upgrade a package with `uv`.""" + + # TODO should we try to update the project's pyproject.toml as well? + def on_progress(line: str): + if RE_UV_PROGRESS.match(line): + print(line.strip()) + + def on_error(line: str): + print(f"uv: [error]\n {line.strip()}") + + _wrap_command_with_callbacks( + [get_uv_bin(), 'pip', 'install', '-U', '--python', '.venv/bin/python', package], + on_progress=on_progress, + on_error=on_error, + ) def create_venv(python_version: str = DEFAULT_PYTHON_VERSION): @@ -53,10 +95,10 @@ def create_venv(python_version: str = DEFAULT_PYTHON_VERSION): if os.path.exists(conf.PATH / VENV_DIR_NAME): return # venv already exists - RE_USEFUL_PROGRESS = re.compile(r'^(Using|Creating)') + RE_VENV_PROGRESS = re.compile(r'^(Using|Creating)') def on_progress(line: str): - if RE_USEFUL_PROGRESS.match(line): + if RE_VENV_PROGRESS.match(line): print(line.strip()) def on_error(line: str): @@ -82,7 +124,7 @@ def get_uv_bin() -> str: def _setup_env() -> dict[str, str]: """Copy the current environment and add the virtual environment path for use by a subprocess.""" env = os.environ.copy() - env.setdefault("VIRTUAL_ENV", VENV_DIR_NAME) + env["VIRTUAL_ENV"] = str(conf.PATH / VENV_DIR_NAME.absolute()) env["UV_INTERNAL__PARENT_INTERPRETER"] = sys.executable return env diff --git a/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml b/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml index f931a97..f6dc4cf 100644 --- a/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml +++ b/agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/pyproject.toml @@ -9,5 +9,5 @@ license = { text = "{{cookiecutter.project_metadata.license}}" } requires-python = ">=3.10" dependencies = [ - "agentstack[{{cookiecutter.framework}}]=={{cookiecutter.project_metadata.agentstack_version}}", + "agentstack[{{cookiecutter.framework}}]>={{cookiecutter.project_metadata.agentstack_version}}", ] \ No newline at end of file From 609b1bb2952c3e5ed9b9a848b3c7e1929077b7e8 Mon Sep 17 00:00:00 2001 From: Travis Dent Date: Tue, 17 Dec 2024 16:48:44 -0800 Subject: [PATCH 3/4] Cleanup rogue print statement, commented code --- agentstack/cli/cli.py | 20 -------------------- agentstack/cli/run.py | 1 - 2 files changed, 21 deletions(-) diff --git a/agentstack/cli/cli.py b/agentstack/cli/cli.py index 8837cd9..a862143 100644 --- a/agentstack/cli/cli.py +++ b/agentstack/cli/cli.py @@ -411,26 +411,6 @@ def insert_template( except: print("Failed to initialize git repository. Maybe you're already in one? Do this with: git init") - # TODO: check if poetry is installed and if so, run poetry install in the new directory - # os.system("poetry install") - # os.system("cls" if os.name == "nt" else "clear") - # TODO: add `agentstack docs` command - # print( - # "\n" - # "🚀 \033[92mAgentStack project generated successfully!\033[0m\n\n" - # " Next, run:\n" - # f" cd {project_metadata.project_slug}\n" - # " python -m venv .venv\n" - # " source .venv/bin/activate\n\n" - # " Make sure you have the latest version of poetry installed:\n" - # " pip install -U poetry\n\n" - # " You'll need to install the project's dependencies with:\n" - # " poetry install\n\n" - # " Finally, try running your agent with:\n" - # " agentstack run\n\n" - # " Run `agentstack quickstart` or `agentstack docs` for next steps.\n" - # ) - def export_template(output_filename: str): """ diff --git a/agentstack/cli/run.py b/agentstack/cli/run.py index c05ccd3..17c48b4 100644 --- a/agentstack/cli/run.py +++ b/agentstack/cli/run.py @@ -83,7 +83,6 @@ def _import_project_module(path: Path): assert spec is not None # appease type checker assert spec.loader is not None # appease type checker - print('dev version') project_module = importlib.util.module_from_spec(spec) sys.path.append(str((path / MAIN_FILENAME).parent)) spec.loader.exec_module(project_module) From 157e42cedf7c4203af6e3fb7bbb28b228d78a8ab Mon Sep 17 00:00:00 2001 From: Braelyn Boynton Date: Thu, 9 Jan 2025 18:39:57 -0800 Subject: [PATCH 4/4] installation docs --- agentstack/telemetry.py | 4 ++-- docs/installation.mdx | 52 +++++++++++++++++++++++++++++------------ tox.ini | 2 +- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/agentstack/telemetry.py b/agentstack/telemetry.py index 0008efb..db6123e 100644 --- a/agentstack/telemetry.py +++ b/agentstack/telemetry.py @@ -76,7 +76,7 @@ def collect_machine_telemetry(command: str): def track_cli_command(command: str, args: Optional[str] = None): - if bool(os.environ['AGENTSTATCK_IS_TEST_ENV']): + if bool(os.getenv('AGENTSTACK_IS_TEST_ENV')): return try: @@ -91,7 +91,7 @@ def track_cli_command(command: str, args: Optional[str] = None): pass def update_telemetry(id: int, result: int, message: Optional[str] = None): - if bool(os.environ['AGENTSTATCK_IS_TEST_ENV']): + if bool(os.getenv('AGENTSTACK_IS_TEST_ENV')): return try: diff --git a/docs/installation.mdx b/docs/installation.mdx index 5238caf..042db2e 100644 --- a/docs/installation.mdx +++ b/docs/installation.mdx @@ -4,28 +4,50 @@ description: 'Installing AgentStack is super easy!' icon: 'cloud-arrow-down' --- -Use your favorite package manager - - +## Installing with Brew +```bash +brew tap agentops-ai/tap +brew install agentstack +``` -```bash PIP -pip install agentstack +## Installing with pipx +```bash +pipx install agentstack ``` -```bash brew -brew tap agentstack-ai/tap -brew install agentstack + +## Installing with UV + + + +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh ``` -```bash uv -uv install agentstack + + +```bash +uv venv ``` - + + + ```bash + uv pip install agentstack + ``` + + - - Conda is not supported at this time. AgentStack uses Poetry in your project which [does not play well with Conda](https://github.com/AgentOps-AI/AgentStack/issues/40). - ## After installing Run `agentstack --version` to verify that the CLI is installed and accessible. -**Congrats! You now have AgentStack installed in your environment** \ No newline at end of file +**Congrats! You now have AgentStack installed in your environment** + + + + A quickstart guide to using the CLI + + \ No newline at end of file diff --git a/tox.ini b/tox.ini index b37cd4a..dd39747 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,7 @@ commands = coverage xml mypy: mypy agentstack setenv = - AGENTSTATCK_IS_TEST_ENV = 1 + AGENTSTACK_IS_TEST_ENV = 1 AGENTSTACK_TELEMETRY_OPT_OUT = 1 AGENTSTACK_UPDATE_DISABLE = 1