gac 1.5.1__tar.gz → 1.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gac might be problematic. Click here for more details.

Files changed (35) hide show
  1. {gac-1.5.1 → gac-1.5.2}/PKG-INFO +5 -5
  2. {gac-1.5.1 → gac-1.5.2}/README.md +4 -4
  3. {gac-1.5.1 → gac-1.5.2}/pyproject.toml +2 -2
  4. gac-1.5.2/src/gac/__init__.py +15 -0
  5. {gac-1.5.1 → gac-1.5.2}/src/gac/__version__.py +1 -1
  6. {gac-1.5.1 → gac-1.5.2}/src/gac/ai.py +2 -0
  7. {gac-1.5.1 → gac-1.5.2}/src/gac/ai_utils.py +1 -0
  8. gac-1.5.2/src/gac/init_cli.py +116 -0
  9. {gac-1.5.1 → gac-1.5.2}/src/gac/main.py +1 -1
  10. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/__init__.py +2 -0
  11. gac-1.5.2/src/gac/providers/streamlake.py +47 -0
  12. gac-1.5.1/src/gac/__init__.py +0 -27
  13. gac-1.5.1/src/gac/init_cli.py +0 -53
  14. {gac-1.5.1 → gac-1.5.2}/.gitignore +0 -0
  15. {gac-1.5.1 → gac-1.5.2}/LICENSE +0 -0
  16. {gac-1.5.1 → gac-1.5.2}/src/gac/cli.py +0 -0
  17. {gac-1.5.1 → gac-1.5.2}/src/gac/config.py +0 -0
  18. {gac-1.5.1 → gac-1.5.2}/src/gac/config_cli.py +0 -0
  19. {gac-1.5.1 → gac-1.5.2}/src/gac/constants.py +0 -0
  20. {gac-1.5.1 → gac-1.5.2}/src/gac/diff_cli.py +0 -0
  21. {gac-1.5.1 → gac-1.5.2}/src/gac/errors.py +0 -0
  22. {gac-1.5.1 → gac-1.5.2}/src/gac/git.py +0 -0
  23. {gac-1.5.1 → gac-1.5.2}/src/gac/preprocess.py +0 -0
  24. {gac-1.5.1 → gac-1.5.2}/src/gac/prompt.py +0 -0
  25. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/anthropic.py +0 -0
  26. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/cerebras.py +0 -0
  27. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/gemini.py +0 -0
  28. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/groq.py +0 -0
  29. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/lmstudio.py +0 -0
  30. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/ollama.py +0 -0
  31. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/openai.py +0 -0
  32. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/openrouter.py +0 -0
  33. {gac-1.5.1 → gac-1.5.2}/src/gac/providers/zai.py +0 -0
  34. {gac-1.5.1 → gac-1.5.2}/src/gac/security.py +0 -0
  35. {gac-1.5.1 → gac-1.5.2}/src/gac/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gac
3
- Version: 1.5.1
3
+ Version: 1.5.2
4
4
  Summary: AI-powered Git commit message generator with multi-provider support
5
5
  Project-URL: Homepage, https://github.com/cellwebb/gac
6
6
  Project-URL: Documentation, https://github.com/cellwebb/gac#readme
@@ -56,7 +56,7 @@ Description-Content-Type: text/markdown
56
56
 
57
57
  - **LLM-Powered Commit Messages:** Automatically generates clear, concise, and context-aware commit messages using large language models.
58
58
  - **Deep Contextual Analysis:** Understands your code by analyzing staged changes, repository structure, and recent commit history to provide highly relevant suggestions.
59
- - **Multi-Provider & Model Support:** Flexibly works with leading AI providers (Anthropic, Cerebras, Groq, Ollama, OpenAI, OpenRouter, Z.AI) and models, easily configured through an interactive setup or environment variables.
59
+ - **Multi-Provider & Model Support:** Flexibly works with leading AI providers (Anthropic, Cerebras, Gemini, Groq, OpenAI, OpenRouter, Streamlake/Vanchin, Z.AI) and local providers (LM Studio, Ollama), easily configured through an interactive setup or environment variables.
60
60
  - **Seamless Git Workflow:** Integrates smoothly into your existing Git routine as a simple drop-in replacement for `git commit`.
