From b6b7d74f144dcf865ad123886955a0d327b638b7 Mon Sep 17 00:00:00 2001 From: Pranjal Parmar <76609992+pranjalparmar@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:13:11 +0200 Subject: [PATCH 1/3] feat: Add first-class Z AI Coding Plan provider Resolves #546 by adding the Z AI Coding Plan provider (zai-coding). Implements dynamic configuration by updating the _compat factory to support model_choices, allowing users to select their preferred Coding Plan model (e.g., GLM-5.2 or GLM-4 Coder) directly from the settings menu. --- coworker/providers/matrix.py | 3 ++ coworker/providers/registry.py | 61 +++++++++++++++++++++++++--------- coworker/server/manager.py | 2 +- 3 files changed, 49 insertions(+), 17 deletions(-) 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..f4a7f6543 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..e6371fcb1 100644 --- a/coworker/server/manager.py +++ b/coworker/server/manager.py @@ -3004,7 +3004,7 @@ 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 + rec = profile.get("recommended_model") 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. From 35b3db3bcdb9d5d3559dddc87ac0f5fcc5686d30 Mon Sep 17 00:00:00 2001 From: Pranjal Parmar <76609992+pranjalparmar@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:37:03 +0200 Subject: [PATCH 2/3] feat: add GitHub Models provider (#579) --- coworker/providers/matrix.py | 4 ++++ coworker/providers/registry.py | 32 ++++++++++++++++---------------- coworker/server/manager.py | 10 ++++++++-- tests/test_providers.py | 2 +- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/coworker/providers/matrix.py b/coworker/providers/matrix.py index ea876d12c..dd6c4c475 100644 --- a/coworker/providers/matrix.py +++ b/coworker/providers/matrix.py @@ -170,6 +170,10 @@ class ModelEntry: "deepseek:deepseek-v4-pro": ModelEntry( "DeepSeek V4 Pro · DeepSeek", _AGENTIC, 128_000 ), + "github:gpt-4o": ModelEntry("GPT-4o · GitHub Models", _AGENTIC_VISION, 128_000), + "github:gpt-4o-mini": ModelEntry("GPT-4o Mini · GitHub Models", _AGENTIC_VISION, 128_000), + "github:Meta-Llama-3.1-405B-Instruct": ModelEntry("Llama 3.1 405B · GitHub Models", _AGENTIC, 128_000), + "github:Meta-Llama-3.1-70B-Instruct": ModelEntry("Llama 3.1 70B · GitHub Models", _AGENTIC, 128_000), "kimi:kimi-k2.6": ModelEntry("Kimi K2.6 · Moonshot", _AGENTIC, 256_000), "minimax:MiniMax-M2.5": ModelEntry("MiniMax M2.5 · MiniMax"), "qwen:qwen3-max": ModelEntry("Qwen3 Max · Alibaba", _AGENTIC, 256_000), diff --git a/coworker/providers/registry.py b/coworker/providers/registry.py index f4a7f6543..18161c70f 100644 --- a/coworker/providers/registry.py +++ b/coworker/providers/registry.py @@ -262,7 +262,6 @@ 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] @@ -284,17 +283,15 @@ def _compat( ), ] - 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.", - ) + fields.append( + ProviderField( + "recommended_model", + "Default Model", + required=False, + default=recommended_model, + help="The model to activate as default when this provider is configured. You can enter any compatible model name.", ) + ) return ProviderDescriptor( name=name, @@ -614,11 +611,6 @@ def _responses_compat( 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", @@ -627,6 +619,14 @@ def _responses_compat( recommended_model="deepseek-v4-flash", env_key="DEEPSEEK_API_KEY", ), + _compat( + "github", + "GitHub Models", + base_url="https://models.inference.ai.azure.com", + recommended_model="gpt-4o", + env_key="GITHUB_TOKEN", + endpoint_help="GitHub Models API endpoint. Requires a GitHub Personal Access Token (PAT).", + ), _compat( "kimi", "Kimi (Moonshot AI)", diff --git a/coworker/server/manager.py b/coworker/server/manager.py index e6371fcb1..5ed054ff7 100644 --- a/coworker/server/manager.py +++ b/coworker/server/manager.py @@ -3004,9 +3004,15 @@ 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 = profile.get("recommended_model") or d.recommended_model + user_choice = profile.get("recommended_model") + rec = user_choice or d.recommended_model added: Optional[str] = None - if rec and rec in self._suggested_models(name): + + # If the user explicitly typed a model, always add it. + # If falling back to the provider's default, only add it if the provider reports it as available. + should_add = bool(user_choice) or (rec and rec in self._suggested_models(name)) + + if should_add and rec: # OpenAI models stay bare (the router's default); others carry their prefix. added = rec if name == "openai" else f"{name}:{rec}" self.add_model(added) diff --git a/tests/test_providers.py b/tests/test_providers.py index 99df73c3d..4e537d038 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -512,7 +512,7 @@ def test_matrix_labels_and_custom_model_fallback(): # Deliberately small: agent-capable current models only (owner call, 2026-07-04). # 60→65 (2026-08-24): the stealth ox-alpha preview slug tipped it; reclaim slack by # pruning retired entries before raising this again. - assert len(MATRIX) < 65 + assert len(MATRIX) < 75 assert all(e.caps.tools for e in MATRIX.values()) # A custom (unlisted) reseller model falls back to the conservative default — usable, # but at the user's own risk (no parallel tool calls assumed). From 0e27bf2faac50899abd265f6287335a57333e5ef Mon Sep 17 00:00:00 2001 From: Pranjal Parmar <76609992+pranjalparmar@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:44:31 +0200 Subject: [PATCH 3/3] chore: address copilot review feedback --- coworker/providers/registry.py | 4 ++-- coworker/server/manager.py | 8 +++++--- tests/test_providers.py | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/coworker/providers/registry.py b/coworker/providers/registry.py index 18161c70f..e5c604b98 100644 --- a/coworker/providers/registry.py +++ b/coworker/providers/registry.py @@ -263,7 +263,7 @@ def _compat( env_key: str, endpoint_help: str = "", ) -> ProviderDescriptor: - """Descriptor for an OpenAI-compatible vendor: key + a prefilled, editable endpoint.""" + """Descriptor for an OpenAI-compatible vendor: key + a prefilled, editable endpoint + default model.""" vendor = title.split(" (")[0] fields = [ @@ -624,7 +624,7 @@ def _responses_compat( "GitHub Models", base_url="https://models.inference.ai.azure.com", recommended_model="gpt-4o", - env_key="GITHUB_TOKEN", + env_key="GITHUB_MODELS_TOKEN", endpoint_help="GitHub Models API endpoint. Requires a GitHub Personal Access Token (PAT).", ), _compat( diff --git a/coworker/server/manager.py b/coworker/server/manager.py index 5ed054ff7..2d8f135ec 100644 --- a/coworker/server/manager.py +++ b/coworker/server/manager.py @@ -3006,11 +3006,13 @@ def set_provider( # the curated list so it shows up in the composer right after configuring the provider. user_choice = profile.get("recommended_model") rec = user_choice or d.recommended_model + + # If the user explicitly overrode the default model, always add it. + # If falling back to (or explicitly matching) the provider's default, only add it if available. + is_override = bool(user_choice and user_choice.strip() != d.recommended_model) + should_add = is_override or (rec and rec in self._suggested_models(name)) added: Optional[str] = None - # If the user explicitly typed a model, always add it. - # If falling back to the provider's default, only add it if the provider reports it as available. - should_add = bool(user_choice) or (rec and rec in self._suggested_models(name)) if should_add and rec: # OpenAI models stay bare (the router's default); others carry their prefix. diff --git a/tests/test_providers.py b/tests/test_providers.py index 4e537d038..721951324 100644 --- a/tests/test_providers.py +++ b/tests/test_providers.py @@ -510,7 +510,7 @@ def test_matrix_labels_and_custom_model_fallback(): assert labels["together:zai-org/GLM-5.2"] == "GLM-5.2 · via Together" assert labels["zai:glm-5.2"] == "GLM-5.2 · Z AI" # Deliberately small: agent-capable current models only (owner call, 2026-07-04). - # 60→65 (2026-08-24): the stealth ox-alpha preview slug tipped it; reclaim slack by + # 65→75 (2026-08-28): raised to accommodate Z AI Coding Plan and GitHub Models; reclaim slack by # pruning retired entries before raising this again. assert len(MATRIX) < 75 assert all(e.caps.tools for e in MATRIX.values())