Keep OAuth metadata provider-owned

Alessandro committed Jun 1, 2026 at 03:20 UTC ef3b4dda71cd7d1b626efccc982610fa062cdf19
6 files changed +30 -54
plugins/_model_config/provider_metadata.yaml
-8
@@ -1,18 +1,10 @@
1 chat:
2 - codex_oauth:
3 - api_key_mode: oauth
4 - github_copilot_oauth:
5 - api_key_mode: oauth
6 - gemini_api_oauth:
7 - api_key_mode: oauth
2 lm_studio:
3 api_key_mode: none
4 ollama:
5 api_key_mode: none
6 other:
7 api_key_mode: optional
14 - xai_grok_oauth:
15 - api_key_mode: oauth
8
9 embedding:
10 huggingface:
plugins/_oauth/conf/model_providers.yaml
+4
@@ -2,6 +2,7 @@ chat:
2 codex_oauth:
3 name: Codex/ChatGPT Account
4 litellm_provider: openai
5 + api_key_mode: oauth
6 models_list:
7 endpoint_url: "/models"
8 kwargs:
@@ -10,6 +11,7 @@ chat:
11 github_copilot_oauth:
12 name: GitHub Copilot Account
13 litellm_provider: openai
14 + api_key_mode: oauth
15 models_list:
16 endpoint_url: "/models"
17 kwargs:
@@ -18,6 +20,7 @@ chat:
20 gemini_api_oauth:
21 name: Google Gemini API Account
22 litellm_provider: openai
23 + api_key_mode: oauth
24 models_list:
25 endpoint_url: "/models"
26 kwargs:
@@ -26,6 +29,7 @@ chat:
29 xai_grok_oauth:
30 name: xAI Grok Account
31 litellm_provider: openai
32 + api_key_mode: oauth
33 models_list:
34 endpoint_url: "/models"
35 kwargs:
plugins/_oauth/extensions/python/_functions/models/get_api_key/end/_20_oauth_account_dummy_key.py renamed
+1 -1
@@ -4,7 +4,7 @@ from helpers.extension import Extension
4 from plugins._oauth.helpers.providers import DUMMY_API_KEY, oauth_provider_ids
5
6
7 -class CodexAccountDummyKey(Extension):
7 +class OAuthAccountDummyKey(Extension):
8 def execute(self, data: dict | None = None, **kwargs):
9 del kwargs
10 if not isinstance(data, dict):
plugins/_oauth/webui/oauth-config-store.js
-32
@@ -284,14 +284,6 @@ export const store = createStore("oauthConfig", {
284 return path ? `${window.location.origin}${path}` : "";
285 },
286
287 - connected() {
288 - return this.providerConnected(CODEX_PROVIDER);
289 - },
290 -
291 - statusLabel() {
292 - return this.providerStatusLabel(CODEX_PROVIDER);
293 - },
294 -
287 usage(providerId = CODEX_PROVIDER) {
288 return this.providerStatus(providerId)?.usage || null;
289 },
@@ -364,14 +356,6 @@ export const store = createStore("oauthConfig", {
356 return `${Math.round(hours / 24)}d`;
357 },
358
367 - endpointUrl() {
368 - return this.providerEndpointUrl(CODEX_PROVIDER);
369 - },
370 -
371 - callbackUrl() {
372 - return this.providerCallbackUrl(CODEX_PROVIDER);
373 - },
374 -
359 installSettingsHooks(context) {
360 if (!context || context.__oauthConfigHooksInstalled) return;
361
@@ -429,10 +413,6 @@ export const store = createStore("oauthConfig", {
413 return this.modelSlot(key).provider === providerId;
414 },
415
432 - slotUsesCodex(key) {
433 - return this.slotUsesProvider(key, CODEX_PROVIDER);
434 - },
435 -
416 providerName(provider) {
417 if (!provider) return "Not configured";
418 const found = (modelConfigStore.chatProviders || []).find((item) => item.value === provider);
@@ -470,10 +450,6 @@ export const store = createStore("oauthConfig", {
450 }
451 },
452
473 - useCodexForSlot(key) {
474 - this.useProviderForSlot(key, CODEX_PROVIDER);
475 - },
476 -
453 copyMainToUtility() {
454 if (!this.modelConfig) return;
455 const main = this.modelSlot("chat_model");
@@ -636,10 +612,6 @@ export const store = createStore("oauthConfig", {
612 }
613 },
614
639 - connectCodex() {
640 - return this.connectProvider(CODEX_PROVIDER);
641 - },
642 -
615 startPolling(providerId = CODEX_PROVIDER) {
616 this.stopPolling(providerId);
617 this.pollStartedAt = { ...this.pollStartedAt, [providerId]: Date.now() };
@@ -859,10 +831,6 @@ export const store = createStore("oauthConfig", {
831 }
832 },
833
862 - disconnectCodex() {
863 - return this.disconnectProvider(CODEX_PROVIDER);
864 - },
865 -
834 cancelConnect(providerId = "") {
835 if (providerId) {
836 this.stopPolling(providerId);
tests/test_oauth_codex.py
+4 -4
@@ -15,8 +15,8 @@ import yaml
15 sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
16 from plugins._oauth.helpers import codex
17 from plugins._oauth.helpers import routes
18 -from plugins._oauth.extensions.python._functions.models.get_api_key.end._20_codex_account_dummy_key import (
19 - CodexAccountDummyKey,
18 +from plugins._oauth.extensions.python._functions.models.get_api_key.end._20_oauth_account_dummy_key import (
19 + OAuthAccountDummyKey,
20 )
21
22
@@ -689,7 +689,7 @@ def test_provider_config_uses_container_local_agent_zero_origin():
689 def test_codex_provider_reports_dummy_api_key_when_missing():
690 data = {"args": ("codex_oauth",), "kwargs": {}, "result": "None"}
691
692 - CodexAccountDummyKey(agent=None).execute(data=data)
692 + OAuthAccountDummyKey(agent=None).execute(data=data)
693
694 assert data["result"] == "oauth"
695
@@ -697,6 +697,6 @@ def test_codex_provider_reports_dummy_api_key_when_missing():
697 def test_codex_provider_preserves_configured_api_key():
698 data = {"args": ("codex_oauth",), "kwargs": {}, "result": "configured"}
699
700 - CodexAccountDummyKey(agent=None).execute(data=data)
700 + OAuthAccountDummyKey(agent=None).execute(data=data)
701
702 assert data["result"] == "configured"
tests/test_oauth_providers.py
+21 -9
@@ -55,8 +55,8 @@ from plugins._oauth.api import poll_device_login as poll_device_login_api
55 from plugins._oauth.api import start_device_login as start_device_login_api
56 from plugins._oauth.api import start_login as start_login_api
57 from plugins._oauth.api.models import Models
58 -from plugins._oauth.extensions.python._functions.models.get_api_key.end._20_codex_account_dummy_key import (
59 - CodexAccountDummyKey,
58 +from plugins._oauth.extensions.python._functions.models.get_api_key.end._20_oauth_account_dummy_key import (
59 + OAuthAccountDummyKey,
60 )
61 from plugins._oauth.helpers import state
62 from plugins._oauth.helpers.providers import base as provider_base
@@ -635,7 +635,7 @@ def test_poll_device_login_unknown_provider_returns_structured_error():
635 def test_oauth_providers_report_dummy_api_key_when_missing(provider_id, initial):
636 data = {"args": (provider_id,), "kwargs": {}, "result": initial}
637
638 - CodexAccountDummyKey(agent=None).execute(data=data)
638 + OAuthAccountDummyKey(agent=None).execute(data=data)
639
640 assert data["result"] == DUMMY_API_KEY
641
@@ -647,7 +647,7 @@ def test_oauth_providers_report_dummy_api_key_when_missing(provider_id, initial)
647 def test_oauth_providers_report_dummy_api_key_when_result_missing(provider_id):
648 data = {"args": (provider_id,), "kwargs": {}}
649
650 - CodexAccountDummyKey(agent=None).execute(data=data)
650 + OAuthAccountDummyKey(agent=None).execute(data=data)
651
652 assert data["result"] == DUMMY_API_KEY
653
@@ -659,7 +659,7 @@ def test_oauth_providers_report_dummy_api_key_when_result_missing(provider_id):
659 def test_oauth_providers_preserve_configured_api_key(provider_id):
660 data = {"args": (provider_id,), "kwargs": {}, "result": "configured"}
661
662 - CodexAccountDummyKey(agent=None).execute(data=data)
662 + OAuthAccountDummyKey(agent=None).execute(data=data)
663
664 assert data["result"] == "configured"
665
@@ -689,13 +689,25 @@ def test_model_provider_config_contains_all_oauth_providers():
689 assert "50001" not in json.dumps(provider_config)
690
691
692 -def test_provider_metadata_marks_new_oauth_providers_as_oauth_api_key_mode():
692 +def test_oauth_provider_config_marks_oauth_providers_as_oauth_api_key_mode():
693 + provider_path = Path(__file__).resolve().parents[1] / "plugins/_oauth/conf/model_providers.yaml"
694 + provider_config = yaml.safe_load(provider_path.read_text(encoding="utf-8"))
695 + chat = provider_config["chat"]
696 +
697 + assert chat[CODEX_PROVIDER_ID]["api_key_mode"] == "oauth"
698 + assert chat[GITHUB_COPILOT_PROVIDER_ID]["api_key_mode"] == "oauth"
699 + assert chat[GEMINI_API_PROVIDER_ID]["api_key_mode"] == "oauth"
700 + assert chat[XAI_GROK_PROVIDER_ID]["api_key_mode"] == "oauth"
701 +
702 +
703 +def test_model_config_provider_metadata_stays_oauth_provider_agnostic():
704 metadata_path = Path(__file__).resolve().parents[1] / "plugins/_model_config/provider_metadata.yaml"
705 metadata = yaml.safe_load(metadata_path.read_text(encoding="utf-8"))
706
696 - assert metadata["chat"][GITHUB_COPILOT_PROVIDER_ID]["api_key_mode"] == "oauth"
697 - assert metadata["chat"][GEMINI_API_PROVIDER_ID]["api_key_mode"] == "oauth"
698 - assert metadata["chat"][XAI_GROK_PROVIDER_ID]["api_key_mode"] == "oauth"
707 + assert CODEX_PROVIDER_ID not in metadata["chat"]
708 + assert GITHUB_COPILOT_PROVIDER_ID not in metadata["chat"]
709 + assert GEMINI_API_PROVIDER_ID not in metadata["chat"]
710 + assert XAI_GROK_PROVIDER_ID not in metadata["chat"]
711
712
713 def test_usage_plan_catalog_covers_connectable_subscription_providers_only():