mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
ef7078a356
Refactor command handlers into separate files to improve code organization and maintainability. Each command (agent, auth, cron, gateway, migrate, onboard, skills, status) now has its own dedicated file. Restructure provider creation to support new model_list configuration system that enables zero-code addition of OpenAI-compatible providers. Move legacy provider logic to separate file for backward compatibility. Move configuration functions from config.go to separate files (defaults.go, migration.go) for better organization.
137 lines
3.9 KiB
Go
137 lines
3.9 KiB
Go
// PicoClaw - Ultra-lightweight personal AI agent
|
|
// License: MIT
|
|
//
|
|
// Copyright (c) 2026 PicoClaw contributors
|
|
|
|
package providers
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/config"
|
|
)
|
|
|
|
// ExtractProtocol extracts the protocol prefix and model identifier from a model string.
|
|
// If no prefix is specified, it defaults to "openai".
|
|
// Examples:
|
|
// - "openai/gpt-4o" -> ("openai", "gpt-4o")
|
|
// - "anthropic/claude-3" -> ("anthropic", "claude-3")
|
|
// - "gpt-4o" -> ("openai", "gpt-4o") // default protocol
|
|
func ExtractProtocol(model string) (protocol, modelID string) {
|
|
model = strings.TrimSpace(model)
|
|
for i := 0; i < len(model); i++ {
|
|
if model[i] == '/' {
|
|
return model[:i], model[i+1:]
|
|
}
|
|
}
|
|
// No prefix found, default to openai
|
|
return "openai", model
|
|
}
|
|
|
|
// CreateProviderFromConfig creates a provider based on the ModelConfig.
|
|
// It uses the protocol prefix in the Model field to determine which provider to create.
|
|
// Supported protocols: openai, anthropic, antigravity, claude-cli, codex-cli, github-copilot
|
|
// Returns the provider, the model ID (without protocol prefix), and any error.
|
|
func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, error) {
|
|
if cfg == nil {
|
|
return nil, "", fmt.Errorf("config is nil")
|
|
}
|
|
|
|
if cfg.Model == "" {
|
|
return nil, "", fmt.Errorf("model is required")
|
|
}
|
|
|
|
protocol, modelID := ExtractProtocol(cfg.Model)
|
|
|
|
switch protocol {
|
|
case "openai", "openrouter", "groq", "zhipu", "gemini", "nvidia",
|
|
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
|
|
"volcengine", "vllm", "qwen":
|
|
// All OpenAI-compatible HTTP providers
|
|
if cfg.APIKey == "" && cfg.APIBase == "" {
|
|
return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol)
|
|
}
|
|
apiBase := cfg.APIBase
|
|
if apiBase == "" {
|
|
apiBase = getDefaultAPIBase(protocol)
|
|
}
|
|
return NewHTTPProvider(cfg.APIKey, apiBase, cfg.Proxy), modelID, nil
|
|
|
|
case "anthropic":
|
|
if cfg.AuthMethod == "oauth" || cfg.AuthMethod == "token" {
|
|
// Use Claude SDK with token
|
|
return NewClaudeProvider(cfg.APIKey), modelID, nil
|
|
}
|
|
// Use HTTP API
|
|
apiBase := cfg.APIBase
|
|
if apiBase == "" {
|
|
apiBase = "https://api.anthropic.com/v1"
|
|
}
|
|
return NewHTTPProvider(cfg.APIKey, apiBase, cfg.Proxy), modelID, nil
|
|
|
|
case "antigravity":
|
|
return NewAntigravityProvider(), modelID, nil
|
|
|
|
case "claude-cli", "claudecli":
|
|
workspace := "."
|
|
return NewClaudeCliProvider(workspace), modelID, nil
|
|
|
|
case "codex-cli", "codexcli":
|
|
workspace := "."
|
|
return NewCodexCliProvider(workspace), modelID, nil
|
|
|
|
case "github-copilot", "copilot":
|
|
apiBase := cfg.APIBase
|
|
if apiBase == "" {
|
|
apiBase = "localhost:4321"
|
|
}
|
|
connectMode := cfg.ConnectMode
|
|
if connectMode == "" {
|
|
connectMode = "grpc"
|
|
}
|
|
provider, err := NewGitHubCopilotProvider(apiBase, connectMode, modelID)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
return provider, modelID, nil
|
|
|
|
default:
|
|
return nil, "", fmt.Errorf("unknown protocol %q in model %q", protocol, cfg.Model)
|
|
}
|
|
}
|
|
|
|
// getDefaultAPIBase returns the default API base URL for a given protocol.
|
|
func getDefaultAPIBase(protocol string) string {
|
|
switch protocol {
|
|
case "openai":
|
|
return "https://api.openai.com/v1"
|
|
case "openrouter":
|
|
return "https://openrouter.ai/api/v1"
|
|
case "groq":
|
|
return "https://api.groq.com/openai/v1"
|
|
case "zhipu":
|
|
return "https://open.bigmodel.cn/api/paas/v4"
|
|
case "gemini":
|
|
return "https://generativelanguage.googleapis.com/v1beta"
|
|
case "nvidia":
|
|
return "https://integrate.api.nvidia.com/v1"
|
|
case "ollama":
|
|
return "http://localhost:11434/v1"
|
|
case "moonshot":
|
|
return "https://api.moonshot.cn/v1"
|
|
case "shengsuanyun":
|
|
return "https://router.shengsuanyun.com/api/v1"
|
|
case "deepseek":
|
|
return "https://api.deepseek.com/v1"
|
|
case "cerebras":
|
|
return "https://api.cerebras.ai/v1"
|
|
case "volcengine":
|
|
return "https://ark.cn-beijing.volces.com/api/v3"
|
|
case "qwen":
|
|
return "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|