Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/cli-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
python-version: "3.13"

- name: Configure Git
run: |
Expand All @@ -35,11 +35,11 @@ jobs:
- name: Bump version
run: |
# Make bump_version.py executable
chmod +x bump_version.py
chmod +x cli/bump_version.py

# Run the bump_version script with patch and commit flags
# Use echo to automatically confirm the prompt
echo "y" | python bump_version.py patch --commit
echo "y" | python cli/bump_version.py patch --commit

- name: Push changes
run: |
Expand Down
27 changes: 14 additions & 13 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
name: Publish to PyPI

on:
push:
tags:
- "v*"
workflow_dispatch:

permissions:
Expand All @@ -14,21 +11,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
python-version: "3.11"
version: "latest"

- name: Set up Python
run: uv python install 3.13

- name: Install dependencies
- name: Build package
run: |
python -m pip install --upgrade pip
pip install build twine
cd cli
uv build

- name: Build and publish
- name: Publish to PyPI
env:
TWINE_USERNAME: "__token__"
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m build
twine check dist/*
twine upload dist/*
cd cli
uv tool install twine
uv tool run twine check dist/*
uv tool run twine upload dist/*
46 changes: 32 additions & 14 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
name: Tests
name: CLI Tests

on:
push:
branches: [master]
paths: ["cli/**"]
pull_request:
branches: [master]
paths: ["cli/**"]

permissions:
contents: read
Expand All @@ -14,26 +16,42 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.13"]
python-version: ["3.13"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add at least one released Python version to the matrix.
Limiting the matrix to 3.13 removes test coverage on the versions your users actually run today (3.11/3.12). Keep 3.13 for bleeding-edge plus one LTS.

🤖 Prompt for AI Agents
In .github/workflows/test.yml at line 19, the Python version matrix currently
includes only version 3.13, which excludes testing on stable and widely used
versions like 3.11 and 3.12. Update the python-version array to include at least
one released version such as 3.11 or 3.12 alongside 3.13 to ensure broader test
coverage across commonly used Python versions.


steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
python-version: ${{ matrix.python-version }}
version: "latest"

- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}

- name: Install CLI package
run: |
cd cli
uv sync
uv pip install -e .

- name: Install dependencies
- name: Verify CLI installation
run: |
python -m pip install --upgrade pip
pip install build twine
pip install -e .
cd cli
uv run infragpt --help

- name: Verify installation
- name: Run tests (if they exist)
run: |
infragpt --help
cd cli
if [ -d "tests" ]; then
uv run pytest tests/ -v
else
echo "No tests directory found, skipping tests"
fi

- name: Check package structure
- name: Check package build
run: |
python -m build --sdist
twine check dist/*
cd cli
uv build
uv tool install twine
uv tool run twine check dist/*
File renamed without changes.
28 changes: 16 additions & 12 deletions pyproject.toml → cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "infragpt"
Expand All @@ -19,22 +19,26 @@ classifiers = [
"Topic :: System :: Systems Administration",
]

dynamic = ["dependencies"]
dependencies = [
"langchain-core>=0.3.68",
"langchain-openai>=0.3.27",
"langchain-anthropic>=0.3.17",
"rich>=14.0.0",
"prompt-toolkit>=3.0.51",
"click>=8.2.1",
"pyperclip>=1.9.0",
"pyyaml>=6.0.2",
]

[project.urls]
Homepage = "https://github.com/priyanshujain/infragpt"

[project.scripts]
infragpt = "cli.cli:cli"
infragpt = "infragpt.main:cli"

[tool.setuptools.packages.find]
include = ["cli", "llm"]

[tool.setuptools.dynamic]
dependencies = { file = "requirements.txt" }

[tool.uv.workspace]
members = [
"services/agent",
"services/backend/backendapi/client/python"
[dependency-groups]
dev = [
"pytest>=8.4.1",
]
File renamed without changes.
2 changes: 1 addition & 1 deletion cli/__main__.py → cli/src/infragpt/__main__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Command-line entry point for InfraGPT."""

from cli.cli import cli
from infragpt.main import cli

if __name__ == "__main__":
cli()
File renamed without changes.
4 changes: 2 additions & 2 deletions cli/config.py → cli/src/infragpt/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ def init_config():
CONFIG_DIR.mkdir(parents=True, exist_ok=True)

# Initialize history directory
from cli.history import init_history_dir
from infragpt.history import init_history_dir
init_history_dir()

config = {}

# Importing here to avoid circular imports
from cli.llm import validate_env_api_keys
from infragpt.llm import validate_env_api_keys

# Check for environment variables to populate initial config
openai_key = os.getenv("OPENAI_API_KEY")
Expand Down
File renamed without changes.
File renamed without changes.
10 changes: 5 additions & 5 deletions llm/__init__.py → cli/src/infragpt/llm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@
__version__ = "0.1.0"

# Public exports from client.py
from llm.client import (
from infragpt.llm.client import (
get_llm_client,
generate_gcloud_command,
get_parameter_info,
)

# Public exports from auth.py
from llm.auth import validate_api_key
from infragpt.llm.auth import validate_api_key

# Public exports from prompts.py
from llm.prompts import get_prompt_template, format_prompt
from infragpt.llm.prompts import get_prompt_template, format_prompt

# Public exports from models.py
from llm.models import MODEL_TYPE
from infragpt.llm.models import MODEL_TYPE

# Public exports from errors.py
from llm.errors import (
from infragpt.llm.errors import (
LLMError,
Comment on lines 12 to 30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add __all__ to make re-exports explicit and silence F401.

The wildcard re-exports are intentional, but Ruff flags them as unused.
Publish them cleanly and document the public surface by adding __all__.

 from infragpt.llm.client import (
     get_llm_client,
     generate_gcloud_command,
     get_parameter_info,
 )

 ...
 from infragpt.llm.errors import (
     LLMError,
     AuthenticationError,
     GenerationError,
     ParsingError,
     ValidationError,
     ConfigurationError,
 )

+# Public API of infragpt.llm
+__all__ = [
+    "get_llm_client",
+    "generate_gcloud_command",
+    "get_parameter_info",
+    "validate_api_key",
+    "get_prompt_template",
+    "format_prompt",
+    "MODEL_TYPE",
+    # errors
+    "LLMError",
+    "AuthenticationError",
+    "GenerationError",
+    "ParsingError",
+    "ValidationError",
+    "ConfigurationError",
+]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Public exports from client.py
from llm.client import (
from infragpt.llm.client import (
get_llm_client,
generate_gcloud_command,
get_parameter_info,
)
# Public exports from auth.py
from llm.auth import validate_api_key
from infragpt.llm.auth import validate_api_key
# Public exports from prompts.py
from llm.prompts import get_prompt_template, format_prompt
from infragpt.llm.prompts import get_prompt_template, format_prompt
# Public exports from models.py
from llm.models import MODEL_TYPE
from infragpt.llm.models import MODEL_TYPE
# Public exports from errors.py
from llm.errors import (
from infragpt.llm.errors import (
LLMError,
# Public exports from client.py
from infragpt.llm.client import (
get_llm_client,
generate_gcloud_command,
get_parameter_info,
)
# Public exports from auth.py
from infragpt.llm.auth import validate_api_key
# Public exports from prompts.py
from infragpt.llm.prompts import get_prompt_template, format_prompt
# Public exports from models.py
from infragpt.llm.models import MODEL_TYPE
# Public exports from errors.py
from infragpt.llm.errors import (
LLMError,
AuthenticationError,
GenerationError,
ParsingError,
ValidationError,
ConfigurationError,
)
# Public API of infragpt.llm
__all__ = [
"get_llm_client",
"generate_gcloud_command",
"get_parameter_info",
"validate_api_key",
"get_prompt_template",
"format_prompt",
"MODEL_TYPE",
# errors
"LLMError",
"AuthenticationError",
"GenerationError",
"ParsingError",
"ValidationError",
"ConfigurationError",
]
🧰 Tools
🪛 Ruff (0.11.9)

14-14: infragpt.llm.client.get_llm_client imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


15-15: infragpt.llm.client.generate_gcloud_command imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


16-16: infragpt.llm.client.get_parameter_info imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


20-20: infragpt.llm.auth.validate_api_key imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


23-23: infragpt.llm.prompts.get_prompt_template imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


23-23: infragpt.llm.prompts.format_prompt imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


26-26: infragpt.llm.models.MODEL_TYPE imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)


30-30: infragpt.llm.errors.LLMError imported but unused; consider removing, adding to __all__, or using a redundant alias

(F401)

🤖 Prompt for AI Agents
In cli/src/infragpt/llm/__init__.py around lines 12 to 30, the imported symbols
are re-exported but not explicitly declared, causing linter (Ruff) to flag them
as unused (F401). To fix this, define a __all__ list that includes all the
public symbols you want to export from this module. This will make the
re-exports explicit, document the public API surface, and silence the unused
import warnings.

AuthenticationError,
GenerationError,
Expand Down
4 changes: 2 additions & 2 deletions llm/auth.py → cli/src/infragpt/llm/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic

from llm.models import MODEL_TYPE
from llm.errors import AuthenticationError
from infragpt.llm.models import MODEL_TYPE
from infragpt.llm.errors import AuthenticationError
Comment on lines +11 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused AuthenticationError import to satisfy Ruff F401

AuthenticationError isn’t referenced anywhere in this module. Keeping it triggers the Ruff F401 warning and may cause CI to fail if warnings are treated as errors.

-from infragpt.llm.models import MODEL_TYPE
-from infragpt.llm.errors import AuthenticationError
+from infragpt.llm.models import MODEL_TYPE
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from infragpt.llm.models import MODEL_TYPE
from infragpt.llm.errors import AuthenticationError
from infragpt.llm.models import MODEL_TYPE
🧰 Tools
🪛 Ruff (0.11.9)

12-12: infragpt.llm.errors.AuthenticationError imported but unused

Remove unused import: infragpt.llm.errors.AuthenticationError

(F401)

🤖 Prompt for AI Agents
In cli/src/infragpt/llm/auth.py at lines 11 to 12, the import statement includes
AuthenticationError which is not used anywhere in the file. Remove the import of
AuthenticationError from the import line to resolve the Ruff F401 unused import
warning and prevent potential CI failures.



def validate_api_key(model_type: MODEL_TYPE, api_key: str) -> bool:
Expand Down
12 changes: 6 additions & 6 deletions llm/client.py → cli/src/infragpt/llm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

from llm.models import MODEL_TYPE, MODEL_MAP, DEFAULT_PARAMS
from llm.errors import AuthenticationError, GenerationError, ValidationError
from llm.auth import validate_api_key
from llm.prompts import get_prompt_template
from infragpt.llm.models import MODEL_TYPE, MODEL_MAP, DEFAULT_PARAMS
from infragpt.llm.errors import AuthenticationError, GenerationError, ValidationError
from infragpt.llm.auth import validate_api_key
from infragpt.llm.prompts import get_prompt_template


def get_llm_client(
Expand Down Expand Up @@ -195,9 +195,9 @@ def get_parameter_info(
# Re-raise authentication errors
raise
except json.JSONDecodeError as e:
from llm.errors import ParsingError
from infragpt.llm.errors import ParsingError
raise ParsingError(f"Failed to parse parameter info: {str(e)}") from e
except Exception as e:
# Wrap other errors
from llm.errors import ParsingError
from infragpt.llm.errors import ParsingError
raise ParsingError(f"Failed to get parameter info: {str(e)}") from e
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions cli/llm.py → cli/src/infragpt/llm_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
from rich.console import Console
from rich.prompt import Prompt

from cli.config import (
from infragpt.config import (
CONFIG_FILE, load_config, save_config
)
from cli.history import log_interaction
from infragpt.history import log_interaction

# Import from shared LLM module
from llm import (
# Import from local LLM module
from infragpt.llm import (
MODEL_TYPE,
validate_api_key as llm_validate_api_key,
get_llm_client,
Expand Down
11 changes: 6 additions & 5 deletions cli/cli.py → cli/src/infragpt/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
except ImportError:
pass

from cli.config import (
from infragpt.config import (
CONFIG_FILE, load_config, init_config, console
)
from cli.llm import (
MODEL_TYPE, generate_gcloud_command, prompt_credentials, validate_env_api_keys
from infragpt.llm.models import MODEL_TYPE
from infragpt.llm_adapter import (
generate_gcloud_command, validate_env_api_keys, prompt_credentials
)
from cli.prompts import handle_command_result
from cli.history import history_command
from infragpt.prompts import handle_command_result
from infragpt.history import history_command

def interactive_mode(model_type: Optional[MODEL_TYPE] = None, api_key: Optional[str] = None, verbose: bool = False):
"""Run InfraGPT in interactive mode with natural language prompting."""
Expand Down
11 changes: 6 additions & 5 deletions cli/prompts.py → cli/src/infragpt/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
from rich.prompt import Prompt, Confirm
from langchain_core.prompts import ChatPromptTemplate

from cli.config import CLIPBOARD_AVAILABLE
from cli.history import log_interaction
from cli.llm import MODEL_TYPE, get_parameter_info
from infragpt.config import CLIPBOARD_AVAILABLE
from infragpt.history import log_interaction
from infragpt.llm.models import MODEL_TYPE
from infragpt.llm_adapter import get_parameter_info

# Import from shared LLM module
from llm.prompts import get_prompt_template
# Import from local LLM module
from infragpt.llm.prompts import get_prompt_template

# Initialize console for rich output
console = Console()
Expand Down
Loading
Loading