Ava2lon commited on
Commit
cf83e6d
·
verified ·
1 Parent(s): 6e93746

Upload 4 files

Browse files
Files changed (2) hide show
  1. app/core/config.py +38 -2
  2. app/core/errors.py +3 -0
app/core/config.py CHANGED
@@ -1,7 +1,7 @@
1
  from functools import lru_cache
2
  from typing import Literal
3
 
4
- from pydantic import Field
5
  from pydantic_settings import BaseSettings, SettingsConfigDict
6
 
7
 
@@ -13,8 +13,19 @@ class Settings(BaseSettings):
13
  supabase_anon_key: str = ""
14
  supabase_service_role_key: str = ""
15
  supabase_jwt_audience: str = "authenticated"
16
- ai_provider: str = "mock"
17
  openai_api_key: str = ""
 
 
 
 
 
 
 
 
 
 
 
18
  n8n_base_url: str = ""
19
  n8n_api_key: str = ""
20
  rate_limit_per_minute: int = Field(default=120, ge=10, le=10_000)
@@ -26,6 +37,31 @@ class Settings(BaseSettings):
26
  extra="ignore",
27
  )
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  @property
30
  def allowed_origins(self) -> list[str]:
31
  return [origin.strip() for origin in self.frontend_url.split(",") if origin.strip()]
 
1
  from functools import lru_cache
2
  from typing import Literal
3
 
4
+ from pydantic import Field, model_validator
5
  from pydantic_settings import BaseSettings, SettingsConfigDict
6
 
7
 
 
13
  supabase_anon_key: str = ""
14
  supabase_service_role_key: str = ""
15
  supabase_jwt_audience: str = "authenticated"
16
+ ai_provider: Literal["openai", "gemini", "openrouter", "deterministic"] = "openai"
17
  openai_api_key: str = ""
18
+ openai_model: str = Field(
19
+ default="gpt-5.5", pattern=r"^[A-Za-z0-9][A-Za-z0-9._:/-]*$"
20
+ )
21
+ gemini_api_key: str = ""
22
+ gemini_model: str = Field(
23
+ default="gemini-2.5-pro", pattern=r"^[A-Za-z0-9][A-Za-z0-9._:/-]*$"
24
+ )
25
+ openrouter_api_key: str = ""
26
+ openrouter_model: str = Field(
27
+ default="openai/gpt-5.5", pattern=r"^[A-Za-z0-9][A-Za-z0-9._:/-]*$"
28
+ )
29
  n8n_base_url: str = ""
30
  n8n_api_key: str = ""
31
  rate_limit_per_minute: int = Field(default=120, ge=10, le=10_000)
 
37
  extra="ignore",
38
  )
39
 
40
+ @model_validator(mode="after")
41
+ def validate_production_configuration(self) -> "Settings":
42
+ if self.environment != "production":
43
+ return self
44
+ missing = []
45
+ if not self.auth_required:
46
+ missing.append("AUTH_REQUIRED=true")
47
+ if not self.supabase_url:
48
+ missing.append("SUPABASE_URL")
49
+ if not self.supabase_service_role_key:
50
+ missing.append("SUPABASE_SERVICE_ROLE_KEY")
51
+ provider_keys = {
52
+ "openai": (self.openai_api_key, "OPENAI_API_KEY"),
53
+ "gemini": (self.gemini_api_key, "GEMINI_API_KEY"),
54
+ "openrouter": (self.openrouter_api_key, "OPENROUTER_API_KEY"),
55
+ }
56
+ provider_key = provider_keys.get(self.ai_provider)
57
+ if provider_key and not provider_key[0]:
58
+ missing.append(provider_key[1])
59
+ if missing:
60
+ raise ValueError(
61
+ "Production configuration is incomplete: " + ", ".join(missing)
62
+ )
63
+ return self
64
+
65
  @property
66
  def allowed_origins(self) -> list[str]:
67
  return [origin.strip() for origin in self.frontend_url.split(",") if origin.strip()]
app/core/errors.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ class ServiceConfigurationError(RuntimeError):
2
+ """Raised when an operation requires an unconfigured external service."""
3
+