diff --git a/.env.example b/.env.example index 5c3cd4f..344a03d 100644 --- a/.env.example +++ b/.env.example @@ -16,7 +16,7 @@ CELERY_BROKER_URL=redis://redis:6379/0 CELERY_RESULT_BACKEND=redis://redis:6379/1 # OpenRouter AI -OPENROUTER_API_KEY=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 +OPENROUTER_API_KEY=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 OPENROUTER_MODEL=nvidia/nemotron-3-super-120b-a12b:free OPENROUTER_BASE_URL=https://openrouter.ai/api/v1/chat/completions diff --git a/backend/app/services/openrouter_config_service.py b/backend/app/services/openrouter_config_service.py index e9a5b14..4d0f7b7 100644 --- a/backend/app/services/openrouter_config_service.py +++ b/backend/app/services/openrouter_config_service.py @@ -8,6 +8,7 @@ from flask import current_app, has_app_context DEFAULT_OPENROUTER_MODEL = "nvidia/nemotron-3-super-120b-a12b:free" DEFAULT_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1/chat/completions" +LEGACY_SAMPLE_OPENROUTER_API_KEY = "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" @dataclass(frozen=True) @@ -42,18 +43,30 @@ def _first_non_empty(*values: str, default: str = "") -> str: return default +def _normalize_api_key(value: str) -> str: + """Treat placeholders and legacy sample keys as missing configuration.""" + normalized = str(value or "").strip() + if not normalized: + return "" + if normalized.startswith("replace-with-"): + return "" + if normalized == LEGACY_SAMPLE_OPENROUTER_API_KEY: + return "" + return normalized + + def get_openrouter_settings() -> OpenRouterSettings: """Return the effective OpenRouter settings for the current execution context.""" dotenv_settings = _load_dotenv_settings() - env_api_key = os.getenv("OPENROUTER_API_KEY", "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") + env_api_key = os.getenv("OPENROUTER_API_KEY", "") env_model = os.getenv("OPENROUTER_MODEL", DEFAULT_OPENROUTER_MODEL) env_base_url = os.getenv("OPENROUTER_BASE_URL", DEFAULT_OPENROUTER_BASE_URL) if has_app_context(): api_key = _first_non_empty( - current_app.config.get("OPENROUTER_API_KEY", "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"), + current_app.config.get("OPENROUTER_API_KEY", ""), env_api_key, - dotenv_settings.get("OPENROUTER_API_KEY", "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"), + dotenv_settings.get("OPENROUTER_API_KEY", ""), ) model = _first_non_empty( current_app.config.get("OPENROUTER_MODEL", DEFAULT_OPENROUTER_MODEL), @@ -67,13 +80,13 @@ def get_openrouter_settings() -> OpenRouterSettings: dotenv_settings.get("OPENROUTER_BASE_URL", DEFAULT_OPENROUTER_BASE_URL), default=DEFAULT_OPENROUTER_BASE_URL, ) - return OpenRouterSettings(api_key=api_key, model=model, base_url=base_url) + return OpenRouterSettings(api_key=_normalize_api_key(api_key), model=model, base_url=base_url) return OpenRouterSettings( - api_key=_first_non_empty( + api_key=_normalize_api_key(_first_non_empty( env_api_key, dotenv_settings.get("OPENROUTER_API_KEY", ""), - ), + )), model=_first_non_empty( env_model, dotenv_settings.get("OPENROUTER_MODEL", DEFAULT_OPENROUTER_MODEL), diff --git a/backend/config/__init__.py b/backend/config/__init__.py index a5d5fb6..8cbe8cb 100644 --- a/backend/config/__init__.py +++ b/backend/config/__init__.py @@ -105,7 +105,7 @@ class BaseConfig: RATELIMIT_DEFAULT = "100/hour" # OpenRouter AI - OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "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") + OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "") OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "nvidia/nemotron-3-super-120b-a12b:free") OPENROUTER_BASE_URL = os.getenv( "OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1/chat/completions" diff --git a/backend/tests/test_openrouter_config_service.py b/backend/tests/test_openrouter_config_service.py index 7a1c429..b49574e 100644 --- a/backend/tests/test_openrouter_config_service.py +++ b/backend/tests/test_openrouter_config_service.py @@ -62,6 +62,39 @@ class TestOpenRouterConfigService: assert settings.model == 'env-model' assert settings.base_url == 'https://env.example/api' + def test_returns_blank_api_key_when_not_configured(self, app, monkeypatch): + monkeypatch.delenv('OPENROUTER_API_KEY', raising=False) + monkeypatch.delenv('OPENROUTER_MODEL', raising=False) + monkeypatch.delenv('OPENROUTER_BASE_URL', raising=False) + monkeypatch.setattr('app.services.openrouter_config_service._load_dotenv_settings', lambda: {}) + + with app.app_context(): + app.config.update({ + 'OPENROUTER_API_KEY': ' ', + 'OPENROUTER_MODEL': '', + 'OPENROUTER_BASE_URL': '', + }) + settings = get_openrouter_settings() + + assert settings.api_key == '' + assert settings.model + assert settings.base_url + + def test_treats_legacy_sample_key_as_not_configured(self, app, monkeypatch): + monkeypatch.delenv('OPENROUTER_API_KEY', raising=False) + monkeypatch.setattr( + 'app.services.openrouter_config_service._load_dotenv_settings', + lambda: { + 'OPENROUTER_API_KEY': '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', + }, + ) + + with app.app_context(): + app.config.update({'OPENROUTER_API_KEY': ''}) + settings = get_openrouter_settings() + + assert settings.api_key == '' + class TestAiServicesUseSharedConfig: def test_pdf_ai_uses_flask_config(self, app, monkeypatch):