Skip to content

Commit cf3d61c

Browse files
committed
feat: migrate to LiteLLM for AI model support
- Replace OpenAI client with LiteLLM for broader model compatibility - Remove OpenAI and Anthropic provider-specific code - Update configuration to use AI_MODEL and AI_API_KEY environment variables - Simplify setup command to configure model and API key This change enhances the tool's flexibility by supporting a wider range of AI models through LiteLLM, simplifying configuration and removing provider-specific dependencies.
1 parent 28fbb2b commit cf3d61c

File tree

6 files changed

+90
-76
lines changed

6 files changed

+90
-76
lines changed

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Smart Commit turns your commit history into a valuable project resource that tel
7171

7272
- Python 3.10 or higher
7373
- Git repository (for generating commit messages)
74-
- API key for your chosen AI provider (OpenAI or Anthropic)
74+
- API key for your chosen AI provider (100+ models supported thanks to [LiteLLM](https://litellm.ai))
7575

7676
### Using uv (Recommended)
7777

@@ -100,12 +100,23 @@ pip install smart-commit-ai[all]
100100

101101
## Quick Setup
102102

103+
`smart-commit` is configured primarily through environment variables, which is secure and flexible.
104+
103105
```bash
104-
# Quick setup with OpenAI
105-
smart-commit setup --provider openai --model gpt-4o --api-key your-api-key
106+
# Set your AI model and API key
107+
export AI_MODEL="openai/gpt-4o" # Or "claude-3-sonnet-20240229", "gemini/gemini-1.5-pro", etc.
108+
export AI_API_KEY="your-super-secret-api-key"
109+
110+
# You can add these to your .bashrc, .zshrc, or shell profile to make them permanent.
111+
```
106112

107-
# Or use the short alias
108-
sc setup --provider openai --model gpt-4o --api-key your-api-key
113+
The `AI_MODEL` can be set to any model supported by `litellm`. For a full list of available providers and model names, please refer to the [LiteLLM Provider Documentation](https://docs.litellm.ai/docs/providers).
114+
115+
You can also run the setup command to save a fallback configuration file:
116+
117+
```bash
118+
# This will guide you through saving a config file.
119+
smart-commit setup
109120
```
110121

111122
## Usage
@@ -168,7 +179,6 @@ The configuration now includes enhanced features for better commit message gener
168179

169180
```toml
170181
[ai]
171-
provider = "openai"
172182
model = "gpt-4o"
173183
api_key = "your-api-key"
174184
max_tokens = 500

pyproject.toml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ classifiers = [
3535
]
3636
dependencies = [
3737
"typer>=0.9.0",
38-
"openai>=1.56.0",
38+
"litellm>=1.73.6.post1",
3939
"pydantic>=2.0.0",
4040
"rich>=13.0.0",
4141
"toml>=0.10.2",
@@ -66,11 +66,8 @@ dev = [
6666
"twine>=4.0.0",
6767
"build>=0.10.0",
6868
]
69-
anthropic = [
70-
"anthropic>=0.25.0",
71-
]
7269
all = [
73-
"smart-commit-ai[dev,anthropic]",
70+
"smart-commit-ai[dev]",
7471
]
7572

7673
[tool.hatch.version]

smart_commit/ai_providers.py

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
"""AI provider implementations."""
2-
32
from abc import ABC, abstractmethod
4-
5-
from openai import OpenAI
6-
3+
import litellm
74
from smart_commit.utils import remove_backticks
85

96

@@ -15,54 +12,39 @@ def generate_commit_message(self, prompt: str, **kwargs) -> str:
1512
"""Generate a commit message using the AI provider."""
1613
pass
1714

15+
class LiteLLMProvider(AIProvider):
16+
"""LiteLLM provider implementation."""
1817

19-
class OpenAIProvider(AIProvider):
20-
"""OpenAI provider implementation."""
21-
22-
def __init__(self, api_key: str, model: str = "gpt-4o", **kwargs):
23-
self.client = OpenAI(api_key=api_key)
18+
def __init__(self, api_key: str, model: str, **kwargs):
19+
if not api_key:
20+
raise ValueError("API_KEY is required for LiteLLMProvider.")
21+
if not model:
22+
raise ValueError("AI_MODEL is required for LiteLLMProvider.")
23+
24+
self.api_key = api_key
2425
self.model = model
2526
self.kwargs = kwargs
26-
27+
2728
def generate_commit_message(self, prompt: str, **kwargs) -> str:
28-
"""Generate commit message using OpenAI."""
29+
"""Generate commit message using LiteLLM."""
2930
merged_kwargs = {**self.kwargs, **kwargs}
3031

31-
response = self.client.chat.completions.create(
32-
model=self.model,
33-
messages=[{"role": "user", "content": prompt}],
34-
max_tokens=merged_kwargs.get("max_tokens", 500),
35-
temperature=merged_kwargs.get("temperature", 0.1),
36-
)
37-
38-
return remove_backticks((response.choices[0].message.content or "").strip())
39-
40-
41-
class AnthropicProvider(AIProvider):
42-
"""Anthropic provider implementation (placeholder)."""
43-
44-
def __init__(self, api_key: str, model: str = "claude-3-sonnet-20240229", **kwargs):
45-
# This would require the anthropic library
46-
# import anthropic
47-
# self.client = anthropic.Anthropic(api_key=api_key)
48-
self.model = model
49-
self.kwargs = kwargs
50-
raise NotImplementedError("Anthropic provider not implemented yet")
51-
52-
def generate_commit_message(self, prompt: str, **kwargs) -> str:
53-
"""Generate commit message using Anthropic."""
54-
# Implementation would go here
55-
raise NotImplementedError("Anthropic provider not implemented yet")
56-
57-
58-
def get_ai_provider(provider_name: str, api_key: str, model: str, **kwargs) -> AIProvider:
59-
"""Factory function to get AI provider."""
60-
providers = {
61-
"openai": OpenAIProvider,
62-
"anthropic": AnthropicProvider,
63-
}
64-
65-
if provider_name not in providers:
66-
raise ValueError(f"Unsupported AI provider: {provider_name}")
67-
68-
return providers[provider_name](api_key=api_key, model=model, **kwargs)
32+
try:
33+
response = litellm.completion(
34+
model=self.model,
35+
messages=[{"role": "user", "content": prompt}],
36+
api_key=self.api_key,
37+
max_tokens=merged_kwargs.get("max_tokens", 500),
38+
temperature=merged_kwargs.get("temperature", 0.1),
39+
)
40+
# The response object is a ModelResponse, which is dict-like
41+
return remove_backticks((response.choices[0].message.content or "").strip())
42+
except Exception as e:
43+
# LiteLLM provides rich exception types, you can handle them specifically if needed
44+
# For now, we'll just re-raise a generic error.
45+
raise RuntimeError(f"LiteLLM failed to generate a response: {e}") from e
46+
47+
def get_ai_provider(api_key: str, model: str, **kwargs) -> AIProvider:
48+
"""Factory function to get the LiteLLM AI provider."""
49+
# The factory is now much simpler.
50+
return LiteLLMProvider(api_key=api_key, model=model, **kwargs)

smart_commit/cli.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ def generate(
4444
try:
4545
# Load configuration
4646
config = config_manager.load_config()
47+
48+
# Get AI credentials from environment variables first, then from config
49+
api_key = os.getenv("AI_API_KEY") or config.ai.api_key
50+
model = os.getenv("AI_MODEL") or config.ai.model
51+
52+
if not api_key:
53+
console.print("[red]Error: AI_API_KEY environment variable or api_key in config not set.[/red]")
54+
console.print("Please run `smart-commit setup` or set the environment variable.")
55+
raise typer.Exit(1)
56+
57+
if not model:
58+
console.print("[red]Error: AI_MODEL environment variable or model in config not set.[/red]")
59+
raise typer.Exit(1)
4760

4861
# Check for staged changes
4962
staged_changes = _get_staged_changes()
@@ -86,13 +99,11 @@ def generate(
8699

87100
try:
88101
ai_provider = get_ai_provider(
89-
provider_name=config.ai.provider,
90-
api_key=config.ai.api_key or "",
91-
model=config.ai.model,
102+
api_key=api_key,
103+
model=model,
92104
max_tokens=config.ai.max_tokens,
93105
temperature=config.ai.temperature
94106
)
95-
96107
raw_message = ai_provider.generate_commit_message(prompt)
97108

98109
# Format message
@@ -180,28 +191,28 @@ def context(
180191

181192
@app.command()
182193
def setup(
183-
provider: str = typer.Option("openai", help="AI provider (openai, anthropic)"),
184-
model: str = typer.Option("gpt-4o", help="Model to use"),
194+
model: str = typer.Option("openai/gpt-4o", help="Model to use (e.g., 'openai/gpt-4o', 'claude-3-haiku-20240307')"),
185195
api_key: Optional[str] = typer.Option(None, help="API key (will prompt if not provided)"),
186196
) -> None:
187197
"""Quick setup for smart-commit."""
188198

189199
console.print("[bold blue]Smart-Commit Setup[/bold blue]")
190200

201+
console.print("This will save your configuration globally. For best practice, use environment variables:")
202+
console.print(" [cyan]export AI_MODEL='model_name'[/cyan]")
203+
console.print(" [cyan]export AI_API_KEY='your_api_key'[/cyan]")
191204
if not api_key:
192-
api_key = Prompt.ask(f"Enter your {provider.upper()} API key", password=True)
205+
api_key = Prompt.ask("Enter your API key", password=True)
193206

194-
# Create basic config
207+
# Save to global config as a fallback
195208
config = config_manager.load_config()
196-
config.ai.provider = provider
197209
config.ai.model = model
198210
config.ai.api_key = api_key
199211

200212
# Save global config
201213
config_manager.save_config(config, local=False)
202214

203215
console.print("[green]✓ Configuration saved successfully![/green]")
204-
console.print(f"Provider: {provider}")
205216
console.print(f"Model: {model}")
206217
console.print(f"Config saved to: {config_manager.global_config_path}")
207218

smart_commit/config.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,13 @@ class CommitTemplateConfig(BaseModel):
7070

7171
class AIConfig(BaseModel):
7272
"""Configuration for AI provider."""
73-
provider: str = Field(default="openai", description="AI provider (openai, anthropic, etc.)")
74-
model: str = Field(default="gpt-4o", description="Model to use")
75-
api_key: Optional[str] = Field(default=None, description="API key (can be set via environment)")
73+
# provider: str = Field(default="openai", description="AI provider (openai, anthropic, etc.)") <- REMOVE
74+
model: str = Field(default="openai/gpt-4o", description="Model to use (e.g., 'openai/gpt-4o', 'claude-3-sonnet-20240229')")
75+
api_key: Optional[str] = Field(default=None, description="API key (best set via AI_API_KEY environment variable)")
7676
max_tokens: int = Field(default=500, description="Maximum tokens for response")
7777
temperature: float = Field(default=0.1, description="Temperature for AI generation")
78+
# this field is for backwards compatibility
79+
provider: str = Field(default="openai", description="AI provider (openai, anthropic, etc.) [Deprecated]")
7880

7981

8082
class RepositoryConfig(BaseModel):

smart_commit/mcp.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""MCP (Model Context Protocol) server implementation using FastMCP."""
22

3+
import os
34
import subprocess
45
from pathlib import Path
56
from typing import Optional
@@ -85,6 +86,18 @@ def generate_commit_message(
8586
show_prompt: Whether to include the generated prompt in the response
8687
"""
8788
try:
89+
# ...
90+
# Load configuration
91+
config_manager = ConfigManager()
92+
config = config_manager.load_config()
93+
94+
# Get AI credentials from environment variables first, then from config
95+
api_key = os.getenv("AI_API_KEY") or config.ai.api_key
96+
model = os.getenv("AI_MODEL") or config.ai.model
97+
98+
if not api_key or not model:
99+
return "Error: AI_MODEL and AI_API_KEY must be set as environment variables or in the config."
100+
88101
repo_path = Path(repository_path) if repository_path else None
89102

90103
# Get staged changes
@@ -118,9 +131,8 @@ def generate_commit_message(
118131

119132
# Generate commit message
120133
ai_provider = get_ai_provider(
121-
provider_name=config.ai.provider,
122-
api_key=config.ai.api_key or "",
123-
model=config.ai.model,
134+
api_key=api_key,
135+
model=model,
124136
max_tokens=config.ai.max_tokens,
125137
temperature=config.ai.temperature
126138
)

0 commit comments

Comments
 (0)