61
61
  - **Extensive Customization:** Tailor commit messages to your needs with a rich set of flags, including one-liners (`-o`), AI hints (`-h`), scope inference (`-s`), and specific model selection (`-m`).
62
62
  - **Streamlined Workflow Commands:** Boost your productivity with convenient options to stage all changes (`-a`), auto-confirm commits (`-y`), and push to your remote repository (`-p`) in a single step.
@@ -137,9 +137,6 @@ Example `$HOME/.gac.env` output:
137
137
  ```env
138
138
  GAC_MODEL=anthropic:claude-3-5-haiku-latest
139
139
  ANTHROPIC_API_KEY=your_anthropic_key_here
140
- # Optional: configure OpenRouter
141
- # GAC_MODEL=openrouter:openrouter/auto
142
- # OPENROUTER_API_KEY=your_openrouter_key_here
143
140
  ```
144
141
 
145
142
  Alternatively, you can configure `gac` using environment variables or by manually creating/editing the configuration file.
@@ -148,6 +145,9 @@ Alternatively, you can configure `gac` using environment variables or by manuall
148
145
 
149
146
  You can manage settings in your `$HOME/.gac.env` file using `gac config` commands:
150
147
 
148
+ - Streamlake uses inference endpoint IDs instead of model names. When prompted, paste the exact endpoint ID from the Streamlake console.
149
+ - For local providers like Ollama and LM Studio, gac will ask for the base API URL. API keys are optional for these providers unless your instance requires authentication.
150
+
151
151
  - Show config: `gac config show`
152
152
  - Set a value: `gac config set GAC_MODEL groq:meta-llama/llama-4-scout-17b-16e-instruct`
153
153
  - Get a value: `gac config get GAC_MODEL`
@@ -14,7 +14,7 @@
14
14
 
15
15
  - **LLM-Powered Commit Messages:** Automatically generates clear, concise, and context-aware commit messages using large language models.
16
16
  - **Deep Contextual Analysis:** Understands your code by analyzing staged changes, repository structure, and recent commit history to provide highly relevant suggestions.
17
- - **Multi-Provider & Model Support:** Flexibly works with leading AI providers (Anthropic, Cerebras, Groq, Ollama, OpenAI, OpenRouter, Z.AI) and models, easily configured through an interactive setup or environment variables.
17
+ - **Multi-Provider & Model Support:** Flexibly works with leading AI providers (Anthropic, Cerebras, Gemini, Groq, OpenAI, OpenRouter, Streamlake/Vanchin, Z.AI) and local providers (LM Studio, Ollama), easily configured through an interactive setup or environment variables.
18
18
  - **Seamless Git Workflow:** Integrates smoothly into your existing Git routine as a simple drop-in replacement for `git commit`.
19
19
  - **Extensive Customization:** Tailor commit messages to your needs with a rich set of flags, including one-liners (`-o`), AI hints (`-h`), scope inference (`-s`), and specific model selection (`-m`).
20
20
  - **Streamlined Workflow Commands:** Boost your productivity with convenient options to stage all changes (`-a`), auto-confirm commits (`-y`), and push to your remote repository (`-p`) in a single step.
@@ -95,9 +95,6 @@ Example `$HOME/.gac.env` output:
95
95
  ```env
96
96
  GAC_MODEL=anthropic:claude-3-5-haiku-latest
97
97
  ANTHROPIC_API_KEY=your_anthropic_key_here
98
- # Optional: configure OpenRouter
99
- # GAC_MODEL=openrouter:openrouter/auto
100
- # OPENROUTER_API_KEY=your_openrouter_key_here
101
98
  ```
102
99
 
103
100
  Alternatively, you can configure `gac` using environment variables or by manually creating/editing the configuration file.
