📚 docs(changelog): updated changelog format and added ChangelogGenerator class documentation

This commit is contained in:
sankalp committed 2025-06-04 12:10:41 +05:45
1 parent 707724e0ea
commit dd342ec86a
3 files changed
+535 -82

No files matched your search

+38 -15
View File
@@ -1,30 +1,53 @@
## 1.0.0
📝 Documentation
Version 1.0.0 Changelog
-------------------------
- 📚 docs(changelog): updated changelog format for clarity and consistency
- 📚 docs(changelog): added documentation for ChangelogGenerator class
### 🔧 Maintenance
🚀 Performance
* 📝 docs(changelog): updated changelog format for clarity and consistency
- No performance changes in this version
### 📝 Documentation
🐛 Fixed
* 📚 docs(changelog): update changelog format and add ChangelogGenerator class documentation
- No fixes in this version
### 🔄 Changed
🔧 Maintenance
* ♻️ refactor(git.py): remove unused variable in _run_git_command calls
* ♻️ refactor(devtools/shared/git.py): update GitService to return exit code from _run_git_command and improve get_commit_history method signature and docstring.
- Updated LICENSE file for legal compliance
### 📝 Documentation
🗑️ Removed
* Update LICENSE
- No features or components removed in this version
### 🗑️ Removed
🔒 Security
* Initial commit
- No security improvements in this version
## 1.0.0
✨ Added
### Version 1.0.0
#### 🔄 Changed
- refactor(git.py): Removed unused variable in `_run_git_command` calls
- refactor(devtools/shared/git.py): Updated `GitService` to return exit code from `_run_git_command` and improved `get_commit_history` method signature and docstring
#### 📝 Documentation
- docs(changelog): Updated changelog format for clarity and consistency
- docs(changelog): Update changelog format and add ChangelogGenerator class documentation
#### 🔧 Maintenance
- Update LICENSE
- Initial commit
#### 🚀 Performance & 🗑️ Removed
- None
#### 🔒 Security
- None
- Initial commit of project files and structure
+53 -15
View File
@@ -7,7 +7,7 @@ A collection of developer tools to enhance your workflow. This package includes
```bash
# Clone the repository
$ git clone https://github.com/S4NKALP/DevTools.git
$ cd DevTools
$ cd devtools
# Install in editable mode
$ pipx install -e .
@@ -20,28 +20,42 @@ $ pipx install -e .
The tools use various APIs that require API keys. You can set these using the config command:
```bash
# Set OpenRouter API key (for AI features)
# Set AI provider API key (required for AI features)
# For OpenRouter
devtools config set OPENROUTER_API_KEY "your-openrouter-api-key"
# Set GitHub API key (optional, for enhanced features)
devtools config set github_api_key "your-github-api-key"
# For OpenAI
devtools config set OPENAI_API_KEY "your-openai-api-key"
# For Google Gemini
devtools config set GOOGLE_API_KEY "your-google-api-key"
# For Anthropic Claude
devtools config set ANTHROPIC_API_KEY "your-anthropic-api-key"
# For Hugging Face
devtools config set HUGGINGFACE_API_KEY "your-huggingface-api-key"
```
Or set them in your environment:
```bash
# OpenRouter API key
# AI Provider API keys
export OPENROUTER_API_KEY="your-openrouter-api-key"
# GitHub API key
export GITHUB_API_KEY="your-github-api-key"
export OPENAI_API_KEY="your-openai-api-key"
export GOOGLE_API_KEY="your-google-api-key"
export ANTHROPIC_API_KEY="your-anthropic-api-key"
export HUGGINGFACE_API_KEY="your-huggingface-api-key"
```
Or create a `.env` file in your project root:
```bash
OPENROUTER_API_KEY=your-openrouter-api-key
GITHUB_API_KEY=your-github-api-key
OPENAI_API_KEY=your-openai-api-key
GOOGLE_API_KEY=your-google-api-key
ANTHROPIC_API_KEY=your-anthropic-api-key
HUGGINGFACE_API_KEY=your-huggingface-api-key
```
### Managing Configuration
@@ -69,8 +83,14 @@ Example config.yaml:
```yaml
# API Keys
OPENROUTER_API_KEY:
OPENAI_API_KEY:
GOOGLE_API_KEY:
ANTHROPIC_API_KEY:
HUGGINGFACE_API_KEY:
# AI Settings
provider: openrouter # or "openai", "gemini", "claude", "huggingface"
model: mistralai/mixtral-8x7b-instruct # model name for the selected provider
max_tokens: 1024
temperature: 0.7
output_format: text
@@ -87,11 +107,20 @@ repositories: []
You can set up all configuration options for DevTools using the CLI. This is the recommended way to configure your `~/.devtools/config.yaml` file:
```bash
# Set OpenRouter API key (required for AI features)
devtools config set openrouter_api_key "your-openrouter-api-key"
# Set AI provider (required for AI features)
devtools config set provider "openrouter" # or "openai", "gemini", "claude", "huggingface"
# Set preferred AI model (optional, default is Mixtral)
# Set preferred AI model (optional, depends on provider)
# For OpenRouter
devtools config set model "mistralai/mixtral-8x7b-instruct"
# For OpenAI
devtools config set model "gpt-4-turbo-preview"
# For Gemini
devtools config set model "gemini-pro"
# For Claude
devtools config set model "claude-3-opus-20240229"
# For Hugging Face
devtools config set model "mistralai/Mixtral-8x7B-Instruct-v0.1"
# Set temperature for AI completions (optional, default is 0.7)
devtools config set temperature 0.7
@@ -248,7 +277,12 @@ The generator uses the gitignore.io API to fetch templates. No API key is requir
## Features
- **Commit Message Generator:**
- AI-powered commit message suggestions using Mixtral 8x7B
- AI-powered commit message suggestions using multiple providers:
- OpenRouter (Mixtral 8x7B)
- OpenAI (GPT-4, GPT-3.5)
- Google Gemini
- Anthropic Claude
- Hugging Face
- Conventional commit format with emojis
- GPG signing and push support
- Changelog generation from commit history
@@ -263,8 +297,12 @@ The generator uses the gitignore.io API to fetch templates. No API key is requir
- Python 3.8+
- Git
- OpenRouter API key (for AI features)
- GitHub API key (optional)
- AI Provider API key (one of):
- OpenRouter API key
- OpenAI API key
- Google API key
- Anthropic API key
- Hugging Face API key
## Development
+444 -52
View File
@@ -1,74 +1,100 @@
"""
Shared AI service for devtools.
"""
import requests
import os
from typing import List, Dict, Optional, Any
from abc import ABC, abstractmethod
from .config import BaseConfig
class AIService:
"""Base AI service that can be extended by specific tools."""
def __init__(self, config: BaseConfig):
"""Initialize AI service with configuration."""
self.config = config
self._setup_ai()
def _setup_ai(self) -> None:
"""Set up the AI model with configuration."""
self.api_key = self.config.get_env_or_config("OPENROUTER_API_KEY")
class AIProvider(ABC):
"""Base class for AI providers."""
@abstractmethod
def setup(self, config: BaseConfig) -> None:
"""Set up the provider with configuration."""
pass
@abstractmethod
def generate_completion(
self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95,
) -> str:
"""Generate a completion from the AI model."""
pass
class OpenRouterProvider(AIProvider):
"""OpenRouter AI provider implementation."""
def setup(self, config: BaseConfig) -> None:
self.config = config
self.api_key = config.get_env_or_config("OPENROUTER_API_KEY")
if not self.api_key:
raise ValueError("OpenRouter API key not found. Please set it in config or environment.")
self.model = self.config.get("model", "mistralai/mixtral-8x7b-instruct")
raise ValueError(
"OpenRouter API key not found. Please set it in config or environment."
)
self.model = config.get("model", "mistralai/mixtral-8x7b-instruct")
self.api_url = "https://openrouter.ai/api/v1/chat/completions"
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"HTTP-Referer": "https://github.com/S4NKALP/DevTools",
"X-Title": "DevTools",
"Content-Type": "application/json"
"Content-Type": "application/json",
}
def _create_prompt(self, system_prompt: str, user_prompt: str) -> List[Dict[str, str]]:
"""Create a prompt for the AI model."""
def _create_prompt(
self, system_prompt: str, user_prompt: str
) -> List[Dict[str, str]]:
return [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
{"role": "user", "content": user_prompt},
]
def generate_completion(self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95) -> str:
"""Generate a completion from the AI model."""
def generate_completion(
self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95,
) -> str:
messages = self._create_prompt(system_prompt, user_prompt)
try:
# Convert config values to proper types
temp = temperature if temperature is not None else float(self.config.get("temperature", 0.7))
tokens = max_tokens if max_tokens is not None else int(self.config.get("max_tokens", 150))
temp = (
temperature
if temperature is not None
else float(self.config.get("temperature", 0.7))
)
tokens = (
max_tokens
if max_tokens is not None
else int(self.config.get("max_tokens", 150))
)
payload = {
"model": self.model,
"messages": messages,
"temperature": temp,
"max_tokens": tokens,
"top_p": float(top_p),
"stream": False
"stream": False,
}
# Remove None values
payload = {k: v for k, v in payload.items() if v is not None}
response = requests.post(
self.api_url,
headers=self.headers,
json=payload,
timeout=30
self.api_url, headers=self.headers, json=payload, timeout=30
)
if response.status_code != 200:
error_msg = response.text
try:
@@ -78,30 +104,396 @@ class AIService:
except:
pass
raise Exception(f"OpenRouter API error: {error_msg}")
result = response.json()
if "choices" in result and len(result["choices"]) > 0:
return result["choices"][0]["message"]["content"].strip()
return ""
except Exception as e:
raise Exception(f"AI generation failed: {str(e)}")
def generate_batch_completions(self,
system_prompt: str,
prompts: List[str],
temperature: Optional[float] = None,
max_tokens: Optional[int] = None) -> List[str]:
class OpenAIProvider(AIProvider):
"""OpenAI provider implementation."""
def setup(self, config: BaseConfig) -> None:
self.config = config
self.api_key = config.get_env_or_config("OPENAI_API_KEY")
if not self.api_key:
raise ValueError(
"OpenAI API key not found. Please set it in config or environment."
)
self.model = config.get("model", "gpt-4-turbo-preview")
self.api_url = "https://api.openai.com/v1/chat/completions"
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
def _create_prompt(
self, system_prompt: str, user_prompt: str
) -> List[Dict[str, str]]:
return [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
]
def generate_completion(
self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95,
) -> str:
messages = self._create_prompt(system_prompt, user_prompt)
try:
temp = (
temperature
if temperature is not None
else float(self.config.get("temperature", 0.7))
)
tokens = (
max_tokens
if max_tokens is not None
else int(self.config.get("max_tokens", 150))
)
payload = {
"model": self.model,
"messages": messages,
"temperature": temp,
"max_tokens": tokens,
"top_p": float(top_p),
}
payload = {k: v for k, v in payload.items() if v is not None}
response = requests.post(
self.api_url, headers=self.headers, json=payload, timeout=30
)
if response.status_code != 200:
error_msg = response.text
try:
error_json = response.json()
if "error" in error_json:
error_msg = error_json["error"]
except:
pass
raise Exception(f"OpenAI API error: {error_msg}")
result = response.json()
if "choices" in result and len(result["choices"]) > 0:
return result["choices"][0]["message"]["content"].strip()
return ""
except Exception as e:
raise Exception(f"AI generation failed: {str(e)}")
class GeminiProvider(AIProvider):
"""Google Gemini provider implementation."""
def setup(self, config: BaseConfig) -> None:
self.config = config
self.api_key = config.get_env_or_config("GOOGLE_API_KEY")
if not self.api_key:
raise ValueError(
"Google API key not found. Please set it in config or environment."
)
self.model = config.get("model", "gemini-pro")
self.api_url = f"https://generativelanguage.googleapis.com/v1beta/models/{
self.model
}:generateContent"
self.headers = {"Content-Type": "application/json"}
def generate_completion(
self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95,
) -> str:
try:
temp = (
temperature
if temperature is not None
else float(self.config.get("temperature", 0.7))
)
tokens = (
max_tokens
if max_tokens is not None
else int(self.config.get("max_tokens", 150))
)
# Combine system and user prompts for Gemini
full_prompt = f"{system_prompt}\n\n{user_prompt}"
payload = {
"contents": [{"parts": [{"text": full_prompt}]}],
"generationConfig": {
"temperature": temp,
"maxOutputTokens": tokens,
"topP": float(top_p),
},
}
response = requests.post(
f"{self.api_url}?key={self.api_key}",
headers=self.headers,
json=payload,
timeout=30,
)
if response.status_code != 200:
error_msg = response.text
try:
error_json = response.json()
if "error" in error_json:
error_msg = error_json["error"]["message"]
except:
pass
raise Exception(f"Gemini API error: {error_msg}")
result = response.json()
if "candidates" in result and len(result["candidates"]) > 0:
return result["candidates"][0]["content"]["parts"][0]["text"].strip()
return ""
except Exception as e:
raise Exception(f"AI generation failed: {str(e)}")
class ClaudeProvider(AIProvider):
"""Anthropic Claude provider implementation."""
def setup(self, config: BaseConfig) -> None:
self.config = config
self.api_key = config.get_env_or_config("ANTHROPIC_API_KEY")
if not self.api_key:
raise ValueError(
"Anthropic API key not found. Please set it in config or environment."
)
self.model = config.get("model", "claude-3-opus-20240229")
self.api_url = "https://api.anthropic.com/v1/messages"
self.headers = {
"x-api-key": self.api_key,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
}
def _create_prompt(
self, system_prompt: str, user_prompt: str
) -> List[Dict[str, str]]:
return [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
]
def generate_completion(
self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95,
) -> str:
messages = self._create_prompt(system_prompt, user_prompt)
try:
temp = (
temperature
if temperature is not None
else float(self.config.get("temperature", 0.7))
)
tokens = (
max_tokens
if max_tokens is not None
else int(self.config.get("max_tokens", 150))
)
payload = {
"model": self.model,
"messages": messages,
"temperature": temp,
"max_tokens": tokens,
"top_p": float(top_p),
}
payload = {k: v for k, v in payload.items() if v is not None}
response = requests.post(
self.api_url, headers=self.headers, json=payload, timeout=30
)
if response.status_code != 200:
error_msg = response.text
try:
error_json = response.json()
if "error" in error_json:
error_msg = error_json["error"]["message"]
except:
pass
raise Exception(f"Claude API error: {error_msg}")
result = response.json()
if "content" in result and len(result["content"]) > 0:
return result["content"][0]["text"].strip()
return ""
except Exception as e:
raise Exception(f"AI generation failed: {str(e)}")
class HuggingFaceProvider(AIProvider):
"""Hugging Face provider implementation."""
def setup(self, config: BaseConfig) -> None:
self.config = config
self.api_key = config.get_env_or_config("HUGGINGFACE_API_KEY")
if not self.api_key:
raise ValueError(
"Hugging Face API key not found. Please set it in config or environment."
)
self.model = config.get("model", "mistralai/Mixtral-8x7B-Instruct-v0.1")
self.api_url = f"https://api-inference.huggingface.co/models/{self.model}"
self.headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
def _create_prompt(self, system_prompt: str, user_prompt: str) -> str:
# Format prompt according to model's requirements
return f"<s>[INST] {system_prompt}\n\n{user_prompt} [/INST]"
def generate_completion(
self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95,
) -> str:
try:
temp = (
temperature
if temperature is not None
else float(self.config.get("temperature", 0.7))
)
tokens = (
max_tokens
if max_tokens is not None
else int(self.config.get("max_tokens", 150))
)
prompt = self._create_prompt(system_prompt, user_prompt)
payload = {
"inputs": prompt,
"parameters": {
"temperature": temp,
"max_new_tokens": tokens,
"top_p": float(top_p),
"return_full_text": False,
},
}
response = requests.post(
self.api_url, headers=self.headers, json=payload, timeout=30
)
if response.status_code != 200:
error_msg = response.text
try:
error_json = response.json()
if "error" in error_json:
error_msg = error_json["error"]
except:
pass
raise Exception(f"Hugging Face API error: {error_msg}")
result = response.json()
if isinstance(result, list) and len(result) > 0:
return result[0]["generated_text"].strip()
return ""
except Exception as e:
raise Exception(f"AI generation failed: {str(e)}")
class AIService:
"""Base AI service that can be extended by specific tools."""
def __init__(self, config: BaseConfig):
"""Initialize AI service with configuration."""
self.config = config
self.provider = self._get_provider()
self.provider.setup(config)
def _get_provider(self) -> AIProvider:
"""Get the appropriate AI provider based on configuration."""
provider = self.config.get("provider", "openrouter").lower()
providers = {
"openrouter": OpenRouterProvider,
"openai": OpenAIProvider,
"gemini": GeminiProvider,
"claude": ClaudeProvider,
"huggingface": HuggingFaceProvider,
}
if provider not in providers:
raise ValueError(
f"Unsupported AI provider: {provider}. Supported providers: {
', '.join(providers.keys())
}"
)
return providers[provider]()
def generate_completion(
self,
system_prompt: str,
user_prompt: str,
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
top_p: float = 0.95,
) -> str:
"""Generate a completion from the AI model."""
return self.provider.generate_completion(
system_prompt, user_prompt, temperature, max_tokens, top_p
)
def generate_batch_completions(
self,
system_prompt: str,
prompts: List[str],
temperature: Optional[float] = None,
max_tokens: Optional[int] = None,
) -> List[str]:
"""Generate completions for multiple prompts."""
completions = []
for prompt in prompts:
completion = self.generate_completion(
system_prompt,
prompt,
temperature,
max_tokens
system_prompt, prompt, temperature, max_tokens
)
completions.append(completion)
return completions
return completions