← Back to Kontasks
Provider Interface & Multi-Provider Routing
Technical deep-dive into gvShell's Provider interface and Registry system
Output
🤖 Provider Interface & Multi-Provider Routing
gvShell's abstraction layer for AI model access
📝 Provider Interface
type Provider interface { // Name returns the provider identifier (e.g., "claude", "gpt") Name() string // Send sends a prompt and returns the response Send(ctx context.Context, prompt string) (Response, error) // Stream sends a prompt and streams the response Stream(ctx context.Context, prompt string) (<-chan StreamEvent, error) }
📦 Response & StreamEvent Types
type Response struct { Content string // AI response text Model string // Model used (e.g., "claude-sonnet-4") TokensIn int // Input tokens consumed TokensOut int // Output tokens generated DurationMs int64 // Request duration } type StreamEvent struct { Type string // "text", "done", "error" Content string // Chunk content Error error // Error if Type=="error" }
🗂️ Provider Types
| Type | Description | Providers |
|---|---|---|
TypeAPI |
Direct HTTP API calls (stateless, function calling only) | claude, openai, gemini, ollama |
TypeCLI |
Spawn subprocess (full tool access, persistent context) | claude-cli, codex |
🚦 @Provider Routing
Users can target specific providers with prefix syntax:
// Format: @provider prompt or @provider:prompt "@claude explain quantum computing" // → Claude API "@gpt4 write a haiku about code" // → OpenAI (via alias) "@ollama:summarize this file" // → Local Ollama "@cc fix this bug" // → Claude CLI (alias) "what is 2+2?" // → Default provider
📚 Standard Providers
| Name | Type | Aliases | Env Var |
|---|---|---|---|
claude |
API | anthropic, sonnet, opus, haiku | ANTHROPIC_API_KEY |
claude-cli |
CLI | claudecli, cc | ANTHROPIC_API_KEY |
openai |
API | gpt, gpt4, gpt4o, chatgpt, o1, o3 | OPENAI_API_KEY |
codex |
CLI | codex-cli, codexcli | OPENAI_API_KEY |
gemini |
API | google, flash, pro | GOOGLE_API_KEY |
ollama |
API | local, llama, mistral | (none - local) |
🔄 Registry Flow
┌───────────────┐ │ User Prompt │ "@claude hello" └───────┬───────┘ │ ▼ ┌─────────────────────────┐ │ ParseProviderPrefix() │ Extract @provider prefix └────────────┬────────────┘ │ ▼ ┌─────────────────────────┐ │ Registry.Get(name) │ Resolve alias → factory() └────────────┬────────────┘ │ ▼ ┌─────────────────────────┐ │ Provider.Stream(prompt) │ Send to AI model └────────────┬────────────┘ │ ▼ ┌───────────────┐ │ StreamEvents │ text → text → done └───────────────┘
⚡ Key Registry Methods
// Registration registry.Register(info, factory) // Add provider registry.Unregister(name) // Remove provider // Lookup registry.Get(name) // Get by name/alias registry.GetDefault() // Get default provider registry.GetWithFallback() // Try fallback chain // Routing registry.RoutePrompt(prompt) // Parse @prefix, return provider registry.SendWithRouting(ctx, p) // Route + send (sync) registry.StreamWithRouting(ctx, p) // Route + stream // Configuration registry.SetDefault(name) // Set default provider registry.SetFallbackChain(names) // Set fallback order registry.ListByType(TypeCLI) // Filter by type
💡 Design Highlights
- Factory pattern - Providers created on-demand via
ProviderFactory - Thread-safe -
sync.RWMutexprotects all registry operations - Alias resolution - Case-insensitive, supports multiple aliases per provider
- Fallback chains - Automatic failover when primary provider unavailable
- CLI vs API split - CLI providers have full tool access, API providers are stateless
What's Next?
Choose an action:
Quick Actions
Original Request
No layout configured
Details
Type
General
Status
Completed
Scope
vibetools
Tags
gvshellprovidersarchitectureapi
Created
7 Jan 2026, 11:29 pm
Updated
7 Jan 2026, 11:30 pm
Timing
⏱️ 86.4s
0 tools
● TTFE 0%
● Thinking 100%
● Tools 0%
Created By
claude
Raw Data
{
"id": "f6537bc6-0eea-480f-b746-9e735c598693",
"type": "general",
"status": "completed",
"title": "Provider Interface & Multi-Provider Routing",
"description": "Technical deep-dive into gvShell's Provider interface and Registry system",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:'Consolas','Monaco',monospace;\">\n<h2 style=\"font-family:sans-serif;color:#fff;margin:0 0 8px 0;\">🤖 Provider Interface & Multi-Provider Routing</h2>\n<p style=\"color:#9ca3af;margin:0 0 20px 0;\">gvShell's abstraction layer for AI model access</p>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:8px;margin-bottom:20px;\">\n<h3 style=\"color:#3b82f6;margin:0 0 12px 0;font-family:sans-serif;\">📝 Provider Interface</h3>\n<pre style=\"margin:0;line-height:1.6;overflow-x:auto;\"><span style=\"color:#c678dd;\">type</span> <span style=\"color:#e5c07b;\">Provider</span> <span style=\"color:#c678dd;\">interface</span> {\n <span style=\"color:#7f848e;\">// Name returns the provider identifier (e.g., \"claude\", \"gpt\")</span>\n <span style=\"color:#61afef;\">Name</span>() <span style=\"color:#98c379;\">string</span>\n\n <span style=\"color:#7f848e;\">// Send sends a prompt and returns the response</span>\n <span style=\"color:#61afef;\">Send</span>(ctx <span style=\"color:#e5c07b;\">context.Context</span>, prompt <span style=\"color:#98c379;\">string</span>) (<span style=\"color:#e5c07b;\">Response</span>, <span style=\"color:#98c379;\">error</span>)\n\n <span style=\"color:#7f848e;\">// Stream sends a prompt and streams the response</span>\n <span style=\"color:#61afef;\">Stream</span>(ctx <span style=\"color:#e5c07b;\">context.Context</span>, prompt <span style=\"color:#98c379;\">string</span>) (<span style=\"color:#c678dd;\"><-chan</span> <span style=\"color:#e5c07b;\">StreamEvent</span>, <span style=\"color:#98c379;\">error</span>)\n}</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:8px;margin-bottom:20px;\">\n<h3 style=\"color:#22c55e;margin:0 0 12px 0;font-family:sans-serif;\">📦 Response & StreamEvent Types</h3>\n<pre style=\"margin:0;line-height:1.6;overflow-x:auto;\"><span style=\"color:#c678dd;\">type</span> <span style=\"color:#e5c07b;\">Response</span> <span style=\"color:#c678dd;\">struct</span> {\n Content <span style=\"color:#98c379;\">string</span> <span style=\"color:#7f848e;\">// AI response text</span>\n Model <span style=\"color:#98c379;\">string</span> <span style=\"color:#7f848e;\">// Model used (e.g., \"claude-sonnet-4\")</span>\n TokensIn <span style=\"color:#98c379;\">int</span> <span style=\"color:#7f848e;\">// Input tokens consumed</span>\n TokensOut <span style=\"color:#98c379;\">int</span> <span style=\"color:#7f848e;\">// Output tokens generated</span>\n DurationMs <span style=\"color:#98c379;\">int64</span> <span style=\"color:#7f848e;\">// Request duration</span>\n}\n\n<span style=\"color:#c678dd;\">type</span> <span style=\"color:#e5c07b;\">StreamEvent</span> <span style=\"color:#c678dd;\">struct</span> {\n Type <span style=\"color:#98c379;\">string</span> <span style=\"color:#7f848e;\">// \"text\", \"done\", \"error\"</span>\n Content <span style=\"color:#98c379;\">string</span> <span style=\"color:#7f848e;\">// Chunk content</span>\n Error <span style=\"color:#98c379;\">error</span> <span style=\"color:#7f848e;\">// Error if Type==\"error\"</span>\n}</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:8px;margin-bottom:20px;\">\n<h3 style=\"color:#8b5cf6;margin:0 0 12px 0;font-family:sans-serif;\">🗂️ Provider Types</h3>\n<table style=\"width:100%;border-collapse:collapse;font-size:0.9rem;\">\n<tr style=\"background:#1e1e1e;\">\n <th style=\"text-align:left;padding:10px;color:#8b5cf6;border-bottom:1px solid #444;\">Type</th>\n <th style=\"text-align:left;padding:10px;color:#8b5cf6;border-bottom:1px solid #444;\">Description</th>\n <th style=\"text-align:left;padding:10px;color:#8b5cf6;border-bottom:1px solid #444;\">Providers</th>\n</tr>\n<tr style=\"background:#252525;\">\n <td style=\"padding:10px;\"><code style=\"color:#3b82f6;\">TypeAPI</code></td>\n <td style=\"padding:10px;color:#9ca3af;\">Direct HTTP API calls (stateless, function calling only)</td>\n <td style=\"padding:10px;\">claude, openai, gemini, ollama</td>\n</tr>\n<tr style=\"background:#1e1e1e;\">\n <td style=\"padding:10px;\"><code style=\"color:#22c55e;\">TypeCLI</code></td>\n <td style=\"padding:10px;color:#9ca3af;\">Spawn subprocess (full tool access, persistent context)</td>\n <td style=\"padding:10px;\">claude-cli, codex</td>\n</tr>\n</table>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:8px;margin-bottom:20px;\">\n<h3 style=\"color:#f59e0b;margin:0 0 12px 0;font-family:sans-serif;\">🚦 @Provider Routing</h3>\n<p style=\"color:#9ca3af;margin:0 0 12px 0;font-family:sans-serif;\">Users can target specific providers with prefix syntax:</p>\n<pre style=\"margin:0;line-height:1.8;overflow-x:auto;\"><span style=\"color:#7f848e;\">// Format: @provider prompt or @provider:prompt</span>\n\n<span style=\"color:#98c379;\">\"@claude explain quantum computing\"</span> <span style=\"color:#7f848e;\">// → Claude API</span>\n<span style=\"color:#98c379;\">\"@gpt4 write a haiku about code\"</span> <span style=\"color:#7f848e;\">// → OpenAI (via alias)</span>\n<span style=\"color:#98c379;\">\"@ollama:summarize this file\"</span> <span style=\"color:#7f848e;\">// → Local Ollama</span>\n<span style=\"color:#98c379;\">\"@cc fix this bug\"</span> <span style=\"color:#7f848e;\">// → Claude CLI (alias)</span>\n<span style=\"color:#98c379;\">\"what is 2+2?\"</span> <span style=\"color:#7f848e;\">// → Default provider</span></pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:8px;margin-bottom:20px;\">\n<h3 style=\"color:#ef4444;margin:0 0 12px 0;font-family:sans-serif;\">📚 Standard Providers</h3>\n<table style=\"width:100%;border-collapse:collapse;font-size:0.85rem;\">\n<tr style=\"background:#1e1e1e;\">\n <th style=\"text-align:left;padding:8px;color:#ef4444;border-bottom:1px solid #444;\">Name</th>\n <th style=\"text-align:left;padding:8px;color:#ef4444;border-bottom:1px solid #444;\">Type</th>\n <th style=\"text-align:left;padding:8px;color:#ef4444;border-bottom:1px solid #444;\">Aliases</th>\n <th style=\"text-align:left;padding:8px;color:#ef4444;border-bottom:1px solid #444;\">Env Var</th>\n</tr>\n<tr style=\"background:#252525;\">\n <td style=\"padding:8px;\"><code style=\"color:#8b5cf6;\">claude</code></td>\n <td style=\"padding:8px;\">API</td>\n <td style=\"padding:8px;color:#7f848e;\">anthropic, sonnet, opus, haiku</td>\n <td style=\"padding:8px;\"><code style=\"color:#e5c07b;\">ANTHROPIC_API_KEY</code></td>\n</tr>\n<tr style=\"background:#1e1e1e;\">\n <td style=\"padding:8px;\"><code style=\"color:#22c55e;\">claude-cli</code></td>\n <td style=\"padding:8px;\">CLI</td>\n <td style=\"padding:8px;color:#7f848e;\">claudecli, cc</td>\n <td style=\"padding:8px;\"><code style=\"color:#e5c07b;\">ANTHROPIC_API_KEY</code></td>\n</tr>\n<tr style=\"background:#252525;\">\n <td style=\"padding:8px;\"><code style=\"color:#8b5cf6;\">openai</code></td>\n <td style=\"padding:8px;\">API</td>\n <td style=\"padding:8px;color:#7f848e;\">gpt, gpt4, gpt4o, chatgpt, o1, o3</td>\n <td style=\"padding:8px;\"><code style=\"color:#e5c07b;\">OPENAI_API_KEY</code></td>\n</tr>\n<tr style=\"background:#1e1e1e;\">\n <td style=\"padding:8px;\"><code style=\"color:#22c55e;\">codex</code></td>\n <td style=\"padding:8px;\">CLI</td>\n <td style=\"padding:8px;color:#7f848e;\">codex-cli, codexcli</td>\n <td style=\"padding:8px;\"><code style=\"color:#e5c07b;\">OPENAI_API_KEY</code></td>\n</tr>\n<tr style=\"background:#252525;\">\n <td style=\"padding:8px;\"><code style=\"color:#8b5cf6;\">gemini</code></td>\n <td style=\"padding:8px;\">API</td>\n <td style=\"padding:8px;color:#7f848e;\">google, flash, pro</td>\n <td style=\"padding:8px;\"><code style=\"color:#e5c07b;\">GOOGLE_API_KEY</code></td>\n</tr>\n<tr style=\"background:#1e1e1e;\">\n <td style=\"padding:8px;\"><code style=\"color:#8b5cf6;\">ollama</code></td>\n <td style=\"padding:8px;\">API</td>\n <td style=\"padding:8px;color:#7f848e;\">local, llama, mistral</td>\n <td style=\"padding:8px;color:#7f848e;\">(none - local)</td>\n</tr>\n</table>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:8px;margin-bottom:20px;\">\n<h3 style=\"color:#06b6d4;margin:0 0 12px 0;font-family:sans-serif;\">🔄 Registry Flow</h3>\n<pre style=\"margin:0;line-height:1.6;overflow-x:auto;color:#e0e0e0;\">\n<span style=\"color:#3b82f6;\">┌───────────────┐</span>\n<span style=\"color:#3b82f6;\">│</span> User Prompt <span style=\"color:#3b82f6;\">│</span> <span style=\"color:#98c379;\">\"@claude hello\"</span>\n<span style=\"color:#3b82f6;\">└───────┬───────┘</span>\n │\n ▼\n<span style=\"color:#8b5cf6;\">┌─────────────────────────┐</span>\n<span style=\"color:#8b5cf6;\">│</span> ParseProviderPrefix() <span style=\"color:#8b5cf6;\">│</span> Extract <span style=\"color:#e5c07b;\">@provider</span> prefix\n<span style=\"color:#8b5cf6;\">└────────────┬────────────┘</span>\n │\n ▼\n<span style=\"color:#22c55e;\">┌─────────────────────────┐</span>\n<span style=\"color:#22c55e;\">│</span> Registry.Get(name) <span style=\"color:#22c55e;\">│</span> Resolve alias → factory()\n<span style=\"color:#22c55e;\">└────────────┬────────────┘</span>\n │\n ▼\n<span style=\"color:#f59e0b;\">┌─────────────────────────┐</span>\n<span style=\"color:#f59e0b;\">│</span> Provider.Stream(prompt) <span style=\"color:#f59e0b;\">│</span> Send to AI model\n<span style=\"color:#f59e0b;\">└────────────┬────────────┘</span>\n │\n ▼\n<span style=\"color:#ef4444;\">┌───────────────┐</span>\n<span style=\"color:#ef4444;\">│</span> StreamEvents <span style=\"color:#ef4444;\">│</span> text → text → done\n<span style=\"color:#ef4444;\">└───────────────┘</span>\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:8px;margin-bottom:20px;\">\n<h3 style=\"color:#ec4899;margin:0 0 12px 0;font-family:sans-serif;\">⚡ Key Registry Methods</h3>\n<pre style=\"margin:0;line-height:1.6;overflow-x:auto;\"><span style=\"color:#7f848e;\">// Registration</span>\nregistry.<span style=\"color:#61afef;\">Register</span>(info, factory) <span style=\"color:#7f848e;\">// Add provider</span>\nregistry.<span style=\"color:#61afef;\">Unregister</span>(name) <span style=\"color:#7f848e;\">// Remove provider</span>\n\n<span style=\"color:#7f848e;\">// Lookup</span>\nregistry.<span style=\"color:#61afef;\">Get</span>(name) <span style=\"color:#7f848e;\">// Get by name/alias</span>\nregistry.<span style=\"color:#61afef;\">GetDefault</span>() <span style=\"color:#7f848e;\">// Get default provider</span>\nregistry.<span style=\"color:#61afef;\">GetWithFallback</span>() <span style=\"color:#7f848e;\">// Try fallback chain</span>\n\n<span style=\"color:#7f848e;\">// Routing</span>\nregistry.<span style=\"color:#61afef;\">RoutePrompt</span>(prompt) <span style=\"color:#7f848e;\">// Parse @prefix, return provider</span>\nregistry.<span style=\"color:#61afef;\">SendWithRouting</span>(ctx, p) <span style=\"color:#7f848e;\">// Route + send (sync)</span>\nregistry.<span style=\"color:#61afef;\">StreamWithRouting</span>(ctx, p) <span style=\"color:#7f848e;\">// Route + stream</span>\n\n<span style=\"color:#7f848e;\">// Configuration</span>\nregistry.<span style=\"color:#61afef;\">SetDefault</span>(name) <span style=\"color:#7f848e;\">// Set default provider</span>\nregistry.<span style=\"color:#61afef;\">SetFallbackChain</span>(names) <span style=\"color:#7f848e;\">// Set fallback order</span>\nregistry.<span style=\"color:#61afef;\">ListByType</span>(TypeCLI) <span style=\"color:#7f848e;\">// Filter by type</span></pre>\n</div>\n\n<div style=\"background:#1e3a5f;border:2px solid #3b82f6;padding:16px;border-radius:8px;font-family:sans-serif;\">\n<h4 style=\"color:#3b82f6;margin:0 0 8px 0;\">💡 Design Highlights</h4>\n<ul style=\"color:#e0e0e0;margin:0;padding-left:20px;line-height:1.8;\">\n<li><strong>Factory pattern</strong> - Providers created on-demand via <code>ProviderFactory</code></li>\n<li><strong>Thread-safe</strong> - <code>sync.RWMutex</code> protects all registry operations</li>\n<li><strong>Alias resolution</strong> - Case-insensitive, supports multiple aliases per provider</li>\n<li><strong>Fallback chains</strong> - Automatic failover when primary provider unavailable</li>\n<li><strong>CLI vs API split</strong> - CLI providers have full tool access, API providers are stateless</li>\n</ul>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-top:24px;font-family:sans-serif;\">\n<h4 style=\"margin:0 0 8px 0;color:#fff;\">What's Next?</h4>\n<p style=\"color:#9ca3af;margin:0;\">Choose an action:</p>\n</div>\n</div>",
"choices": [
{
"label": "Claude Provider",
"value": "Show the Claude provider implementation details",
"primary": true
},
{
"label": "CLI vs API Deep Dive",
"value": "Explain the differences between CLI and API providers in detail"
},
{
"label": "Fallback Chains",
"value": "Show how provider fallback chains work with examples"
}
],
"requestedAt": "2026-01-07T10:45:00Z",
"requestId": "dd378b67-4cfc-47ae-a01f-b0fe70795adf",
"turnTiming": {
"totalMs": 86419,
"ttfeMs": 48,
"thinkingMs": 86371,
"toolExecutionMs": 0,
"toolCallCount": 0,
"thinkingPct": 100,
"toolsPct": 0,
"ttfePct": 0
}
},
"createdBy": "claude",
"createdAt": "2026-01-07T13:29:57.590Z",
"updatedAt": "2026-01-07T13:30:04.786Z",
"requestId": "dd378b67-4cfc-47ae-a01f-b0fe70795adf",
"scope": "vibetools",
"tags": [
"gvshell",
"providers",
"architecture",
"api"
],
"targetUser": "claude"
}