@@ -106,6 +103,9 @@ Alternatively, you can configure `gac` using environment variables or by manuall
106
103
 
107
104
  You can manage settings in your `$HOME/.gac.env` file using `gac config` commands:
108
105
 
106
+ - Streamlake uses inference endpoint IDs instead of model names. When prompted, paste the exact endpoint ID from the Streamlake console.
107
+ - For local providers like Ollama and LM Studio, gac will ask for the base API URL. API keys are optional for these providers unless your instance requires authentication.
108
+
109
109
  - Show config: `gac config show`
110
110
  - Set a value: `gac config set GAC_MODEL groq:meta-llama/llama-4-scout-17b-16e-instruct`
111
111
  - Get a value: `gac config get GAC_MODEL`
@@ -195,9 +195,9 @@ line-ending = "auto"
195
195
 
196
196
  [tool.pytest.ini_options]
197
197
  markers = [
198
- "providers: marks tests that make real API calls to AI providers (deselect with '-m \"not providers\"')",
198
+ "integration: marks tests that make real API calls to external services (deselect with '-m \"not integration\"')",
199
199
  ]
200
- addopts = "-m 'not providers'"
200
+ addopts = "-m 'not integration'"
201
201
 
202
202
  [template.plugins.default]
203
203
  tests = true
@@ -0,0 +1,15 @@
1
+ """Git Auto Commit (gac) - Generate commit messages using AI."""
2
+
3
+ from gac.__version__ import __version__
4
+ from gac.ai import generate_commit_message
5
+ from gac.git import get_staged_files, push_changes
6
+ from gac.prompt import build_prompt, clean_commit_message
7
+
8
+ __all__ = [
9
+ "__version__",
10
+ "generate_commit_message",
11
+ "build_prompt",
12
+ "clean_commit_message",
13
+ "get_staged_files",
14
+ "push_changes",
15
+ ]
@@ -1,3 +1,3 @@
1
1
  """Version information for gac package."""
2
2
 
3
- __version__ = "1.5.1"
3
+ __version__ = "1.5.2"
@@ -18,6 +18,7 @@ from gac.providers import (
18
18
  call_ollama_api,
19
19
  call_openai_api,
20
20
  call_openrouter_api,
21
+ call_streamlake_api,
21
22
  call_zai_api,
22
23
  call_zai_coding_api,
23
24
  )
