From 30932d27add2d4e8b1c8d9de2e2fa27bab1237c2 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Singh <85818124+rohankrsingh@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:52:34 +0530 Subject: [PATCH 1/2] chore(release): add release notes for v0.1.6 --- .github/release_notes/v0.1.6.md | 29 +++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .github/release_notes/v0.1.6.md diff --git a/.github/release_notes/v0.1.6.md b/.github/release_notes/v0.1.6.md new file mode 100644 index 0000000..7581054 --- /dev/null +++ b/.github/release_notes/v0.1.6.md @@ -0,0 +1,29 @@ +## v0.1.6 — Fix venv activation persistence & add Gemini retry/backoff + +### Summary +This release fixes two user-facing issues discovered when running the CLI on Arch Linux: + +- Ensure suggested environment steps (like `python -m venv .venv` + `source .venv/bin/activate`) are executed in the same shell so activation persists and subsequent commands (e.g., `pip install`) run inside the created venv instead of the system Python. +- Add a small retry + exponential backoff around the Google Gemini client call to handle transient 503 “model overloaded” errors before falling back to the REST path. + +These changes reduce the PEP 668 (“externally-managed-environment”) errors on Arch and make the AI backend more resilient to temporary model overloads. + +### Notable changes +- `src/ai_pkg/cli.py` — run all `env_steps` together in one bash shell and add failure handling. +- `src/ai_pkg/backends.py` — retry transient 503 server errors with exponential backoff before falling back to REST. + +### Upgrade / usage notes +- Prefer running inside a virtual environment and install in editable mode for development: + ```bash + python -m venv .venv + source .venv/bin/activate + pip install -e . + ``` +- Ensure `GEMINI_API_KEY` is set or pass `--api-key` to the CLI. + +### Files changed +- `src/ai_pkg/cli.py` +- `src/ai_pkg/backends.py` + +--- +Generated release notes by the maintainer tooling. diff --git a/README.md b/README.md index 7d571ce..14ba4ca 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ AI-PKG Banner

-

🌟 ai-pkg

+

🌟 AIPkg

Your AI-Powered Development Environment Wizard for Arch Linux From 664b264aa64888c53db13757bfa95a83a267530d Mon Sep 17 00:00:00 2001 From: Rohan Kumar Singh <85818124+rohankrsingh@users.noreply.github.com> Date: Wed, 19 Nov 2025 17:02:03 +0530 Subject: [PATCH 2/2] fix error 503 an change default terminal to bash Fix: Run all env_steps in a single bash session so source .venv/bin/activate affects subsequent commands. File: cli.py Behavior: Previously each step was run in its own subprocess which caused activation to be lost; now we join steps with && and run them under bash. The CLI exits with a non-zero code if the combined env steps fail. Fix: Retry transient server errors from the google-genai client (503) with exponential backoff. File: backends.py Behavior: Retry up to 3 attempts (with backoff) before falling back to the existing REST path. This reduces noisy failures when the model endpoint is temporarily overloaded. --- README.md | 2 +- install.sh | 48 ++++++++++++++++++++++++++++++++---------- src/ai_pkg/backends.py | 18 ++++++++++++++-- src/ai_pkg/cli.py | 18 ++++++++++++---- 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 14ba4ca..cc0c558 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ AI-PKG Banner

-

🌟 AIPkg

+

ai-pkg

