mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
feat(provider): add Alibaba Coding Plan and regional Qwen endpoints (#1748)
* feat(provider): add Alibaba Coding Plan and regional Qwen endpoints - Add Alibaba Coding Plan provider with OpenAI-compatible endpoint (https://coding-intl.dashscope.aliyuncs.com/v1) - Add Coding Plan Anthropic-compatible endpoint (https://coding-intl.dashscope.aliyuncs.com/apps/anthropic) - Add regional Qwen endpoints (qwen-intl, qwen-us) - Add provider aliases: coding-plan, alibaba-coding, qwen-coding - Normalize provider names for coding-plan variants 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(provider): add reviewer-requested fixes for Alibaba Coding Plan - Add qwen-international, dashscope-intl, dashscope-us aliases to switch case - Add coding-plan-anthropic case with anthropicmessages.NewProviderWithTimeout - Add alibaba-coding-anthropic -> coding-plan-anthropic normalization - Add qwen-international -> qwen-intl and dashscope-us -> qwen-us normalization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test(provider): add tests for Alibaba Coding Plan protocol aliases - Add tests for qwen-international, dashscope-intl, dashscope-us aliases - Add tests for coding-plan-anthropic and alibaba-coding-anthropic - Add getDefaultAPIBase tests for all new aliases - Add normalization tests for new provider aliases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -115,8 +115,9 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
|
||||
|
||||
case "litellm", "openrouter", "groq", "zhipu", "gemini", "nvidia",
|
||||
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
|
||||
"vivgrid", "volcengine", "vllm", "qwen", "mistral", "avian",
|
||||
"minimax", "longcat", "modelscope", "novita":
|
||||
"vivgrid", "volcengine", "vllm", "qwen", "qwen-intl", "qwen-international", "dashscope-intl",
|
||||
"qwen-us", "dashscope-us", "mistral", "avian", "minimax", "longcat", "modelscope", "novita",
|
||||
"coding-plan", "alibaba-coding", "qwen-coding":
|
||||
// All other 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)
|
||||
@@ -173,6 +174,21 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
|
||||
cfg.RequestTimeout,
|
||||
), modelID, nil
|
||||
|
||||
case "coding-plan-anthropic", "alibaba-coding-anthropic":
|
||||
// Alibaba Coding Plan with Anthropic-compatible API
|
||||
apiBase := cfg.APIBase
|
||||
if apiBase == "" {
|
||||
apiBase = getDefaultAPIBase(protocol)
|
||||
}
|
||||
if cfg.APIKey == "" {
|
||||
return nil, "", fmt.Errorf("api_key is required for %q protocol (model: %s)", protocol, cfg.Model)
|
||||
}
|
||||
return anthropicmessages.NewProviderWithTimeout(
|
||||
cfg.APIKey,
|
||||
apiBase,
|
||||
cfg.RequestTimeout,
|
||||
), modelID, nil
|
||||
|
||||
case "antigravity":
|
||||
return NewAntigravityProvider(), modelID, nil
|
||||
|
||||
@@ -245,6 +261,14 @@ func getDefaultAPIBase(protocol string) string {
|
||||
return "https://ark.cn-beijing.volces.com/api/v3"
|
||||
case "qwen":
|
||||
return "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
case "qwen-intl", "qwen-international", "dashscope-intl":
|
||||
return "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
||||
case "qwen-us", "dashscope-us":
|
||||
return "https://dashscope-us.aliyuncs.com/compatible-mode/v1"
|
||||
case "coding-plan", "alibaba-coding", "qwen-coding":
|
||||
return "https://coding-intl.dashscope.aliyuncs.com/v1"
|
||||
case "coding-plan-anthropic", "alibaba-coding-anthropic":
|
||||
return "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic"
|
||||
case "vllm":
|
||||
return "http://localhost:8000/v1"
|
||||
case "mistral":
|
||||
|
||||
@@ -472,3 +472,134 @@ func TestCreateProviderFromConfig_AzureMissingAPIBase(t *testing.T) {
|
||||
t.Fatal("CreateProviderFromConfig() expected error for missing API base")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateProviderFromConfig_QwenInternationalAlias(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
protocol string
|
||||
}{
|
||||
{"qwen-international", "qwen-international"},
|
||||
{"dashscope-intl", "dashscope-intl"},
|
||||
{"qwen-intl", "qwen-intl"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &config.ModelConfig{
|
||||
ModelName: "test-" + tt.protocol,
|
||||
Model: tt.protocol + "/qwen-max",
|
||||
APIKey: "test-key",
|
||||
}
|
||||
|
||||
provider, modelID, err := CreateProviderFromConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
||||
}
|
||||
if provider == nil {
|
||||
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
||||
}
|
||||
if modelID != "qwen-max" {
|
||||
t.Errorf("modelID = %q, want %q", modelID, "qwen-max")
|
||||
}
|
||||
if _, ok := provider.(*HTTPProvider); !ok {
|
||||
t.Fatalf("expected *HTTPProvider, got %T", provider)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateProviderFromConfig_QwenUSAlias(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
protocol string
|
||||
}{
|
||||
{"qwen-us", "qwen-us"},
|
||||
{"dashscope-us", "dashscope-us"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &config.ModelConfig{
|
||||
ModelName: "test-" + tt.protocol,
|
||||
Model: tt.protocol + "/qwen-max",
|
||||
APIKey: "test-key",
|
||||
}
|
||||
|
||||
provider, modelID, err := CreateProviderFromConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
||||
}
|
||||
if provider == nil {
|
||||
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
||||
}
|
||||
if modelID != "qwen-max" {
|
||||
t.Errorf("modelID = %q, want %q", modelID, "qwen-max")
|
||||
}
|
||||
if _, ok := provider.(*HTTPProvider); !ok {
|
||||
t.Fatalf("expected *HTTPProvider, got %T", provider)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateProviderFromConfig_CodingPlanAnthropic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
protocol string
|
||||
}{
|
||||
{"coding-plan-anthropic", "coding-plan-anthropic"},
|
||||
{"alibaba-coding-anthropic", "alibaba-coding-anthropic"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &config.ModelConfig{
|
||||
ModelName: "test-" + tt.protocol,
|
||||
Model: tt.protocol + "/claude-sonnet-4-20250514",
|
||||
APIKey: "test-key",
|
||||
}
|
||||
|
||||
provider, modelID, err := CreateProviderFromConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateProviderFromConfig() error = %v", err)
|
||||
}
|
||||
if provider == nil {
|
||||
t.Fatal("CreateProviderFromConfig() returned nil provider")
|
||||
}
|
||||
if modelID != "claude-sonnet-4-20250514" {
|
||||
t.Errorf("modelID = %q, want %q", modelID, "claude-sonnet-4-20250514")
|
||||
}
|
||||
// coding-plan-anthropic uses Anthropic Messages provider
|
||||
// Verify it's the anthropic messages provider by checking interface
|
||||
var _ LLMProvider = provider
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultAPIBase_CodingPlanAnthropic(t *testing.T) {
|
||||
expectedURL := "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic"
|
||||
if got := getDefaultAPIBase("coding-plan-anthropic"); got != expectedURL {
|
||||
t.Fatalf("getDefaultAPIBase(%q) = %q, want %q", "coding-plan-anthropic", got, expectedURL)
|
||||
}
|
||||
if got := getDefaultAPIBase("alibaba-coding-anthropic"); got != expectedURL {
|
||||
t.Fatalf("getDefaultAPIBase(%q) = %q, want %q", "alibaba-coding-anthropic", got, expectedURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultAPIBase_QwenIntlAliases(t *testing.T) {
|
||||
expectedURL := "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
||||
for _, protocol := range []string{"qwen-intl", "qwen-international", "dashscope-intl"} {
|
||||
if got := getDefaultAPIBase(protocol); got != expectedURL {
|
||||
t.Fatalf("getDefaultAPIBase(%q) = %q, want %q", protocol, got, expectedURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultAPIBase_QwenUSAliases(t *testing.T) {
|
||||
expectedURL := "https://dashscope-us.aliyuncs.com/compatible-mode/v1"
|
||||
for _, protocol := range []string{"qwen-us", "dashscope-us"} {
|
||||
if got := getDefaultAPIBase(protocol); got != expectedURL {
|
||||
t.Fatalf("getDefaultAPIBase(%q) = %q, want %q", protocol, got, expectedURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,14 @@ func NormalizeProvider(provider string) string {
|
||||
return "zhipu"
|
||||
case "google":
|
||||
return "gemini"
|
||||
case "alibaba-coding", "qwen-coding":
|
||||
return "coding-plan"
|
||||
case "alibaba-coding-anthropic":
|
||||
return "coding-plan-anthropic"
|
||||
case "qwen-international", "dashscope-intl":
|
||||
return "qwen-intl"
|
||||
case "dashscope-us":
|
||||
return "qwen-us"
|
||||
}
|
||||
|
||||
return p
|
||||
|
||||
@@ -73,6 +73,14 @@ func TestNormalizeProvider(t *testing.T) {
|
||||
{"glm", "zhipu"},
|
||||
{"google", "gemini"},
|
||||
{"groq", "groq"},
|
||||
// Alibaba Coding Plan aliases
|
||||
{"alibaba-coding", "coding-plan"},
|
||||
{"qwen-coding", "coding-plan"},
|
||||
{"alibaba-coding-anthropic", "coding-plan-anthropic"},
|
||||
// Qwen international aliases
|
||||
{"qwen-international", "qwen-intl"},
|
||||
{"dashscope-intl", "qwen-intl"},
|
||||
{"dashscope-us", "qwen-us"},
|
||||
{"", ""},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user