refactor: clean up banner extensions
Use call_extensions() helper Let extensions append banners directly Rename HUGGINGFACE_LOCAL_FOR_EMBEDDING to LOCAL_EMBEDDING
keyboardstaff committed
Dec 31, 2025 at 01:19 UTC
99a2a94813f867a5003c8e79d340d35017b543fb
3 files changed
+12
-40
python/api/banners.py
+5
-30
@@ -1,6 +1,5 @@
1
from python.helpers.api import ApiHandler, Request, Response
2
-from python.helpers.extension import Extension
3
-from python.helpers import files, extract_tools
2
+from python.helpers.extension import call_extensions
3
4
5
class GetBanners(ApiHandler):
@@ -12,36 +11,12 @@ class GetBanners(ApiHandler):
11
async def process(self, input: dict, request: Request) -> dict | Response:
12
frontend_banners = input.get("banners", [])
13
frontend_context = input.get("context", {})
15
- backend_banners = await self._run_banner_extensions(frontend_context, frontend_banners)
16
- return {"banners": backend_banners}
17
-
18
- async def _run_banner_extensions(self, context: dict, frontend_banners: list) -> list[dict]:
19
- """Run all banner checks via extension point system."""
14
+
15
+ # Banners array passed by reference - extensions append directly to it
16
banners = []
21
- for cls in self._get_banner_extensions():
22
- try:
23
- result = await cls(agent=None).execute(context=context, frontend_banners=frontend_banners)
24
- if result:
25
- banners.extend(result if isinstance(result, list) else [result])
26
- except Exception as e:
27
- print(f"Banner check failed ({cls.__name__}): {e}")
28
- return banners
29
-
30
- def _get_banner_extensions(self) -> list[type[Extension]]:
31
- """Load banner extension classes from extensions folders."""
32
- all_exts = []
33
- for path in ["python/extensions/banners", "usr/extensions/banners"]:
34
- abs_path = files.get_abs_path(path)
35
- if files.exists(abs_path):
36
- all_exts.extend(extract_tools.load_classes_from_folder(abs_path, "*", Extension))
17
+ await call_extensions("banners", agent=None, banners=banners, context=frontend_context, frontend_banners=frontend_banners)
18
38
- # Deduplicate by filename (usr overrides default), sort by name
39
- unique = {}
40
- for cls in all_exts:
41
- file = cls.__module__.split(".")[-1]
42
- if file not in unique:
43
- unique[file] = cls
44
- return sorted(unique.values(), key=lambda c: c.__module__.split(".")[-1])
19
+ return {"banners": banners}
20
21
@classmethod
22
def get_methods(cls) -> list[str]:
python/extensions/banners/_10_unsecured_connection.py
+1
-4
@@ -6,8 +6,7 @@ import re
6
class UnsecuredConnectionCheck(Extension):
7
"""Check: non-local without credentials, or credentials over non-HTTPS."""
8
9
- async def execute(self, context: dict = {}, **kwargs) -> dict | list | None:
10
- banners = []
9
+ async def execute(self, banners: list = [], context: dict = {}, **kwargs):
10
hostname = context.get("hostname", "")
11
protocol = context.get("protocol", "")
12
@@ -42,8 +41,6 @@ class UnsecuredConnectionCheck(Extension):
41
"dismissible": True,
42
"source": "backend"
43
})
45
-
46
- return banners if banners else None
44
45
def _is_localhost(self, hostname: str) -> bool:
46
local_patterns = ["localhost", "127.0.0.1", "::1", "0.0.0.0"]
python/extensions/banners/_20_missing_api_key.py
+6
-6
@@ -7,7 +7,7 @@ class MissingApiKeyCheck(Extension):
7
"""Check if API keys are configured for selected model providers."""
8
9
LOCAL_PROVIDERS = ["ollama", "lm_studio"]
10
- HUGGINGFACE_LOCAL_FOR_EMBEDDING = ["huggingface"]
10
+ LOCAL_EMBEDDING = ["huggingface"]
11
MODEL_TYPE_NAMES = {
12
"chat": "Chat Model",
13
"utility": "Utility Model",
@@ -15,7 +15,7 @@ class MissingApiKeyCheck(Extension):
15
"embedding": "Embedding Model",
16
}
17
18
- async def execute(self, context: dict = {}, **kwargs) -> dict | None:
18
+ async def execute(self, banners: list = [], context: dict = {}, **kwargs):
19
current_settings = settings_helper.get_settings()
20
model_providers = {
21
"chat": current_settings.get("chat_model_provider", ""),
@@ -33,7 +33,7 @@ class MissingApiKeyCheck(Extension):
33
provider_lower = provider.lower()
34
if provider_lower in self.LOCAL_PROVIDERS:
35
continue
36
- if model_type == "embedding" and provider_lower in self.HUGGINGFACE_LOCAL_FOR_EMBEDDING:
36
+ if model_type == "embedding" and provider_lower in self.LOCAL_EMBEDDING:
37
continue
38
39
api_key = models.get_api_key(provider_lower)
@@ -44,13 +44,13 @@ class MissingApiKeyCheck(Extension):
44
})
45
46
if not missing_providers:
47
- return None
47
+ return
48
49
model_list = ", ".join(
50
f"{p['model_type']} ({p['provider']})" for p in missing_providers
51
)
52
53
- return {
53
+ banners.append({
54
"id": "missing-api-key",
55
"type": "error",
56
"priority": 100,
@@ -61,4 +61,4 @@ class MissingApiKeyCheck(Extension):
61
Add your API key</a> in Settings → External Services → API Keys.""",
62
"dismissible": False,
63
"source": "backend"
64
- }
64
+ })