Modernize provider support
This commit is contained in:
@@ -17,6 +17,7 @@ class ProviderDialog(QDialog):
|
||||
self.setMinimumWidth(450)
|
||||
self._editing = provider is not None
|
||||
self._original_name = name
|
||||
self._provider = provider
|
||||
self._setup_ui()
|
||||
|
||||
if provider:
|
||||
@@ -114,7 +115,20 @@ class ProviderDialog(QDialog):
|
||||
return
|
||||
|
||||
try:
|
||||
add_provider(Provider(name, command, description, fallback))
|
||||
existing = self._provider
|
||||
add_provider(Provider(
|
||||
name=name,
|
||||
command=command,
|
||||
description=description,
|
||||
fallback=fallback,
|
||||
type=existing.type if existing else "subprocess",
|
||||
model=existing.model if existing else None,
|
||||
tags=existing.tags if existing else [],
|
||||
install=existing.install if existing else None,
|
||||
fallback_chain=existing.fallback_chain if existing else None,
|
||||
api_key_env=existing.api_key_env if existing else None,
|
||||
pty_config=existing.pty_config if existing else None,
|
||||
))
|
||||
self.accept()
|
||||
except Exception as e:
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
|
||||
@@ -19,59 +19,68 @@ from ...providers import load_providers, add_provider, Provider
|
||||
PROVIDER_INSTALL_INFO = {
|
||||
"claude": {
|
||||
"group": "Anthropic Claude",
|
||||
"install_cmd": "npm install -g @anthropic-ai/claude-code",
|
||||
"requires": "Node.js 18+ and npm",
|
||||
"setup": "Run 'claude' - opens browser for sign-in",
|
||||
"cost": "Pay-per-use (billed to your Anthropic account)",
|
||||
"install_cmd": "curl -fsSL https://claude.ai/install.sh | bash",
|
||||
"requires": "curl and bash",
|
||||
"setup": "Run 'claude' to sign in with a subscription or API key.",
|
||||
"cost": "Paid subscription or API key",
|
||||
"variants": [
|
||||
("claude", "claude -p", "Claude (default model)"),
|
||||
("claude-haiku", "claude -p --model claude-3-haiku-20240307", "Claude Haiku (fast, cheap)"),
|
||||
("claude-sonnet", "claude -p --model claude-3-5-sonnet-20241022", "Claude Sonnet (balanced)"),
|
||||
("claude-opus", "claude -p --model claude-3-opus-20240229", "Claude Opus (most capable)"),
|
||||
("claude-haiku", "claude -p --model haiku", "Claude Haiku (fast)"),
|
||||
("claude-sonnet", "claude -p --model sonnet", "Claude Sonnet (balanced)"),
|
||||
("claude-opus", "claude -p --model opus", "Claude Opus (highest quality)"),
|
||||
],
|
||||
},
|
||||
"codex": {
|
||||
"group": "OpenAI Codex",
|
||||
"install_cmd": "npm install -g @openai/codex",
|
||||
"requires": "Node.js 18+ and npm",
|
||||
"setup": "Run 'codex' - opens browser for sign-in",
|
||||
"cost": "Pay-per-use (billed to your OpenAI account)",
|
||||
"setup": "Run 'codex' to sign in.",
|
||||
"cost": "Free tier or ChatGPT subscription, subject to account limits",
|
||||
"variants": [
|
||||
("codex", "codex", "OpenAI Codex"),
|
||||
("codex", "codex exec -", "OpenAI Codex"),
|
||||
],
|
||||
},
|
||||
"gemini": {
|
||||
"group": "Google Gemini",
|
||||
"install_cmd": "npm install -g @google/gemini-cli",
|
||||
"requires": "Node.js 18+ and npm",
|
||||
"setup": "Run 'gemini' - opens browser for Google sign-in",
|
||||
"cost": "Free tier available, pay-per-use for more",
|
||||
"agy": {
|
||||
"group": "Google Antigravity",
|
||||
"install_cmd": None,
|
||||
"requires": "Google Antigravity desktop application",
|
||||
"setup": "Install from https://antigravity.google/download, then run 'agy install' to configure your PATH.",
|
||||
"cost": "Free tier available, subject to account limits",
|
||||
"variants": [
|
||||
("gemini", "gemini", "Gemini Pro (default)"),
|
||||
("gemini-flash", "gemini --model gemini-1.5-flash", "Gemini Flash (faster)"),
|
||||
("agy", "agy -p", "Google Antigravity (automatic model selection)"),
|
||||
],
|
||||
},
|
||||
"opencode": {
|
||||
"group": "OpenCode (75+ providers)",
|
||||
"group": "OpenCode",
|
||||
"install_cmd": "curl -fsSL https://opencode.ai/install | bash",
|
||||
"requires": "curl, bash",
|
||||
"setup": "Run 'opencode' - opens browser to connect providers",
|
||||
"cost": "4 FREE models included, 75+ more available",
|
||||
"setup": "Run 'opencode' and use /connect to configure providers.",
|
||||
"cost": "Free models available; paid providers require credentials",
|
||||
"variants": [
|
||||
("opencode-pickle", "$HOME/.opencode/bin/opencode -p big-pickle", "Big Pickle (FREE, high quality)"),
|
||||
("opencode-deepseek", "$HOME/.opencode/bin/opencode -p deepseek", "DeepSeek (fast, cheap)"),
|
||||
("opencode-reasoner", "$HOME/.opencode/bin/opencode -p deepseek-reasoner", "DeepSeek Reasoner (complex tasks)"),
|
||||
("opencode-grok", "$HOME/.opencode/bin/opencode -p grok", "Grok (xAI)"),
|
||||
("opencode-pickle", "opencode run --model opencode/big-pickle", "Big Pickle (free)"),
|
||||
("opencode-free", "opencode run --model opencode/deepseek-v4-flash-free", "DeepSeek V4 Flash (free)"),
|
||||
("opencode-deepseek", "opencode run --model deepseek/deepseek-chat", "DeepSeek Chat (API key required)"),
|
||||
("opencode-reasoner", "opencode run --model deepseek/deepseek-reasoner", "DeepSeek Reasoner (API key required)"),
|
||||
],
|
||||
},
|
||||
"crush": {
|
||||
"group": "Crush (Charm)",
|
||||
"install_cmd": "npm install -g @charmland/crush",
|
||||
"requires": "Node.js 18+ and npm",
|
||||
"setup": "Run 'crush' to configure Hyper or your provider API keys.",
|
||||
"cost": "Limited Hyper credits or your own provider billing",
|
||||
"variants": [
|
||||
("crush", "crush run --quiet", "Crush multi-model agent"),
|
||||
],
|
||||
},
|
||||
"ollama": {
|
||||
"group": "Ollama (Local LLMs)",
|
||||
"install_cmd": "curl -fsSL https://ollama.ai/install.sh | bash",
|
||||
"requires": "curl, bash, 8GB+ RAM (GPU recommended)",
|
||||
"setup": "Run 'ollama pull llama3' to download a model",
|
||||
"setup": "Run 'ollama pull llama3.2' to download a model.",
|
||||
"cost": "FREE (runs entirely on your machine)",
|
||||
"variants": [
|
||||
("ollama-llama3", "ollama run llama3", "Llama 3 (general purpose)"),
|
||||
("ollama-llama3", "ollama run llama3.2", "Llama 3.2 (general purpose)"),
|
||||
("ollama-codellama", "ollama run codellama", "CodeLlama (coding)"),
|
||||
("ollama-mistral", "ollama run mistral", "Mistral (fast)"),
|
||||
],
|
||||
@@ -336,7 +345,7 @@ class ProviderInstallDialog(QDialog):
|
||||
return opencode_path.exists() or shutil.which("opencode") is not None
|
||||
elif provider_key == "ollama":
|
||||
return shutil.which("ollama") is not None
|
||||
elif provider_key in ("claude", "codex", "gemini"):
|
||||
elif provider_key in ("claude", "codex", "agy", "crush"):
|
||||
return shutil.which(provider_key) is not None
|
||||
|
||||
return False
|
||||
@@ -351,10 +360,11 @@ class ProviderInstallDialog(QDialog):
|
||||
self.btn_install.setEnabled(True)
|
||||
|
||||
# Update details
|
||||
install_method = info['install_cmd'] or "Manual download"
|
||||
details = f"""<b>{info['group']}</b><br><br>
|
||||
<b>Cost:</b> {info['cost']}<br>
|
||||
<b>Requirements:</b> {info['requires']}<br>
|
||||
<b>Install command:</b> <code>{info['install_cmd']}</code><br>
|
||||
<b>Install method:</b> <code>{install_method}</code><br>
|
||||
<b>After install:</b> {info['setup']}<br><br>
|
||||
<b>Available models:</b> {', '.join(v[0] for v in info['variants'])}"""
|
||||
|
||||
@@ -364,7 +374,10 @@ class ProviderInstallDialog(QDialog):
|
||||
self.details_label.setText(details)
|
||||
|
||||
# Change button text based on installed status
|
||||
self.btn_install.setText("Reinstall" if is_installed else "Install Selected")
|
||||
if info["install_cmd"]:
|
||||
self.btn_install.setText("Reinstall" if is_installed else "Install Selected")
|
||||
else:
|
||||
self.btn_install.setText("Add Providers" if is_installed else "Setup Instructions")
|
||||
|
||||
def _on_provider_double_clicked(self, item: QListWidgetItem):
|
||||
"""Handle double-click to start install."""
|
||||
@@ -378,6 +391,14 @@ class ProviderInstallDialog(QDialog):
|
||||
|
||||
info = PROVIDER_INSTALL_INFO[self._selected_provider]
|
||||
|
||||
if not info["install_cmd"]:
|
||||
if self._check_installed(self._selected_provider):
|
||||
self._populate_variants()
|
||||
self.stack.setCurrentIndex(2)
|
||||
else:
|
||||
QMessageBox.information(self, "Manual Setup", info["setup"])
|
||||
return
|
||||
|
||||
# Switch to install page
|
||||
self.stack.setCurrentIndex(1)
|
||||
self.install_title.setText(f"Installing {info['group']}...")
|
||||
@@ -450,10 +471,8 @@ class ProviderInstallDialog(QDialog):
|
||||
# Update next steps
|
||||
self.next_steps_label.setText(
|
||||
f"<b>Important:</b> Before using these providers, you need to authenticate:<br><br>"
|
||||
f"1. Open a terminal<br>"
|
||||
f"2. Run: <code>source ~/.bashrc</code> (to update PATH)<br>"
|
||||
f"3. Run: <code>{info['setup'].split(' - ')[0].replace('Run ', '')}</code><br><br>"
|
||||
f"This will open your browser to sign in."
|
||||
f"1. Open a new terminal so PATH changes are loaded.<br>"
|
||||
f"2. {info['setup']}"
|
||||
)
|
||||
|
||||
def _add_selected_variants(self):
|
||||
|
||||
Reference in New Issue
Block a user