Skip to content

Commit 1a66680

Browse files
committed
refactor(ai): address review findings on the provider registry
- Build PROVIDERS via _build_registry(), which raises on a duplicate provider name at import time instead of silently dropping the earlier spec (dict-comprehension behavior); regression test added - Pin the exact OpenAI-compatible provider -> discovery URL mapping in a test so a registry edit can't silently drop or misassign a URL - Give TEST_MODELS a real type annotation (Dict[str, Tuple[Optional[str], str]]) instead of bare dict
1 parent 135a5bf commit 1a66680

3 files changed

Lines changed: 63 additions & 7 deletions

File tree

open_notebook/ai/connection_tester.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import json
1010
import os
1111
import struct
12-
from typing import Optional, Tuple
12+
from typing import Dict, Optional, Tuple
1313

1414
import httpx
1515
from loguru import logger
@@ -59,7 +59,7 @@ def _is_vertex_credentials_file_error(exc: Exception) -> bool:
5959
# its own. The provider test also no longer treats a model-level failure as
6060
# a connection failure (see `_connection_failure_reason`), so even if an
6161
# alias ever breaks, the test still reports the credentials correctly.
62-
TEST_MODELS: dict = {
62+
TEST_MODELS: Dict[str, Tuple[Optional[str], str]] = {
6363
name: (spec.test_model, spec.test_model_type) for name, spec in PROVIDERS.items()
6464
}
6565

open_notebook/ai/provider_registry.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ def env_config(self) -> Dict[str, List[str]]:
6767
_ALL_MODALITIES = ("language", "embedding", "speech_to_text", "text_to_speech")
6868

6969

70-
PROVIDERS: Dict[str, ProviderSpec] = {
71-
spec.name: spec
72-
for spec in (
70+
_PROVIDER_SPECS: Tuple[ProviderSpec, ...] = (
7371
ProviderSpec(
7472
name="openai",
7573
display_name="OpenAI",
@@ -227,5 +225,23 @@ def env_config(self) -> Dict[str, List[str]]:
227225
test_model=None, # Dynamic - uses first available model
228226
docs_url="https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/openai-compatible.md",
229227
),
230-
)
231-
}
228+
)
229+
230+
231+
def _build_registry(specs: Tuple[ProviderSpec, ...]) -> Dict[str, ProviderSpec]:
232+
"""Build the name -> spec map, refusing duplicate names at import time.
233+
234+
A plain dict comprehension would silently drop the earlier spec on a
235+
name collision; fail loudly instead.
236+
"""
237+
registry: Dict[str, ProviderSpec] = {}
238+
for spec in specs:
239+
if spec.name in registry:
240+
raise ValueError(
241+
f"Duplicate provider name in registry: {spec.name!r}"
242+
)
243+
registry[spec.name] = spec
244+
return registry
245+
246+
247+
PROVIDERS: Dict[str, ProviderSpec] = _build_registry(_PROVIDER_SPECS)

tests/test_credential_provider_validation.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,46 @@ def test_discovery_functions_cover_registry(self):
7676

7777
assert set(PROVIDER_DISCOVERY_FUNCTIONS.keys()) == set(PROVIDERS.keys())
7878

79+
def test_registry_rejects_duplicate_provider_names(self):
80+
"""A plain dict comprehension would silently drop the earlier spec
81+
on a name collision; the registry builder must raise instead."""
82+
from open_notebook.ai.provider_registry import (
83+
ProviderSpec,
84+
_build_registry,
85+
)
86+
87+
duplicate = (
88+
ProviderSpec(name="dupe", display_name="Dupe A", modalities=("language",)),
89+
ProviderSpec(name="dupe", display_name="Dupe B", modalities=("language",)),
90+
)
91+
with pytest.raises(ValueError, match="Duplicate provider name"):
92+
_build_registry(duplicate)
93+
94+
def test_openai_compat_discovery_urls_are_exactly_as_expected(self):
95+
"""Pin the derived provider -> discovery URL mapping so a registry
96+
edit can't silently drop or misassign a URL (both the model_discovery
97+
table and the credentials_service url_map are built from these)."""
98+
from open_notebook.ai.model_discovery import OPENAI_COMPAT_PROVIDERS
99+
100+
expected = {
101+
"openai": "https://api.openai.com/v1/models",
102+
"groq": "https://api.groq.com/openai/v1/models",
103+
"mistral": "https://api.mistral.ai/v1/models",
104+
"deepseek": "https://api.deepseek.com/models",
105+
"xai": "https://api.x.ai/v1/models",
106+
"openrouter": "https://openrouter.ai/api/v1/models",
107+
"dashscope": "https://dashscope.aliyuncs.com/compatible-mode/v1/models",
108+
"minimax": "https://api.minimax.io/v1/models",
109+
}
110+
assert {
111+
name: spec.openai_compat_discovery_url
112+
for name, spec in PROVIDERS.items()
113+
if spec.openai_compat_discovery_url
114+
} == expected
115+
assert {
116+
name: spec.url for name, spec in OPENAI_COMPAT_PROVIDERS.items()
117+
} == expected
118+
79119

80120
class TestSupportedProviderMatchesOtherSourcesOfTruth:
81121
def test_matches_known_good_provider_list(self):

0 commit comments

Comments
 (0)