Skip to content
Open
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
21 changes: 20 additions & 1 deletion src/kit/pr_review/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,12 @@ export KIT_OPENAI_TOKEN="your_openrouter_api_key"
export KIT_OPENAI_TOKEN="your_groq_api_key"
```

**Forge** (Unified API router)
```bash
export FORGE_API_KEY="your_forge_api_key"
# Optional: export FORGE_API_BASE="https://api.forge.tensorblock.co/v1"
```

**Local OpenAI API Server** (e.g., text-generation-webui, vLLM)
```bash
export KIT_OPENAI_TOKEN="not-used" # Local servers often don't need API keys
Expand All @@ -488,7 +494,7 @@ github:
base_url: https://api.github.com

llm:
provider: anthropic # or "openai"
provider: anthropic # or "openai", "google", "ollama", "forge"
model: claude-sonnet-4-20250514 # or "gpt-4o"
api_key: sk-ant-your_key_here
max_tokens: 4000
Expand All @@ -515,6 +521,16 @@ custom_pricing:

#### Custom OpenAI Compatible Provider Examples

**Forge Configuration:**
```yaml
llm:
provider: forge
model: "openai/gpt-4o-mini"
api_key: "your_forge_api_key"
# api_base_url: "https://api.forge.tensorblock.co/v1" # Optional override
max_tokens: 4000
```

**Together AI Configuration:**
```yaml
llm:
Expand Down Expand Up @@ -879,6 +895,8 @@ Output example:
| Anthropic Key | `KIT_ANTHROPIC_TOKEN` |
| OpenAI Key | `KIT_OPENAI_TOKEN` |
| Google Key | `KIT_GOOGLE_API_KEY` |
| Forge Key | `FORGE_API_KEY` |
| Forge Base URL (optional) | `FORGE_API_BASE` |

## Supported LLM Providers

Expand All @@ -888,6 +906,7 @@ Kit review supports multiple LLM providers:
- **OpenAI GPT** - Reliable performance with `gpt-4.1-2025-04-14`
- **Google Gemini** - Great models like `gemini-2.5-flash`
- **Ollama** - Free local models like `qwen2.5-coder:latest`
- **Forge** - OpenAI-compatible router using models like `openai/gpt-4o-mini`

### Cost Tracking

Expand Down
49 changes: 38 additions & 11 deletions src/kit/pr_review/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class LLMProvider(Enum):
GOOGLE = "google"


FORGE_DEFAULT_BASE_URL = "https://api.forge.tensorblock.co/v1"


class ReviewDepth(Enum):
"""Review analysis depth levels."""

Expand Down Expand Up @@ -232,12 +235,18 @@ def from_file(
or "anthropic"
)

provider_str = provider_str.lower()
is_forge_provider = provider_str == "forge"

try:
provider = LLMProvider(provider_str)
provider = LLMProvider.OPENAI if is_forge_provider else LLMProvider(provider_str)
except ValueError:
raise ValueError(f"Unsupported LLM provider: {provider_str}. Use: {[p.value for p in LLMProvider]}")
supported = [p.value for p in LLMProvider]
supported.append("forge")
raise ValueError(f"Unsupported LLM provider: {provider_str}. Use: {supported}")

# Default models and API key environment variables
api_base_url = llm_data.get("api_base_url")
if provider == LLMProvider.ANTHROPIC:
default_model = "claude-sonnet-4-5"
api_key_env = "KIT_ANTHROPIC_TOKEN or ANTHROPIC_API_KEY"
Expand All @@ -258,12 +267,22 @@ def from_file(
# Ollama doesn't need an API key, but we'll use a placeholder for consistency
api_key = llm_data.get("api_key", "ollama")
else: # OpenAI
default_model = "gpt-4.1-2025-04-14"
api_key_env = "KIT_OPENAI_TOKEN or OPENAI_API_KEY"
config_api_key = llm_data.get("api_key")
if _is_placeholder_token(config_api_key):
config_api_key = None # Treat placeholder as missing
api_key = config_api_key or os.getenv("KIT_OPENAI_TOKEN") or os.getenv("OPENAI_API_KEY")
if is_forge_provider:
default_model = "openai/gpt-4o-mini"
api_key_env = "FORGE_API_KEY"
config_api_key = llm_data.get("api_key")
if _is_placeholder_token(config_api_key):
config_api_key = None # Treat placeholder as missing
api_key = config_api_key or os.getenv("FORGE_API_KEY")
if not api_base_url:
api_base_url = os.getenv("FORGE_API_BASE") or FORGE_DEFAULT_BASE_URL
else:
default_model = "gpt-4.1-2025-04-14"
api_key_env = "KIT_OPENAI_TOKEN or OPENAI_API_KEY"
config_api_key = llm_data.get("api_key")
if _is_placeholder_token(config_api_key):
config_api_key = None # Treat placeholder as missing
api_key = config_api_key or os.getenv("KIT_OPENAI_TOKEN") or os.getenv("OPENAI_API_KEY")

# Ollama doesn't require API keys, so skip validation for it
if not api_key and provider != LLMProvider.OLLAMA:
Expand All @@ -286,7 +305,7 @@ def from_file(
model=llm_data.get("model", default_model),
api_key=str(api_key),
max_tokens=llm_data.get("max_tokens", 4000),
api_base_url=llm_data.get("api_base_url"),
api_base_url=api_base_url,
)

# Set default base URLs for local providers
Expand Down Expand Up @@ -362,8 +381,8 @@ def create_default_config_file(self, config_path: Optional[str] = None) -> str:
default_config = {
"github": {"token": "ghp_your_token_here", "base_url": "https://api.github.com"},
"llm": {
"provider": "anthropic", # or "openai", "google"
"model": "claude-sonnet-4-5", # or "gpt-4.1", "gemini-2.5-flash"
"provider": "anthropic", # or "openai", "google", "forge"
"model": "claude-sonnet-4-5", # or "gpt-4.1", "gemini-2.5-flash", "openai/gpt-4o-mini"
"api_key": "sk-your_api_key_here",
"max_tokens": 4000,
# For custom OpenAI compatible providers (e.g., Together AI, OpenRouter, etc.)
Expand Down Expand Up @@ -427,6 +446,14 @@ def create_default_config_file(self, config_path: Optional[str] = None) -> str:

# Example OpenAI compatible provider configurations:
#
# Forge (https://forge.tensorblock.co/):
# llm:
# provider: forge
# model: "openai/gpt-4o-mini"
# api_key: "your_forge_api_key"
# # api_base_url: "https://api.forge.tensorblock.co/v1" # Optional override
# max_tokens: 4000
#
# Together AI (https://together.ai/):
# llm:
# provider: openai
Expand Down
Loading