From 780a0af132c61ad8169ffde6dfd8750beb0bc8db Mon Sep 17 00:00:00 2001 From: Adil Hafeez Date: Thu, 5 Mar 2026 13:50:50 -0800 Subject: [PATCH 1/2] Use editable install to fix sdist build failure (#805) --- .github/workflows/ci.yml | 2 +- Dockerfile | 2 +- cli/planoai/main.py | 6 ++-- cli/planoai/native_binaries.py | 25 ++++++++-------- cli/planoai/native_runner.py | 53 ++++++++++++++-------------------- 5 files changed, 39 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb4a92f..64637e18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,7 +163,7 @@ jobs: python-version: "3.14" - name: Install planoai - run: pip install ./cli + run: pip install -e ./cli - name: Validate plano config run: bash config/validate_plano_config.sh diff --git a/Dockerfile b/Dockerfile index 43bb5d0c..1e27f8ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,7 +72,7 @@ COPY cli/README.md ./ COPY config/plano_config_schema.yaml /config/plano_config_schema.yaml COPY config/envoy.template.yaml /config/envoy.template.yaml -RUN uv run pip install --no-cache-dir . +RUN pip install --no-cache-dir -e . COPY cli/planoai planoai/ COPY config/envoy.template.yaml . diff --git a/cli/planoai/main.py b/cli/planoai/main.py index 82d2039f..4d72a12e 100644 --- a/cli/planoai/main.py +++ b/cli/planoai/main.py @@ -179,7 +179,7 @@ def build(docker): cwd=crates_dir, check=True, ) - console.print("[green]✓[/green] WASM plugins built") + log.info("WASM plugins built") except subprocess.CalledProcessError as e: console.print(f"[red]✗[/red] WASM build failed: {e}") sys.exit(1) @@ -197,7 +197,7 @@ def build(docker): cwd=crates_dir, check=True, ) - console.print("[green]✓[/green] brightstaff built") + log.info("brightstaff built") except subprocess.CalledProcessError as e: console.print(f"[red]✗[/red] brightstaff build failed: {e}") sys.exit(1) @@ -319,7 +319,7 @@ def up(file, path, foreground, with_tracing, tracing_port, docker): console.print(f" [dim]{validation_stderr.strip()}[/dim]") sys.exit(1) - console.print(f"[green]✓[/green] Configuration valid") + log.info("Configuration valid") # Set up environment default_otel = ( diff --git a/cli/planoai/native_binaries.py b/cli/planoai/native_binaries.py index 6a255bdf..cbfd5f0f 100644 --- a/cli/planoai/native_binaries.py +++ b/cli/planoai/native_binaries.py @@ -100,15 +100,13 @@ def ensure_envoy_binary(): with open(version_path, "r") as f: cached_version = f.read().strip() if cached_version == ENVOY_VERSION: - log.info(f"Envoy {ENVOY_VERSION} found at {envoy_path}") + log.info(f"Envoy {ENVOY_VERSION} (cached)") return envoy_path - print( + log.info( f"Envoy version changed ({cached_version} → {ENVOY_VERSION}), re-downloading..." ) else: - log.info( - f"Envoy binary found at {envoy_path} (unknown version, re-downloading...)" - ) + log.info("Envoy binary found (unknown version, re-downloading...)") slug = _get_platform_slug() url = ( @@ -123,6 +121,7 @@ def ensure_envoy_binary(): try: _download_file(url, tmp_path, label=f"Envoy {ENVOY_VERSION}") + log.info(f"Extracting Envoy {ENVOY_VERSION}...") with tarfile.open(tmp_path, "r:xz") as tar: # Find the envoy binary inside the archive envoy_member = None @@ -150,7 +149,6 @@ def ensure_envoy_binary(): os.chmod(envoy_path, 0o755) with open(version_path, "w") as f: f.write(ENVOY_VERSION) - log.info(f"Envoy {ENVOY_VERSION} installed at {envoy_path}") return envoy_path finally: @@ -187,7 +185,7 @@ def ensure_wasm_plugins(): # 1. Local source build (inside repo) local = _find_local_wasm_plugins() if local: - log.info(f"Using locally-built WASM plugins: {local[0]}") + log.info("Using locally-built WASM plugins") return local # 2. Cached download @@ -201,9 +199,9 @@ def ensure_wasm_plugins(): with open(version_path, "r") as f: cached_version = f.read().strip() if cached_version == version: - log.info(f"WASM plugins {version} found at {PLANO_PLUGINS_DIR}") + log.info(f"WASM plugins {version} (cached)") return prompt_gw_path, llm_gw_path - print( + log.info( f"WASM plugins version changed ({cached_version} → {version}), re-downloading..." ) else: @@ -220,6 +218,7 @@ def ensure_wasm_plugins(): url = f"{PLANO_RELEASE_BASE_URL}/{version}/{gz_name}" gz_dest = dest + ".gz" _download_file(url, gz_dest, label=f"{name} ({version})") + log.info(f"Decompressing {name}...") with gzip.open(gz_dest, "rb") as f_in, open(dest, "wb") as f_out: shutil.copyfileobj(f_in, f_out) os.unlink(gz_dest) @@ -235,7 +234,7 @@ def ensure_brightstaff_binary(): # 1. Local source build (inside repo) local = _find_local_brightstaff() if local: - log.info(f"Using locally-built brightstaff: {local}") + log.info("Using locally-built brightstaff") return local # 2. Cached download @@ -248,9 +247,9 @@ def ensure_brightstaff_binary(): with open(version_path, "r") as f: cached_version = f.read().strip() if cached_version == version: - log.info(f"brightstaff {version} found at {brightstaff_path}") + log.info(f"brightstaff {version} (cached)") return brightstaff_path - print( + log.info( f"brightstaff version changed ({cached_version} → {version}), re-downloading..." ) else: @@ -265,6 +264,7 @@ def ensure_brightstaff_binary(): gz_path = brightstaff_path + ".gz" _download_file(url, gz_path, label=f"brightstaff ({version}, {slug})") + log.info("Decompressing brightstaff...") with gzip.open(gz_path, "rb") as f_in, open(brightstaff_path, "wb") as f_out: shutil.copyfileobj(f_in, f_out) os.unlink(gz_path) @@ -272,7 +272,6 @@ def ensure_brightstaff_binary(): os.chmod(brightstaff_path, 0o755) with open(version_path, "w") as f: f.write(version) - log.info(f"brightstaff {version} installed at {brightstaff_path}") return brightstaff_path diff --git a/cli/planoai/native_runner.py b/cli/planoai/native_runner.py index 8331d698..c5ccd6e7 100644 --- a/cli/planoai/native_runner.py +++ b/cli/planoai/native_runner.py @@ -161,19 +161,10 @@ def start_native(plano_config_file, env, foreground=False, with_tracing=False): """Start Envoy and brightstaff natively.""" from planoai.core import _get_gateway_ports - console = None - try: - from rich.console import Console - - console = Console() - except ImportError: - pass - - def status_print(msg): - if console: - console.print(msg) - else: - print(msg) + # Stop any existing instance first + if os.path.exists(NATIVE_PID_FILE): + log.info("Stopping existing Plano instance...") + stop_native() envoy_path = ensure_envoy_binary() ensure_wasm_plugins() @@ -182,7 +173,7 @@ def start_native(plano_config_file, env, foreground=False, with_tracing=False): plano_config_file, env, with_tracing=with_tracing ) - status_print(f"[green]✓[/green] Configuration rendered") + log.info("Configuration rendered") log_dir = os.path.join(PLANO_RUN_DIR, "logs") os.makedirs(log_dir, exist_ok=True) @@ -233,7 +224,7 @@ def start_native(plano_config_file, env, foreground=False, with_tracing=False): # Health check gateway_ports = _get_gateway_ports(plano_config_file) - status_print(f"[dim]Waiting for listeners to become healthy...[/dim]") + log.info("Waiting for listeners to become healthy...") start_time = time.time() timeout = 60 @@ -244,35 +235,35 @@ def start_native(plano_config_file, env, foreground=False, with_tracing=False): all_healthy = False if all_healthy: - status_print(f"[green]✓[/green] Plano is running (native mode)") + log.info("Plano is running (native mode)") for port in gateway_ports: - status_print(f" [cyan]http://localhost:{port}[/cyan]") + log.info(f" http://localhost:{port}") break # Check if processes are still alive if not _is_pid_alive(brightstaff_pid): - status_print("[red]✗[/red] brightstaff exited unexpectedly") - status_print(f" Check logs: {os.path.join(log_dir, 'brightstaff.log')}") + log.error("brightstaff exited unexpectedly") + log.error(f" Check logs: {os.path.join(log_dir, 'brightstaff.log')}") _kill_pid(envoy_pid) sys.exit(1) if not _is_pid_alive(envoy_pid): - status_print("[red]✗[/red] envoy exited unexpectedly") - status_print(f" Check logs: {os.path.join(log_dir, 'envoy.log')}") + log.error("envoy exited unexpectedly") + log.error(f" Check logs: {os.path.join(log_dir, 'envoy.log')}") _kill_pid(brightstaff_pid) sys.exit(1) if time.time() - start_time > timeout: - status_print(f"[red]✗[/red] Health check timed out after {timeout}s") - status_print(f" Check logs in: {log_dir}") + log.error(f"Health check timed out after {timeout}s") + log.error(f" Check logs in: {log_dir}") stop_native() sys.exit(1) time.sleep(1) if foreground: - status_print(f"[dim]Running in foreground. Press Ctrl+C to stop.[/dim]") - status_print(f"[dim]Logs: {log_dir}[/dim]") + log.info("Running in foreground. Press Ctrl+C to stop.") + log.info(f"Logs: {log_dir}") try: import glob @@ -290,13 +281,13 @@ def start_native(plano_config_file, env, foreground=False, with_tracing=False): ) tail_proc.wait() except KeyboardInterrupt: - status_print(f"\n[dim]Stopping Plano...[/dim]") + log.info("Stopping Plano...") if tail_proc.poll() is None: tail_proc.terminate() stop_native() else: - status_print(f"[dim]Logs: {log_dir}[/dim]") - status_print(f"[dim]Run 'planoai down' to stop.[/dim]") + log.info(f"Logs: {log_dir}") + log.info("Run 'planoai down' to stop.") def _daemon_exec(args, env, log_path): @@ -364,7 +355,7 @@ def _kill_pid(pid): def stop_native(): """Stop natively-running Envoy and brightstaff processes.""" if not os.path.exists(NATIVE_PID_FILE): - print("No native Plano instance found (PID file missing).") + log.info("No native Plano instance found (PID file missing).") return with open(NATIVE_PID_FILE, "r") as f: @@ -383,7 +374,7 @@ def stop_native(): log.info(f"{name} (PID {pid}) already stopped") continue except PermissionError: - log.info(f"Permission denied stopping {name} (PID {pid})") + log.error(f"Permission denied stopping {name} (PID {pid})") continue # Wait for graceful shutdown @@ -403,7 +394,7 @@ def stop_native(): pass os.unlink(NATIVE_PID_FILE) - print("Plano stopped (native mode).") + log.info("Plano stopped (native mode).") def native_validate_config(plano_config_file): From 065328e11cb2132aec766e84a365a547b8201358 Mon Sep 17 00:00:00 2001 From: Adil Hafeez Date: Thu, 5 Mar 2026 13:58:19 -0800 Subject: [PATCH 2/2] release 0.4.11 (#806) --- .github/workflows/ci.yml | 4 ++-- apps/www/src/components/Hero.tsx | 2 +- build_filter_image.sh | 2 +- cli/planoai/__init__.py | 2 +- cli/planoai/consts.py | 2 +- cli/pyproject.toml | 2 +- docs/source/conf.py | 2 +- docs/source/get_started/quickstart.rst | 4 ++-- docs/source/resources/deployment.rst | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64637e18..25e6f99d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,13 +133,13 @@ jobs: load: true tags: | ${{ env.PLANO_DOCKER_IMAGE }} - ${{ env.DOCKER_IMAGE }}:0.4.10 + ${{ env.DOCKER_IMAGE }}:0.4.11 ${{ env.DOCKER_IMAGE }}:latest cache-from: type=gha cache-to: type=gha,mode=max - name: Save image as artifact - run: docker save ${{ env.PLANO_DOCKER_IMAGE }} ${{ env.DOCKER_IMAGE }}:0.4.10 ${{ env.DOCKER_IMAGE }}:latest -o /tmp/plano-image.tar + run: docker save ${{ env.PLANO_DOCKER_IMAGE }} ${{ env.DOCKER_IMAGE }}:0.4.11 ${{ env.DOCKER_IMAGE }}:latest -o /tmp/plano-image.tar - name: Upload image artifact uses: actions/upload-artifact@v6 diff --git a/apps/www/src/components/Hero.tsx b/apps/www/src/components/Hero.tsx index 66f07c1b..7952c68f 100644 --- a/apps/www/src/components/Hero.tsx +++ b/apps/www/src/components/Hero.tsx @@ -24,7 +24,7 @@ export function Hero() { >
- v0.4.10 + v0.4.11 — diff --git a/build_filter_image.sh b/build_filter_image.sh index a4f3acea..8e041894 100644 --- a/build_filter_image.sh +++ b/build_filter_image.sh @@ -1 +1 @@ -docker build -f Dockerfile . -t katanemo/plano -t katanemo/plano:0.4.10 +docker build -f Dockerfile . -t katanemo/plano -t katanemo/plano:0.4.11 diff --git a/cli/planoai/__init__.py b/cli/planoai/__init__.py index 895323da..b94eadc2 100644 --- a/cli/planoai/__init__.py +++ b/cli/planoai/__init__.py @@ -1,3 +1,3 @@ """Plano CLI - Intelligent Prompt Gateway.""" -__version__ = "0.4.10" +__version__ = "0.4.11" diff --git a/cli/planoai/consts.py b/cli/planoai/consts.py index 11caa70b..145fb640 100644 --- a/cli/planoai/consts.py +++ b/cli/planoai/consts.py @@ -5,7 +5,7 @@ PLANO_COLOR = "#969FF4" SERVICE_NAME_ARCHGW = "plano" PLANO_DOCKER_NAME = "plano" -PLANO_DOCKER_IMAGE = os.getenv("PLANO_DOCKER_IMAGE", "katanemo/plano:0.4.10") +PLANO_DOCKER_IMAGE = os.getenv("PLANO_DOCKER_IMAGE", "katanemo/plano:0.4.11") DEFAULT_OTEL_TRACING_GRPC_ENDPOINT = "http://localhost:4317" # Native mode constants diff --git a/cli/pyproject.toml b/cli/pyproject.toml index f92d557e..3f9be272 100644 --- a/cli/pyproject.toml +++ b/cli/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "planoai" -version = "0.4.10" +version = "0.4.11" description = "Python-based CLI tool to manage Plano." authors = [{name = "Katanemo Labs, Inc."}] readme = "README.md" diff --git a/docs/source/conf.py b/docs/source/conf.py index db16f928..ec476136 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -17,7 +17,7 @@ from sphinxawesome_theme.postprocess import Icons project = "Plano Docs" copyright = "2025, Katanemo Labs, Inc" author = "Katanemo Labs, Inc" -release = " v0.4.10" +release = " v0.4.11" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/source/get_started/quickstart.rst b/docs/source/get_started/quickstart.rst index f87d05ac..279fde2d 100644 --- a/docs/source/get_started/quickstart.rst +++ b/docs/source/get_started/quickstart.rst @@ -43,7 +43,7 @@ Plano's CLI allows you to manage and interact with the Plano efficiently. To ins .. code-block:: console - $ uv tool install planoai==0.4.10 + $ uv tool install planoai==0.4.11 **Option 2: Install with pip (Traditional)** @@ -51,7 +51,7 @@ Plano's CLI allows you to manage and interact with the Plano efficiently. To ins $ python -m venv venv $ source venv/bin/activate # On Windows, use: venv\Scripts\activate - $ pip install planoai==0.4.10 + $ pip install planoai==0.4.11 .. _llm_routing_quickstart: diff --git a/docs/source/resources/deployment.rst b/docs/source/resources/deployment.rst index 60c02910..71452ea3 100644 --- a/docs/source/resources/deployment.rst +++ b/docs/source/resources/deployment.rst @@ -65,7 +65,7 @@ Create a ``docker-compose.yml`` file with the following configuration: # docker-compose.yml services: plano: - image: katanemo/plano:0.4.10 + image: katanemo/plano:0.4.11 container_name: plano ports: - "10000:10000" # ingress (client -> plano)