diff --git a/coworker/providers/matrix.py b/coworker/providers/matrix.py index 052a19ff0..ea876d12c 100644 --- a/coworker/providers/matrix.py +++ b/coworker/providers/matrix.py @@ -161,6 +161,9 @@ class ModelEntry: ), ), "zai:glm-5.2": ModelEntry("GLM-5.2 · Z AI", _AGENTIC, 128_000), + "zai-coding:glm-5.2": ModelEntry("GLM-5.2 · Z AI Coding Plan", _AGENTIC, 128_000), + "zai-coding:glm-4.7": ModelEntry("GLM-4.7 · Z AI Coding Plan", _AGENTIC, 128_000), + "zai-coding:glm-4-coder": ModelEntry("GLM-4 Coder · Z AI Coding Plan", _AGENTIC, 128_000), "deepseek:deepseek-v4-flash": ModelEntry( "DeepSeek V4 Flash · DeepSeek", _AGENTIC, 128_000 ), diff --git a/coworker/providers/registry.py b/coworker/providers/registry.py index 397efee10..bc5cc438b 100644 --- a/coworker/providers/registry.py +++ b/coworker/providers/registry.py @@ -262,29 +262,45 @@ def _compat( recommended_model: str, env_key: str, endpoint_help: str = "", + model_choices: tuple = (), ) -> ProviderDescriptor: """Descriptor for an OpenAI-compatible vendor: key + a prefilled, editable endpoint.""" vendor = title.split(" (")[0] + + fields = [ + ProviderField( + "api_key", + f"{vendor} API key", + secret=True, + ), + ProviderField( + "base_url", + "Endpoint", + required=False, + default=base_url, + placeholder=base_url, + help=endpoint_help + or f"Prefilled with {vendor}'s official endpoint; edit only for a regional or proxy variant.", + ), + ] + + if model_choices: + fields.append( + ProviderField( + "recommended_model", + "Default Model", + required=False, + default=recommended_model, + choices=model_choices, + help="The model to activate as default when this provider is configured.", + ) + ) + return ProviderDescriptor( name=name, title=title, needs_key=True, - fields=[ - ProviderField( - "api_key", - f"{vendor} API key", - secret=True, - ), - ProviderField( - "base_url", - "Endpoint", - required=False, - default=base_url, - placeholder=base_url, - help=endpoint_help - or f"Prefilled with {vendor}'s official endpoint; edit only for a regional or proxy variant.", - ), - ], + fields=fields, build=_openai_compat(vendor, base_url, env_key), recommended_model=recommended_model, env_key=env_key, @@ -591,6 +607,19 @@ def _responses_compat( env_key="ZAI_API_KEY", endpoint_help="Prefilled with Z AI's international endpoint. China mainland: https://open.bigmodel.cn/api/paas/v4", ), + _compat( + "zai-coding", + "Z AI Coding Plan", + base_url="https://api.z.ai/api/coding/paas/v4", + recommended_model="glm-5.2", + env_key="ZAI_CODING_API_KEY", + endpoint_help="Prefilled with Z AI's Coding Plan endpoint. Note: This requires a separate Coding Plan subscription.", + model_choices=( + {"value": "glm-5.2", "label": "GLM-5.2"}, + {"value": "glm-4.7", "label": "GLM-4.7"}, + {"value": "glm-4-coder", "label": "GLM-4 Coder"}, + ), + ), _compat( "deepseek", "DeepSeek", diff --git a/coworker/server/manager.py b/coworker/server/manager.py index 1486d9064..052e0523e 100644 --- a/coworker/server/manager.py +++ b/coworker/server/manager.py @@ -3004,7 +3004,8 @@ def set_provider( self._refresh_provider(name) # Convenience: if the provider recommends a model and it's actually available, add it to # the curated list so it shows up in the composer right after configuring the provider. - rec = d.recommended_model + supports_profile_rec = any(f.key == "recommended_model" for f in d.fields) + rec = (profile.get("recommended_model") if supports_profile_rec else None) or d.recommended_model added: Optional[str] = None if rec and rec in self._suggested_models(name): # OpenAI models stay bare (the router's default); others carry their prefix.