Skip to content

Commit ccc33e8

Browse files
committed
feat: implement zhipu and alibaba providers
1 parent 37ec616 commit ccc33e8

File tree

9 files changed

+266
-58
lines changed

9 files changed

+266
-58
lines changed

frontend/src/components/pages/models.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,26 @@ const LOCAL_PROVIDER_CONFIGS: Record<string, Partial<ProviderConfig>> = {
110110
category: ["llm", "embedding"],
111111
defaultBaseUrl: "https://api.openai.com/v1"
112112
},
113+
"zai-coding-plan": {
114+
icon: <img src="/zhipu.svg" alt="Z.AI" className="w-6 h-6" />,
115+
category: ["llm"],
116+
defaultBaseUrl: "https://api.z.ai/api/coding/paas/v4"
117+
},
118+
"zhipuai-coding-plan": {
119+
icon: <img src="/zhipu.svg" alt="Zhipu" className="w-6 h-6" />,
120+
category: ["llm"],
121+
defaultBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4"
122+
},
123+
"alibaba-coding-plan": {
124+
icon: <img src="/dashscope.png" alt="Alibaba Bailian" className="w-6 h-6" />,
125+
category: ["llm"],
126+
defaultBaseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1"
127+
},
128+
"alibaba-coding-plan-cn": {
129+
icon: <img src="/dashscope.png" alt="Alibaba Bailian" className="w-6 h-6" />,
130+
category: ["llm"],
131+
defaultBaseUrl: "https://coding.dashscope.aliyuncs.com/v1"
132+
},
113133
azure_openai: {
114134
icon: <Zap className="w-6 h-6 text-blue-500" />,
115135
category: ["llm"]

frontend/src/i18n/locales/en.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,18 @@ Build when you need.`
12421242
openai: {
12431243
description: "Access GPT-4o, GPT-4 Turbo, and DALL-E 3 models. Industry standard for reasoning and creativity.",
12441244
},
1245+
"zai-coding-plan": {
1246+
description: "GLM Coding Plan endpoints via Z.AI.",
1247+
},
1248+
"zhipuai-coding-plan": {
1249+
description: "GLM Coding Plan endpoints via Zhipu AI.",
1250+
},
1251+
"alibaba-coding-plan": {
1252+
description: "Alibaba Bailian Coding Plan endpoint.",
1253+
},
1254+
"alibaba-coding-plan-cn": {
1255+
description: "Alibaba Bailian Coding Plan endpoint (China).",
1256+
},
12451257
azure_openai: {
12461258
description: "Enterprise-grade OpenAI models hosted on Azure.",
12471259
},

frontend/src/i18n/locales/zh.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,18 @@ Build when you need.`
12421242
openai: {
12431243
description: "访问 GPT-4o、GPT-4 Turbo 和 DALL-E 3 模型。推理和创造力的行业标准。",
12441244
},
1245+
"zai-coding-plan": {
1246+
description: "Z.AI GLM Coding Plan 端点。",
1247+
},
1248+
"zhipuai-coding-plan": {
1249+
description: "智谱 GLM Coding Plan 端点(中国区)。",
1250+
},
1251+
"alibaba-coding-plan": {
1252+
description: "阿里云百炼 Coding Plan 端点。",
1253+
},
1254+
"alibaba-coding-plan-cn": {
1255+
description: "阿里云百炼 Coding Plan 端点(中国区)。",
1256+
},
12451257
azure_openai: {
12461258
description: "Azure 托管的企业级 OpenAI 模型。",
12471259
},

src/xagent/core/model/chat/basic/adapter.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import os
22

33
from ....model import ChatModelConfig, ModelConfig
4+
from ....model.providers import (
5+
default_base_url_for_provider,
6+
provider_compatibility_for_provider,
7+
)
48
from ....retry import create_retry_wrapper
59
from ..error import retry_on
610
from .azure_openai import AzureOpenAILLM
@@ -19,11 +23,25 @@ def create_base_llm(model: ModelConfig) -> BaseLLM:
1923
if not isinstance(model, ChatModelConfig):
2024
raise TypeError(f"Invalid model type: {type(model).__name__}")
2125

22-
if model.model_provider == "openai":
26+
compatibility = provider_compatibility_for_provider(model.model_provider)
27+
28+
if compatibility == "openai_compatible":
2329
llm: BaseLLM = OpenAILLM(
2430
model_name=model.model_name,
2531
api_key=model.api_key,
26-
base_url=model.base_url,
32+
base_url=model.base_url
33+
or default_base_url_for_provider(model.model_provider),
34+
default_temperature=model.default_temperature,
35+
default_max_tokens=model.default_max_tokens,
36+
timeout=model.timeout,
37+
abilities=model.abilities,
38+
)
39+
elif compatibility == "claude_compatible":
40+
llm = ClaudeLLM(
41+
model_name=model.model_name,
42+
api_key=model.api_key,
43+
base_url=model.base_url
44+
or default_base_url_for_provider(model.model_provider),
2745
default_temperature=model.default_temperature,
2846
default_max_tokens=model.default_max_tokens,
2947
timeout=model.timeout,
@@ -60,16 +78,6 @@ def create_base_llm(model: ModelConfig) -> BaseLLM:
6078
timeout=model.timeout,
6179
abilities=model.abilities,
6280
)
63-
elif model.model_provider == "claude":
64-
llm = ClaudeLLM(
65-
model_name=model.model_name,
66-
api_key=model.api_key,
67-
base_url=model.base_url,
68-
default_temperature=model.default_temperature,
69-
default_max_tokens=model.default_max_tokens,
70-
timeout=model.timeout,
71-
abilities=model.abilities,
72-
)
7381
elif model.model_provider == "xinference":
7482
llm = XinferenceLLM(
7583
model_name=model.model_name,

src/xagent/core/model/chat/langchain.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
from langchain_openai import AzureChatOpenAI, ChatOpenAI
1111

1212
from ...model import ChatModelConfig, ModelConfig
13+
from ...model.providers import (
14+
default_base_url_for_provider,
15+
provider_compatibility_for_provider,
16+
)
1317
from ...retry import ExponentialBackoff, RetryStrategy, create_retry_wrapper
1418
from .error import retry_on
1519

@@ -97,14 +101,16 @@ def create_base_chat_model(
97101
raise TypeError(f"Unsupported Chat model type: {type(model).__name__}")
98102

99103
temp = temperature if temperature is not None else model.default_temperature
104+
compatibility = provider_compatibility_for_provider(model.model_provider)
100105

101-
if model.model_provider == "openai":
106+
if compatibility == "openai_compatible":
102107
return ChatOpenAI(
103108
model=model.model_name,
104109
temperature=temp,
105110
max_tokens=model.default_max_tokens,
106111
api_key=model.api_key,
107-
base_url=model.base_url,
112+
base_url=model.base_url
113+
or default_base_url_for_provider(model.model_provider),
108114
timeout=model.timeout,
109115
)
110116
elif model.model_provider == "zhipu":

src/xagent/core/model/providers.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
from typing import Any, Optional
2+
3+
_PROVIDER_ALIASES: dict[str, str] = {
4+
"zai_coding_plan": "zai-coding-plan",
5+
"zhipuai_coding_plan": "zhipuai-coding-plan",
6+
"alibaba_coding_plan": "alibaba-coding-plan",
7+
"alibaba_coding_plan_cn": "alibaba-coding-plan-cn",
8+
}
9+
10+
# Provider default base URLs used when callers omit an explicit base URL.
11+
_DEFAULT_BASE_URL_BY_PROVIDER: dict[str, str] = {
12+
"openai": "https://api.openai.com/v1",
13+
"zhipu": "https://open.bigmodel.cn/api/paas/v4",
14+
"dashscope": "https://dashscope.aliyuncs.com/compatible-mode/v1",
15+
# Opencode / models.dev naming
16+
"zai-coding-plan": "https://api.z.ai/api/coding/paas/v4",
17+
"zhipuai-coding-plan": "https://open.bigmodel.cn/api/coding/paas/v4",
18+
# Alibaba Bailian (Model Studio) coding plan
19+
"alibaba-coding-plan": "https://coding-intl.dashscope.aliyuncs.com/v1",
20+
"alibaba-coding-plan-cn": "https://coding.dashscope.aliyuncs.com/v1",
21+
}
22+
23+
_CURATED_MODELS_BY_PROVIDER: dict[str, tuple[str, ...]] = {
24+
"alibaba-coding-plan": (
25+
"glm-4.7",
26+
"glm-5",
27+
"qwen3-coder-next",
28+
"qwen3-coder-plus",
29+
"qwen3-max-2026-01-23",
30+
"qwen3.5-plus",
31+
),
32+
"alibaba-coding-plan-cn": (
33+
"glm-4.7",
34+
"glm-5",
35+
"qwen3-coder-next",
36+
"qwen3-coder-plus",
37+
"qwen3-max-2026-01-23",
38+
"qwen3.5-plus",
39+
),
40+
}
41+
42+
_SUPPORTED_PROVIDER_METADATA: tuple[dict[str, Any], ...] = (
43+
{
44+
"id": "openai",
45+
"name": "OpenAI",
46+
"description": "OpenAI API compatible models",
47+
"requires_base_url": False,
48+
"compatibility": "openai_compatible",
49+
},
50+
{
51+
"id": "zai-coding-plan",
52+
"name": "Z.AI Coding Plan",
53+
"description": "GLM coding plan via Z.AI",
54+
"requires_base_url": False,
55+
"compatibility": "openai_compatible",
56+
},
57+
{
58+
"id": "zhipuai-coding-plan",
59+
"name": "Zhipu AI Coding Plan",
60+
"description": "GLM coding plan via Zhipu AI",
61+
"requires_base_url": False,
62+
"compatibility": "openai_compatible",
63+
},
64+
{
65+
"id": "alibaba-coding-plan",
66+
"name": "Alibaba Coding Plan",
67+
"description": "Alibaba Bailian (Model Studio) coding plan",
68+
"requires_base_url": False,
69+
"compatibility": "openai_compatible",
70+
},
71+
{
72+
"id": "alibaba-coding-plan-cn",
73+
"name": "Alibaba Coding Plan (China)",
74+
"description": "Alibaba Bailian (Model Studio) coding plan (China)",
75+
"requires_base_url": False,
76+
"compatibility": "openai_compatible",
77+
},
78+
{
79+
"id": "claude",
80+
"name": "Anthropic Claude",
81+
"description": "Anthropic's Claude models",
82+
"requires_base_url": False,
83+
"compatibility": "claude_compatible",
84+
},
85+
{
86+
"id": "gemini",
87+
"name": "Google Gemini",
88+
"description": "Google's Gemini models",
89+
"requires_base_url": False,
90+
},
91+
{
92+
"id": "xinference",
93+
"name": "Xinference",
94+
"description": "Xinference models for local inference",
95+
"requires_base_url": True,
96+
},
97+
{
98+
"id": "zhipu",
99+
"name": "Zhipu AI",
100+
"description": "Zhipu AI models (GLM series) using zai SDK",
101+
"requires_base_url": False,
102+
},
103+
{
104+
"id": "dashscope",
105+
"name": "DashScope",
106+
"description": "Alibaba Cloud's DashScope models",
107+
"requires_base_url": False,
108+
},
109+
)
110+
111+
112+
def _normalize_provider(provider: str) -> str:
113+
return provider.lower().strip()
114+
115+
116+
def canonical_provider_name(provider: str) -> str:
117+
normalized = _normalize_provider(provider)
118+
return _PROVIDER_ALIASES.get(normalized, normalized)
119+
120+
121+
def default_base_url_for_provider(provider: str) -> Optional[str]:
122+
return _DEFAULT_BASE_URL_BY_PROVIDER.get(canonical_provider_name(provider))
123+
124+
125+
def curated_models_for_provider(provider: str) -> tuple[str, ...]:
126+
return _CURATED_MODELS_BY_PROVIDER.get(canonical_provider_name(provider), ())
127+
128+
129+
def provider_compatibility_for_provider(provider: str) -> Optional[str]:
130+
provider_id = canonical_provider_name(provider)
131+
for provider_info in _SUPPORTED_PROVIDER_METADATA:
132+
if provider_info["id"] == provider_id:
133+
compatibility = provider_info.get("compatibility")
134+
return str(compatibility) if compatibility is not None else None
135+
return None
136+
137+
138+
def get_supported_provider_metadata() -> list[dict[str, Any]]:
139+
providers: list[dict[str, Any]] = []
140+
for provider in _SUPPORTED_PROVIDER_METADATA:
141+
provider_info = dict(provider)
142+
default_base_url = default_base_url_for_provider(provider_info["id"])
143+
if default_base_url is not None:
144+
provider_info["default_base_url"] = default_base_url
145+
providers.append(provider_info)
146+
return providers

src/xagent/web/api/model.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
ImageModelConfig,
1414
ModelConfig,
1515
)
16+
from xagent.core.model.providers import default_base_url_for_provider
1617
from xagent.core.utils.security import redact_sensitive_text
1718

1819
from ..auth_dependencies import get_current_user
@@ -59,12 +60,14 @@ async def create_model(
5960
detail="Only administrators can share models with all users",
6061
)
6162

63+
base_url = model.base_url or default_base_url_for_provider(model.model_provider)
64+
6265
if model.category == "llm":
6366
config: ModelConfig = ChatModelConfig(
6467
id=model.model_id,
6568
model_name=model.model_name,
6669
model_provider=model.model_provider,
67-
base_url=model.base_url,
70+
base_url=base_url,
6871
api_key=model.api_key,
6972
default_temperature=model.temperature,
7073
timeout=180.0,
@@ -76,7 +79,7 @@ async def create_model(
7679
id=model.model_id,
7780
model_name=model.model_name,
7881
model_provider=model.model_provider,
79-
base_url=model.base_url,
82+
base_url=base_url,
8083
api_key=model.api_key,
8184
timeout=180.0,
8285
abilities=model.abilities,
@@ -88,7 +91,7 @@ async def create_model(
8891
id=model.model_id,
8992
model_name=model.model_name,
9093
model_provider=model.model_provider,
91-
base_url=model.base_url,
94+
base_url=base_url,
9295
api_key=model.api_key,
9396
default_temperature=model.temperature,
9497
timeout=180.0,

src/xagent/web/schemas/model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ class ProviderInfo(BaseModel):
334334
name: str
335335
description: str
336336
requires_base_url: bool
337+
default_base_url: Optional[str] = None
338+
compatibility: Optional[str] = None
337339

338340

339341
class SupportedProvidersResponse(BaseModel):

0 commit comments

Comments
 (0)