Your AI-Powered Development Environment Wizard for Arch Linux diff --git a/install.sh b/install.sh index ea69032..d556028 100755 --- a/install.sh +++ b/install.sh @@ -10,20 +10,12 @@ if ! command -v pacman &>/dev/null; then fi echo "Choose installation method:" -echo "1) pipx (PyPI)" -echo "2) yay (AUR binary)" +echo "1) yay (AUR binary)" +echo "2) Local virtualenv (installs a venv under ~/.local/ai-pkg and a shim in ~/.local/bin)" read -rp "Enter 1 or 2: " choice if [[ "$choice" == "1" ]]; then - if ! command -v pipx &>/dev/null; then - echo "⚙️ Installing pipx..." - sudo pacman -S --noconfirm python-pipx - pipx ensurepath || true - fi - echo "⚙️ Installing via pipx..." - pipx install ai-pkg || pipx upgrade ai-pkg || echo "pipx install failed" - echo "✅ Installed ai-pkg via pipx" -elif [[ "$choice" == "2" ]]; then + # AUR install via yay if ! command -v yay &>/dev/null; then echo "⚙️ Installing yay (AUR helper)..." sudo pacman -S --needed --noconfirm git base-devel @@ -34,6 +26,40 @@ elif [[ "$choice" == "2" ]]; then echo "⚙️ Installing ai-pkg-bin via yay..." yay -S --noconfirm ai-pkg-bin || echo "AUR install failed" echo "✅ Installed ai-pkg-bin from AUR" +elif [[ "$choice" == "2" ]]; then + # Local venv install + echo "⚙️ Installing into a local virtualenv..." + + repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + venv_dir="$HOME/.local/ai-pkg/.venv" + bin_shim="$HOME/.local/bin/ai-pkg" + + mkdir -p "$(dirname "$bin_shim")" + mkdir -p "$(dirname "$venv_dir")" + + echo "Creating virtualenv at $venv_dir" + python -m venv "$venv_dir" + + echo "Installing package in editable mode into the venv" + # Use the venv's pip to install the package from the repository root + "$venv_dir/bin/python" -m pip install --upgrade pip + "$venv_dir/bin/python" -m pip install -e "$repo_root" || { + echo "❌ Failed to install package into venv" + exit 1 + } + + # Create a small shim so `ai-pkg` is available on PATH via ~/.local/bin + cat > "$bin_shim" </dev/null; then + echo "Note: ensure ~/.local/bin is in your PATH to run 'ai-pkg' directly." + fi else echo "❌ Invalid choice. Exiting." exit 1 diff --git a/src/ai_pkg/backends.py b/src/ai_pkg/backends.py index 0935362..c3d735b 100644 --- a/src/ai_pkg/backends.py +++ b/src/ai_pkg/backends.py @@ -1,5 +1,6 @@ import json import logging +import time import requests from typing import Any @@ -62,14 +63,27 @@ def suggest_with_gemini(goal: str, api_key: str): "IMPORTANT: Respond with ONLY valid JSON (no markdown, no surrounding backticks). The JSON must be either an object with keys 'packages' and 'env_steps', or simply an array of package names (legacy).\n" f"Task: Provide environment steps and packages needed to {goal}\n" "Prefer pacman package names and keep commands idempotent. When suggesting pacman install commands, prefer using '--needed' so packages already installed are skipped.\n" - "Example object response: {\"env_steps\": [\"python -m venv .venv\", \". .venv/bin/activate\"], \"packages\": [\"python\", \"git\"] }\n" + "Example object response: {\"env_steps\": [\"python -m venv .venv\", \"source .venv/bin/activate\"], \"packages\": [\"python\", \"git\"] }\n" ) # Prefer using google-genai client if genai is not None: try: client = genai.Client() - response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt_text) + # Retry a few times for transient server-side overloads (503). + max_attempts = 3 + response = None + for attempt in range(1, max_attempts + 1): + try: + response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt_text) + break + except Exception as e: + # If this was the last attempt, re-raise to be caught by outer except + if attempt == max_attempts: + raise + logger.warning('Gemini client transient error (attempt %d/%d): %s', attempt, max_attempts, e) + # exponential backoff + time.sleep(2 ** (attempt - 1)) # Attempt to extract text safely from likely response shapes text = None try: diff --git a/src/ai_pkg/cli.py b/src/ai_pkg/cli.py index 85eca78..abee768 100644 --- a/src/ai_pkg/cli.py +++ b/src/ai_pkg/cli.py @@ -62,10 +62,20 @@ def main( if not should_run_env: # Ask the user interactively should_run_env = typer.confirm("Run the environment setup steps now?") - if should_run_env: - for cmd in env_steps: - typer.secho(f"Running: {cmd}", fg=typer.colors.YELLOW) - subprocess.run(cmd, shell=True) + if should_run_env: + # Run environment setup steps in a single shell session so that + # activation (source / .) affects subsequent commands. When we + # previously ran each command in its own subprocess, the venv + # activation didn't persist and later `pip install` used the + # system pip (triggering PEP 668 'externally-managed-environment'). + # Combining with && preserves state across the sequence. + combined = " && ".join(env_steps) + typer.secho(f"Running: {combined}", fg=typer.colors.YELLOW) + # Use bash so `source` and `.` are available as builtins. + result = subprocess.run(combined, shell=True, executable="/bin/bash") + if result.returncode != 0: + typer.secho("❌ Environment setup steps failed.", fg=typer.colors.RED) + raise typer.Exit(result.returncode) if dry_run: core.install_packages(pkgs, dry_run=True, auto_yes=yes, aur_helper=aur_helper_final)