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():