@@ -71,6 +72,7 @@ def generate_commit_message(
71
72
  "cerebras": call_cerebras_api,
72
73
  "ollama": call_ollama_api,
73
74
  "openrouter": call_openrouter_api,
75
+ "streamlake": call_streamlake_api,
74
76
  "zai": call_zai_api,
75
77
  "zai-coding": call_zai_coding_api,
76
78
  "gemini": call_gemini_api,
@@ -102,6 +102,7 @@ def generate_with_retries(
102
102
  "ollama",
103
103
  "openai",
104
104
  "openrouter",
105
+ "streamlake",
105
106
  "zai",
106
107
  "zai-coding",
107
108
  ]
@@ -0,0 +1,116 @@
1
+ """CLI for initializing gac configuration interactively."""
2
+
3
+ from pathlib import Path
4
+
5
+ import click
6
+ import questionary
7
+ from dotenv import set_key
8
+
9
+ GAC_ENV_PATH = Path.home() / ".gac.env"
10
+
11
+
12
+ def _prompt_required_text(prompt: str) -> str | None:
13
+ """Prompt until a non-empty string is provided or the user cancels."""
14
+ while True:
15
+ response = questionary.text(prompt).ask()
16
+ if response is None:
17
+ return None
18
+ value = response.strip()
19
+ if value:
20
+ return value
21
+ click.echo("A value is required. Please try again.")
22
+
23
+
24
+ @click.command()
25
+ def init() -> None:
26
+ """Interactively set up $HOME/.gac.env for gac."""
27
+ click.echo("Welcome to gac initialization!\n")
28
+ if GAC_ENV_PATH.exists():
29
+ click.echo(f"$HOME/.gac.env already exists at {GAC_ENV_PATH}. Values will be updated.")
30
+ else:
31
+ GAC_ENV_PATH.touch()
32
+ click.echo(f"Created $HOME/.gac.env at {GAC_ENV_PATH}.")
33
+
34
+ providers = [
35
+ ("Anthropic", "claude-3-5-haiku-latest"),
36
+ ("Cerebras", "qwen-3-coder-480b"),
37
+ ("Gemini", "gemini-2.5-flash"),
38
+ ("Groq", "meta-llama/llama-4-maverick-17b-128e-instruct"),
39
+ ("LM Studio", "gemma3"),
40
+ ("Ollama", "gemma3"),
41
+ ("OpenAI", "gpt-4.1-mini"),
42
+ ("OpenRouter", "openrouter/auto"),
43
+ ("Streamlake", ""),
44
+ ("Z.AI", "glm-4.5-air"),
45
+ ("Z.AI Coding", "glm-4.6"),
46
+ ]
47
+ provider_names = [p[0] for p in providers]
48
+ provider = questionary.select("Select your provider:", choices=provider_names).ask()
49
+ if not provider:
50
+ click.echo("Provider selection cancelled. Exiting.")
51
+ return
52
+ provider_key = provider.lower().replace(".", "").replace(" ", "-")
53
+
54
+ is_ollama = provider_key == "ollama"
55
+ is_lmstudio = provider_key == "lm-studio"
56
+ is_streamlake = provider_key == "streamlake"
57
+ is_zai = provider_key in ("zai", "zai-coding")
58
+
59
+ if is_streamlake:
60
+ endpoint_id = _prompt_required_text("Enter the Streamlake inference endpoint ID (required):")
61
+ if endpoint_id is None:
62
+ click.echo("Streamlake configuration cancelled. Exiting.")
63
+ return
64
+ model_to_save = endpoint_id
65
+ else:
66
+ model_suggestion = dict(providers)[provider]
67
+ model_prompt = f"Enter the model (default: {model_suggestion}):"
68
+ model = questionary.text(model_prompt, default=model_suggestion).ask()
69
+ if model is None:
70
+ click.echo("Model entry cancelled. Exiting.")
71
+ return
72
+ model_to_save = model.strip() if model.strip() else model_suggestion
73
+
74
+ set_key(str(GAC_ENV_PATH), "GAC_MODEL", f"{provider_key}:{model_to_save}")
75
+ click.echo(f"Set GAC_MODEL={provider_key}:{model_to_save}")
76
+
77
+ if is_ollama:
78
+ url_default = "http://localhost:11434"
79
+ url = questionary.text(f"Enter the Ollama API URL (default: {url_default}):", default=url_default).ask()
80
+ if url is None:
81
+ click.echo("Ollama URL entry cancelled. Exiting.")
82
+ return
83
+ url_to_save = url.strip() if url.strip() else url_default
84
+ set_key(str(GAC_ENV_PATH), "OLLAMA_API_URL", url_to_save)
85
+ click.echo(f"Set OLLAMA_API_URL={url_to_save}")
86
+ elif is_lmstudio:
87
+ url_default = "http://localhost:1234"
88
+ url = questionary.text(f"Enter the LM Studio API URL (default: {url_default}):", default=url_default).ask()
89
+ if url is None:
90
+ click.echo("LM Studio URL entry cancelled. Exiting.")
91
+ return
92
+ url_to_save = url.strip() if url.strip() else url_default
93
+ set_key(str(GAC_ENV_PATH), "LMSTUDIO_API_URL", url_to_save)
94
+ click.echo(f"Set LMSTUDIO_API_URL={url_to_save}")
95
+
96
+ api_key_prompt = "Enter your API key (input hidden, can be set later):"
97
+ if is_ollama or is_lmstudio:
98
+ click.echo(
99
+ "This provider typically runs locally. API keys are optional unless your instance requires authentication."
100
+ )
101
+ api_key_prompt = "Enter your API key (optional, press Enter to skip):"
102
+
103
+ api_key = questionary.password(api_key_prompt).ask()
104
+ if api_key:
105
+ if is_zai:
106
+ api_key_name = "ZAI_API_KEY"
107
+ elif is_lmstudio:
108
+ api_key_name = "LMSTUDIO_API_KEY"
109
+ else:
110
+ api_key_name = f"{provider_key.upper()}_API_KEY"
111
+ set_key(str(GAC_ENV_PATH), api_key_name, api_key)
112
+ click.echo(f"Set {api_key_name} (hidden)")
113
+ elif is_ollama or is_lmstudio:
114
+ click.echo("Skipping API key. You can add one later if needed.")
115
+
116
+ click.echo(f"\ngac environment setup complete. You can edit {GAC_ENV_PATH} to update values later.")
@@ -59,7 +59,7 @@ def main(
59
59
  if model is None:
60
60
  handle_error(
61
61
  AIError.model_error(
62
- "No model specified. Please set the GAC_MODEL environment variable or use --model."
62
+ "gac init hasn't been run yet. Please run 'gac init' to set up your configuration, then try again."
63
63
  ),
64
64
  exit_program=True,
65
65
  )
@@ -8,6 +8,7 @@ from .lmstudio import call_lmstudio_api
8
8
  from .ollama import call_ollama_api
9
9
  from .openai import call_openai_api
10
10
  from .openrouter import call_openrouter_api
11
+ from .streamlake import call_streamlake_api
11
12
  from .zai import call_zai_api, call_zai_coding_api
12
13
 
13
14
  __all__ = [
@@ -19,6 +20,7 @@ __all__ = [
19
20
  "call_ollama_api",
20
21
  "call_openai_api",
21
22
  "call_openrouter_api",
23
+ "call_streamlake_api",
22
24
  "call_zai_api",
23
25
  "call_zai_coding_api",
24
26
  ]
@@ -0,0 +1,47 @@
1
+ """StreamLake (Vanchin) API provider for gac."""
2
+
3
+ import os
4
+
5
+ import httpx
6
+
7
+ from gac.errors import AIError
8
+
9
+
10
+ def call_streamlake_api(model: str, messages: list[dict], temperature: float, max_tokens: int) -> str:
11
+ """Call StreamLake (Vanchin) chat completions API."""
12
+ api_key = os.getenv("STREAMLAKE_API_KEY") or os.getenv("VC_API_KEY")
13
+ if not api_key:
14
+ raise AIError.model_error(
15
+ "STREAMLAKE_API_KEY not found in environment variables (VC_API_KEY alias also not set)"
16
+ )
17
+
18
+ url = "https://vanchin.streamlake.ai/api/gateway/v1/endpoints/chat/completions"
19
+ headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
20
+
21
+ data = {
22
+ "model": model,
23
+ "messages": messages,
24
+ "temperature": temperature,
25
+ "max_tokens": max_tokens,
26
+ }
27
+
28
+ try:
29
+ response = httpx.post(url, headers=headers, json=data, timeout=120)
30
+ response.raise_for_status()
31
+ response_data = response.json()
32
+ choices = response_data.get("choices")
33
+ if not choices:
34
+ raise AIError.model_error("StreamLake API returned no choices")
35
+
36
+ message = choices[0].get("message", {})
37
+ content = message.get("content")
38
+ if content is None:
39
+ raise AIError.model_error("StreamLake API returned null content")
40
+ if content == "":
41
+ raise AIError.model_error("StreamLake API returned empty content")
42
+
43
+ return content
44
+ except httpx.HTTPStatusError as e:
45
+ raise AIError.model_error(f"StreamLake API error: {e.response.status_code} - {e.response.text}") from e
46
+ except Exception as e: # noqa: BLE001 - convert to AIError
47
+ raise AIError.model_error(f"Error calling StreamLake API: {str(e)}") from e
@@ -1,27 +0,0 @@
1
- """Git Auto Commit (gac) - Generate commit messages using AI."""
2
-
3
- from gac.__version__ import __version__
4
- from gac.ai import generate_commit_message
5
- from gac.git import get_staged_files, push_changes
6
- from gac.prompt import build_prompt, clean_commit_message
7
- from gac.providers.anthropic import call_anthropic_api as anthropic_generate
8
- from gac.providers.cerebras import call_cerebras_api as cerebras_generate
9
- from gac.providers.groq import call_groq_api as groq_generate
10
- from gac.providers.ollama import call_ollama_api as ollama_generate
11
- from gac.providers.openai import call_openai_api as openai_generate
12
- from gac.providers.openrouter import call_openrouter_api as openrouter_generate
13
-
14
- __all__ = [
15
- "__version__",
16
- "generate_commit_message",
17
- "build_prompt",
18
- "clean_commit_message",
19
- "get_staged_files",
20
- "push_changes",
21
- "anthropic_generate",
22
- "cerebras_generate",
23
- "groq_generate",
24
- "ollama_generate",
25
- "openai_generate",
26
- "openrouter_generate",
27
- ]
@@ -1,53 +0,0 @@
1
- """CLI for initializing gac configuration interactively."""
2
-
3
- from pathlib import Path
4
-
5
- import click
6
- import questionary
7
- from dotenv import set_key
8
-
9
- GAC_ENV_PATH = Path.home() / ".gac.env"
10
-
11
-
12
- @click.command()
13
- def init() -> None:
14
- """Interactively set up $HOME/.gac.env for gac."""
15
- click.echo("Welcome to gac initialization!\n")
16
- if GAC_ENV_PATH.exists():
17
- click.echo(f"$HOME/.gac.env already exists at {GAC_ENV_PATH}. Values will be updated.")
18
- else:
19
- GAC_ENV_PATH.touch()
20
- click.echo(f"Created $HOME/.gac.env at {GAC_ENV_PATH}.")
21
-
22
- providers = [
23
- ("Anthropic", "claude-3-5-haiku-latest"),
24
- ("Cerebras", "qwen-3-coder-480b"),
25
- ("Gemini", "gemini-2.5-flash"),
26
- ("Groq", "meta-llama/llama-4-maverick-17b-128e-instruct"),
27
- ("LM Studio", "deepseek-r1-distill-qwen-7b"),
28
- ("Ollama", "gemma3"),
29
- ("OpenAI", "gpt-4.1-mini"),
30
- ("OpenRouter", "openrouter/auto"),
31
- ("Z.AI", "glm-4.5-air"),
32
- ("Z.AI Coding", "glm-4.6"),
33
- ]
34
- provider_names = [p[0] for p in providers]
35
- provider = questionary.select("Select your provider:", choices=provider_names).ask()
36
- if not provider:
37
- click.echo("Provider selection cancelled. Exiting.")
38
- return
39
- provider_key = provider.lower().replace(".", "").replace(" ", "-")
40
- model_suggestion = dict(providers)[provider]
41
- model = questionary.text(f"Enter the model (default: {model_suggestion}):", default=model_suggestion).ask()
42
- model_to_save = model.strip() if model.strip() else model_suggestion
43
- set_key(str(GAC_ENV_PATH), "GAC_MODEL", f"{provider_key}:{model_to_save}")
44
- click.echo(f"Set GAC_MODEL={provider_key}:{model_to_save}")
45
-
46
- api_key = questionary.password("Enter your API key (input hidden, can be set later):").ask()
47
- if api_key:
48
- # Z.AI and Z.AI Coding both use the same API key
49
- api_key_name = "ZAI_API_KEY" if provider_key in ["zai", "zai-coding"] else f"{provider_key.upper()}_API_KEY"
50
- set_key(str(GAC_ENV_PATH), api_key_name, api_key)
51
- click.echo(f"Set {api_key_name} (hidden)")
52
-
53
- click.echo(f"\ngac environment setup complete. You can edit {GAC_ENV_PATH} to update values later.")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes