diff --git a/aider/commands.py b/aider/commands.py index b98e1f961..9c9f7d004 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -6,8 +6,8 @@ from pathlib import Path import git -from aider import models, prompts, voice -from aider.help import Help, PipInstallHF +from aider import models, prompts, utils, voice +from aider.help import Help from aider.llm import litellm from aider.scrape import Scraper from aider.utils import is_image_file @@ -662,19 +662,22 @@ class Commands: from aider.coders import Coder if not self.help: - try: - self.help = Help() - except PipInstallHF as err: - self.io.tool_error(str(err)) - if self.io.confirm_ask("Run pip install?", default="y"): - try: - self.help = Help(pip_install=True) - except PipInstallHF: - pass + pip_install_cmd = [ + "aider-chat[hf-embed]", + "--extra-index-url", + "https://download.pytorch.org/whl/cpu", + ] + res = utils.check_pip_install_extra( + self.io, + "llama_index.embeddings.huggingface", + "To use interactive /help you need to install HuggingFace embeddings", + pip_install_cmd, + ) + if not res: + self.io.tool_error("Unable to initialize interactive help.") + return - if not self.help: - self.io.tool_error("Unable to initialize interactive help.") - return + self.help = Help() coder = Coder.create( main_model=self.coder.main_model, diff --git a/aider/help.py b/aider/help.py index d93b411ff..41fba92a1 100755 --- a/aider/help.py +++ b/aider/help.py @@ -6,7 +6,7 @@ from pathlib import Path import importlib_resources -from aider import __version__, utils +from aider import __version__ from aider.dump import dump # noqa: F401 from aider.help_pats import exclude_website_pats @@ -87,35 +87,10 @@ def get_index(): return index -class PipInstallHF(Exception): - pass - - -pip_install_cmd = [ - "aider-chat[hf-embed]", - "--extra-index-url", - "https://download.pytorch.org/whl/cpu", -] - -pip_install_error = """ -To use interactive /help you need to install HuggingFace embeddings: - -{cmd} - -""" # noqa: E231 - - class Help: def __init__(self, pip_install=False): - cmd = utils.get_pip_install(pip_install_cmd) - if pip_install: - utils.run_install(cmd) - - try: - from llama_index.core import Settings - from llama_index.embeddings.huggingface import HuggingFaceEmbedding - except ImportError: - raise PipInstallHF(pip_install_error.format(cmd=' '.join(cmd))) + from llama_index.core import Settings + from llama_index.embeddings.huggingface import HuggingFaceEmbedding os.environ["TOKENIZERS_PARALLELISM"] = "true" Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5") diff --git a/aider/main.py b/aider/main.py index 8e52501f4..a8c9de437 100644 --- a/aider/main.py +++ b/aider/main.py @@ -148,6 +148,15 @@ def scrub_sensitive_info(args, text): return text +def check_streamlit_install(io): + return utils.check_pip_install_extra( + io, + "streamlit", + "You need to install the aider browser feature", + ["aider-chat[browser]"], + ) + + def launch_gui(args): from streamlit.web import cli @@ -318,10 +327,6 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F litellm.client_session = httpx.Client(verify=False) - if args.gui and not return_coder: - launch_gui(argv) - return - if args.dark_mode: args.user_input_color = "#32FF32" args.tool_error_color = "#FF3333" @@ -355,6 +360,12 @@ def main(argv=None, input=None, output=None, force_git_root=None, return_coder=F editingmode=editing_mode, ) + if args.gui and not return_coder: + if not check_streamlit_install(io): + return + launch_gui(argv) + return + for fname in loaded_dotenvs: io.tool_output(f"Loaded {fname}") diff --git a/aider/utils.py b/aider/utils.py index 3efdb84da..2d80c84f2 100644 --- a/aider/utils.py +++ b/aider/utils.py @@ -1,8 +1,8 @@ +import itertools import os import subprocess import sys import tempfile -import itertools from pathlib import Path import git @@ -182,7 +182,6 @@ def split_chat_history_markdown(text, include_tool=False): def get_pip_install(args): - cmd = [ sys.executable, "-m", @@ -192,14 +191,22 @@ def get_pip_install(args): cmd += args return cmd + def run_install(cmd): print() - print("Installing: ", ' '.join(cmd)) + print("Installing: ", " ".join(cmd)) try: - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1, universal_newlines=True) output = [] - spinner = itertools.cycle(['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']) + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + universal_newlines=True, + ) + spinner = itertools.cycle(["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]) for line in process.stdout: output.append(line) @@ -208,14 +215,45 @@ def run_install(cmd): return_code = process.wait() if return_code == 0: - print("\rInstallation completed successfully.") + print("\rInstallation complete.") print() - return True + return True, output except subprocess.CalledProcessError as e: print(f"\nError running pip install: {e}") print("\nInstallation failed.\n") + return False, output + + +def check_pip_install_extra(io, module, prompt, pip_install_cmd): + try: + __import__(module) + return True + except (ImportError, ModuleNotFoundError): + pass + + cmd = get_pip_install(pip_install_cmd) + + text = f"{prompt}:\n\n{' '.join(cmd)}\n\n" + io.tool_error(text) + + if not io.confirm_ask("Run pip install?", default="y"): + return + + success, output = run_install(cmd) + if not success: + return + + try: + __import__(module) + return True + except (ImportError, ModuleNotFoundError): + pass + for line in output: print(line) + + print() + print(f"Failed to install {pip_install_cmd[0]}") diff --git a/aider/versioncheck.py b/aider/versioncheck.py index c0133f582..20ee0b076 100644 --- a/aider/versioncheck.py +++ b/aider/versioncheck.py @@ -49,7 +49,8 @@ Newer aider version v{latest_version} is available. To upgrade, run: io.tool_error(text) if io.confirm_ask("Run pip install?"): - if utils.run_install(cmd): + success, _output = utils.run_install(cmd) + if success: io.tool_output("Re-run aider to use new version.") sys.exit() diff --git a/requirements.txt b/requirements.txt index 8176e9cf2..836b244c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,8 +8,6 @@ aiohttp==3.9.5 # via litellm aiosignal==1.3.1 # via aiohttp -altair==5.3.0 - # via streamlit annotated-types==0.7.0 # via pydantic anyio==4.4.0 @@ -25,12 +23,8 @@ backoff==2.2.1 # via -r requirements/requirements.in beautifulsoup4==4.12.3 # via -r requirements/requirements.in -blinker==1.8.2 - # via streamlit cachetools==5.3.3 - # via - # google-auth - # streamlit + # via google-auth certifi==2024.7.4 # via # httpcore @@ -43,9 +37,7 @@ cffi==1.16.0 charset-normalizer==3.3.2 # via requests click==8.1.7 - # via - # litellm - # streamlit + # via litellm configargparse==1.7 # via -r requirements/requirements.in diff-match-patch==20230430 @@ -67,9 +59,7 @@ fsspec==2024.6.1 gitdb==4.0.11 # via gitpython gitpython==3.1.43 - # via - # -r requirements/requirements.in - # streamlit + # via -r requirements/requirements.in google-ai-generativelanguage==0.6.6 # via google-generativeai google-api-core[grpc]==2.19.1 @@ -129,14 +119,10 @@ importlib-metadata==7.2.1 importlib-resources==6.4.0 # via -r requirements/requirements.in jinja2==3.1.4 - # via - # altair - # litellm - # pydeck + # via litellm jsonschema==4.22.0 # via # -r requirements/requirements.in - # altair # litellm jsonschema-specifications==2023.12.1 # via jsonschema @@ -159,32 +145,19 @@ networkx==3.2.1 numpy==1.26.4 # via # -r requirements/requirements.in - # altair - # pandas - # pyarrow - # pydeck # scipy - # streamlit openai==1.35.10 # via litellm packaging==24.1 # via # -r requirements/requirements.in - # altair # huggingface-hub - # streamlit -pandas==2.2.2 - # via - # altair - # streamlit pathspec==0.12.1 # via # -r requirements/requirements.in # grep-ast pillow==10.4.0 - # via - # -r requirements/requirements.in - # streamlit + # via -r requirements/requirements.in playwright==1.45.0 # via -r requirements/requirements.in prompt-toolkit==3.0.47 @@ -201,9 +174,6 @@ protobuf==4.25.3 # googleapis-common-protos # grpcio-status # proto-plus - # streamlit -pyarrow==16.1.0 - # via streamlit pyasn1==0.6.0 # via # pyasn1-modules @@ -221,8 +191,6 @@ pydantic==2.8.2 # openai pydantic-core==2.20.1 # via pydantic -pydeck==0.9.1 - # via streamlit pyee==11.1.0 # via playwright pyflakes==3.2.0 @@ -233,12 +201,8 @@ pypandoc==1.13 # via -r requirements/requirements.in pyparsing==3.1.2 # via httplib2 -python-dateutil==2.9.0.post0 - # via pandas python-dotenv==1.0.1 # via litellm -pytz==2024.1 - # via pandas pyyaml==6.0.1 # via # -r requirements/requirements.in @@ -254,12 +218,9 @@ requests==2.32.3 # google-api-core # huggingface-hub # litellm - # streamlit # tiktoken rich==13.7.1 - # via - # -r requirements/requirements.in - # streamlit + # via -r requirements/requirements.in rpds-py==0.18.1 # via # jsonschema @@ -268,8 +229,6 @@ rsa==4.9 # via google-auth scipy==1.13.1 # via -r requirements/requirements.in -six==1.16.0 - # via python-dateutil smmap==5.0.1 # via gitdb sniffio==1.3.1 @@ -283,20 +242,10 @@ soundfile==0.12.1 # via -r requirements/requirements.in soupsieve==2.5 # via beautifulsoup4 -streamlit==1.36.0 - # via -r requirements/requirements.in -tenacity==8.4.2 - # via streamlit tiktoken==0.7.0 # via litellm tokenizers==0.19.1 # via litellm -toml==0.10.2 - # via streamlit -toolz==0.12.1 - # via altair -tornado==6.4.1 - # via streamlit tqdm==4.66.4 # via # google-generativeai @@ -316,15 +265,10 @@ typing-extensions==4.12.2 # pydantic # pydantic-core # pyee - # streamlit -tzdata==2024.1 - # via pandas uritemplate==4.1.1 # via google-api-python-client urllib3==2.2.2 # via requests -watchdog==4.0.1 - # via -r requirements/requirements.in wcwidth==0.2.13 # via prompt-toolkit yarl==1.9.4 diff --git a/requirements/requirements-browser.in b/requirements/requirements-browser.in new file mode 100644 index 000000000..cf7811e0d --- /dev/null +++ b/requirements/requirements-browser.in @@ -0,0 +1,4 @@ +-c ../requirements.txt + +streamlit +watchdog diff --git a/requirements/requirements-browser.txt b/requirements/requirements-browser.txt new file mode 100644 index 000000000..d6d63717b --- /dev/null +++ b/requirements/requirements-browser.txt @@ -0,0 +1,151 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --output-file=requirements/requirements-browser.txt requirements/requirements-browser.in +# +altair==5.3.0 + # via streamlit +attrs==23.2.0 + # via + # -c requirements/../requirements.txt + # jsonschema + # referencing +blinker==1.8.2 + # via streamlit +cachetools==5.3.3 + # via + # -c requirements/../requirements.txt + # streamlit +certifi==2024.7.4 + # via + # -c requirements/../requirements.txt + # requests +charset-normalizer==3.3.2 + # via + # -c requirements/../requirements.txt + # requests +click==8.1.7 + # via + # -c requirements/../requirements.txt + # streamlit +gitdb==4.0.11 + # via + # -c requirements/../requirements.txt + # gitpython +gitpython==3.1.43 + # via + # -c requirements/../requirements.txt + # streamlit +idna==3.7 + # via + # -c requirements/../requirements.txt + # requests +jinja2==3.1.4 + # via + # -c requirements/../requirements.txt + # altair + # pydeck +jsonschema==4.22.0 + # via + # -c requirements/../requirements.txt + # altair +jsonschema-specifications==2023.12.1 + # via + # -c requirements/../requirements.txt + # jsonschema +markdown-it-py==3.0.0 + # via + # -c requirements/../requirements.txt + # rich +markupsafe==2.1.5 + # via + # -c requirements/../requirements.txt + # jinja2 +mdurl==0.1.2 + # via + # -c requirements/../requirements.txt + # markdown-it-py +numpy==1.26.4 + # via + # -c requirements/../requirements.txt + # altair + # pandas + # pyarrow + # pydeck + # streamlit +packaging==24.1 + # via + # -c requirements/../requirements.txt + # altair + # streamlit +pandas==2.2.2 + # via + # altair + # streamlit +pillow==10.4.0 + # via + # -c requirements/../requirements.txt + # streamlit +protobuf==4.25.3 + # via + # -c requirements/../requirements.txt + # streamlit +pyarrow==16.1.0 + # via streamlit +pydeck==0.9.1 + # via streamlit +pygments==2.18.0 + # via + # -c requirements/../requirements.txt + # rich +python-dateutil==2.9.0.post0 + # via pandas +pytz==2024.1 + # via pandas +referencing==0.35.1 + # via + # -c requirements/../requirements.txt + # jsonschema + # jsonschema-specifications +requests==2.32.3 + # via + # -c requirements/../requirements.txt + # streamlit +rich==13.7.1 + # via + # -c requirements/../requirements.txt + # streamlit +rpds-py==0.18.1 + # via + # -c requirements/../requirements.txt + # jsonschema + # referencing +six==1.16.0 + # via python-dateutil +smmap==5.0.1 + # via + # -c requirements/../requirements.txt + # gitdb +streamlit==1.36.0 + # via -r requirements/requirements-browser.in +tenacity==8.4.2 + # via streamlit +toml==0.10.2 + # via streamlit +toolz==0.12.1 + # via altair +tornado==6.4.1 + # via streamlit +typing-extensions==4.12.2 + # via + # -c requirements/../requirements.txt + # streamlit +tzdata==2024.1 + # via pandas +urllib3==2.2.2 + # via + # -c requirements/../requirements.txt + # requests +watchdog==4.0.1 + # via -r requirements/requirements-browser.in diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index 4c74677fb..96e927a91 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -99,9 +99,7 @@ packaging==24.1 # pytest # sphinx pandas==2.2.2 - # via - # -c requirements/../requirements.txt - # -r requirements/requirements-dev.in + # via -r requirements/requirements-dev.in pathos==0.3.2 # via lox pillow==10.4.0 @@ -137,13 +135,10 @@ pytest==8.2.2 # via -r requirements/requirements-dev.in python-dateutil==2.9.0.post0 # via - # -c requirements/../requirements.txt # matplotlib # pandas pytz==2024.1 - # via - # -c requirements/../requirements.txt - # pandas + # via pandas pyyaml==6.0.1 # via # -c requirements/../requirements.txt @@ -159,9 +154,7 @@ rich==13.7.1 shellingham==1.5.4 # via typer six==1.16.0 - # via - # -c requirements/../requirements.txt - # python-dateutil + # via python-dateutil snowballstemmer==2.2.0 # via sphinx sphinx==7.3.7 @@ -191,9 +184,7 @@ typing-extensions==4.12.2 # -c requirements/../requirements.txt # typer tzdata==2024.1 - # via - # -c requirements/../requirements.txt - # pandas + # via pandas urllib3==2.2.2 # via # -c requirements/../requirements.txt diff --git a/requirements/requirements-hf-embed.txt b/requirements/requirements-hf-embed.txt index 6911de143..f592ff8a1 100644 --- a/requirements/requirements-hf-embed.txt +++ b/requirements/requirements-hf-embed.txt @@ -161,9 +161,7 @@ packaging==24.1 # marshmallow # transformers pandas==2.2.2 - # via - # -c requirements/../requirements.txt - # llama-index-core + # via llama-index-core pillow==10.4.0 # via # -c requirements/../requirements.txt @@ -179,13 +177,9 @@ pydantic-core==2.20.1 # -c requirements/../requirements.txt # pydantic python-dateutil==2.9.0.post0 - # via - # -c requirements/../requirements.txt - # pandas + # via pandas pytz==2024.1 - # via - # -c requirements/../requirements.txt - # pandas + # via pandas pyyaml==6.0.1 # via # -c requirements/../requirements.txt @@ -217,9 +211,7 @@ scipy==1.13.1 sentence-transformers==3.0.1 # via llama-index-embeddings-huggingface six==1.16.0 - # via - # -c requirements/../requirements.txt - # python-dateutil + # via python-dateutil sniffio==1.3.1 # via # -c requirements/../requirements.txt @@ -233,9 +225,7 @@ sqlalchemy[asyncio]==2.0.31 sympy==1.13.0 # via torch tenacity==8.4.2 - # via - # -c requirements/../requirements.txt - # llama-index-core + # via llama-index-core threadpoolctl==3.5.0 # via scikit-learn tiktoken==0.7.0 @@ -275,9 +265,7 @@ typing-inspect==0.9.0 # dataclasses-json # llama-index-core tzdata==2024.1 - # via - # -c requirements/../requirements.txt - # pandas + # via pandas urllib3==2.2.2 # via # -c requirements/../requirements.txt diff --git a/requirements/requirements.in b/requirements/requirements.in index 0dc1f21e0..eef49032d 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -23,8 +23,6 @@ playwright pypandoc litellm google-generativeai -streamlit -watchdog flake8 importlib_resources diff --git a/scripts/pip-compile.sh b/scripts/pip-compile.sh index 3ee55def4..ffc0e9784 100755 --- a/scripts/pip-compile.sh +++ b/scripts/pip-compile.sh @@ -8,8 +8,8 @@ pip-compile \ --output-file=requirements.txt \ $1 -for SUFFIX in dev hf-embed ; do - echo suffix: ${SUFFIX} +for SUFFIX in dev hf-embed browser; do + pip-compile \ requirements/requirements-${SUFFIX}.in \ --output-file=requirements/requirements-${SUFFIX}.txt \ diff --git a/setup.py b/setup.py index 67e64263d..83dbf56d7 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ def get_requirements(suffix=""): requirements = get_requirements() dev_requirements = get_requirements("dev") hf_requirements = get_requirements("hf-embed") +browser_requirements = get_requirements("browser") # README with open("README.md", "r", encoding="utf-8") as f: @@ -47,6 +48,7 @@ setup( extras_require={ "dev": dev_requirements, "hf-embed": hf_requirements, + "browser": browser_requirements, }, python_requires=">=3.9,<3.13", entry_points={