feat(providers): support unknown protocols as OpenAI-compatible fallback
Treat unknown protocol prefixes (e.g., zai-org/GLM-4.7) as OpenAI-compatible HTTP providers instead of returning an error. The full model string is passed to the API for unknown protocols. Added tests for unknown protocol with API key, API base, and missing auth scenarios. Fixes #661 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7ea7bb0717
commit
dca32335ce
2 changed files with 51 additions and 4 deletions
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/sipeed/picoclaw/pkg/config"
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createClaudeAuthProvider creates a Claude provider using OAuth credentials from auth store.
|
// createClaudeAuthProvider creates a Claude provider using OAuth credentials from auth store.
|
||||||
|
|
@ -53,8 +54,8 @@ func ExtractProtocol(model string) (protocol, modelID string) {
|
||||||
|
|
||||||
// CreateProviderFromConfig creates a provider based on the ModelConfig.
|
// CreateProviderFromConfig creates a provider based on the ModelConfig.
|
||||||
// It uses the protocol prefix in the Model field to determine which provider to create.
|
// It uses the protocol prefix in the Model field to determine which provider to create.
|
||||||
// Supported protocols: openai, litellm, anthropic, antigravity, claude-cli, codex-cli, github-copilot
|
// Supported protocols: openai, litellm, anthropic, antigravity, claude-cli, codex-cli, github-copilot, and any OpenAI-compatible HTTP provider.
|
||||||
// Returns the provider, the model ID (without protocol prefix), and any error.
|
// Returns the provider, the model ID (full model string for unknown protocols, otherwise without protocol prefix), and any error.
|
||||||
func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, error) {
|
func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil, "", fmt.Errorf("config is nil")
|
return nil, "", fmt.Errorf("config is nil")
|
||||||
|
|
@ -169,7 +170,16 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
|
||||||
return provider, modelID, nil
|
return provider, modelID, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, "", fmt.Errorf("unknown protocol %q in model %q", protocol, cfg.Model)
|
// Treat unknown protocols as OpenAI-compatible
|
||||||
|
logger.WarnF("unknown protocol, falling back to OpenAI-compatible HTTP provider", map[string]any{"protocol": protocol, "model": cfg.Model})
|
||||||
|
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("openai")
|
||||||
|
}
|
||||||
|
return NewHTTPProviderWithMaxTokensFieldAndRequestTimeout(cfg.APIKey, apiBase, cfg.Proxy, cfg.MaxTokensField, cfg.RequestTimeout), cfg.Model, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -254,9 +254,46 @@ func TestCreateProviderFromConfig_UnknownProtocol(t *testing.T) {
|
||||||
APIKey: "test-key",
|
APIKey: "test-key",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider, modelID, err := CreateProviderFromConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateProviderFromConfig() unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if provider == nil {
|
||||||
|
t.Fatal("CreateProviderFromConfig() returned nil provider for unknown protocol with APIKey")
|
||||||
|
}
|
||||||
|
if modelID != cfg.Model {
|
||||||
|
t.Fatalf("CreateProviderFromConfig() expected modelID=%q, got %q", cfg.Model, modelID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateProviderFromConfig_UnknownProtocolWithAPIBase(t *testing.T) {
|
||||||
|
cfg := &config.ModelConfig{
|
||||||
|
ModelName: "test-unknown",
|
||||||
|
Model: "unknown-protocol/model",
|
||||||
|
APIBase: "http://localhost:8080/v1",
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, modelID, err := CreateProviderFromConfig(cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateProviderFromConfig() unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if provider == nil {
|
||||||
|
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
||||||
|
}
|
||||||
|
if modelID != cfg.Model {
|
||||||
|
t.Fatalf("CreateProviderFromConfig() expected modelID=%q, got %q", cfg.Model, modelID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateProviderFromConfig_UnknownProtocolNoAuth(t *testing.T) {
|
||||||
|
cfg := &config.ModelConfig{
|
||||||
|
ModelName: "test-unknown",
|
||||||
|
Model: "unknown-protocol/model",
|
||||||
|
}
|
||||||
|
|
||||||
_, _, err := CreateProviderFromConfig(cfg)
|
_, _, err := CreateProviderFromConfig(cfg)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("CreateProviderFromConfig() expected error for unknown protocol")
|
t.Fatal("CreateProviderFromConfig() expected error for unknown protocol without api_key or api_base")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue