Spaces:
Running
Running
""" | |
Flare – ConfigProvider (JSONC-safe) | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
• Pydantic v2 (pattern=…) | |
• Yorum ayıklayıcı string literal’leri korur | |
""" | |
from __future__ import annotations | |
import json | |
from pathlib import Path | |
from typing import Dict, List, Optional | |
from pydantic import BaseModel, Field, HttpUrl, ValidationError | |
from utils import log | |
# -------------------- Model tanımları -------------------- | |
class RetryConfig(BaseModel): | |
strategy: str = Field("static", pattern=r"^(static|exponential)$") | |
retry_count: int = 3 | |
backoff_seconds: int = 2 | |
class ParameterConfig(BaseModel): | |
name: str | |
type: str = Field(..., pattern=r"^(int|float|str|bool)$") | |
required: bool = True | |
invalid_prompt: Optional[str] = None | |
class IntentConfig(BaseModel): | |
name: str | |
action: str | |
parameters: List[ParameterConfig] = [] | |
fallback_error_prompt: Optional[str] = None | |
class VersionConfig(BaseModel): | |
id: int | |
caption: str | |
general_prompt: str | |
is_published: bool = Field(False, alias="published") | |
intents: List[IntentConfig] = [] | |
class ProjectConfig(BaseModel): | |
name: str | |
caption: str | |
versions: List[VersionConfig] | |
class APIAuthConfig(BaseModel): | |
token_endpoint: HttpUrl | |
refresh_endpoint: Optional[HttpUrl] = None | |
client_id: str | |
client_secret: str | |
class APIConfig(BaseModel): | |
name: str | |
url: HttpUrl | |
method: str = Field("GET", pattern=r"^(GET|POST|PUT|PATCH|DELETE)$") | |
timeout_seconds: int = 10 | |
proxy: Optional[str] = None | |
auth: Optional[APIAuthConfig] = None | |
response_prompt: Optional[str] = None | |
retry: Optional[RetryConfig] = None | |
class ServiceConfig(BaseModel): | |
projects: List[ProjectConfig] | |
apis: Dict[str, APIConfig] | |
locale: str = "tr-TR" | |
retry: RetryConfig = RetryConfig() | |
# -------------------- Provider singleton ---------------- | |
class ConfigProvider: | |
_config: Optional[ServiceConfig] = None | |
_CONFIG_PATH = Path(__file__).parent / "service_config.jsonc" | |
def get(cls) -> ServiceConfig: | |
if cls._config is None: | |
cls._config = cls._load_config() | |
return cls._config | |
# ---------------- internal ---------------- | |
def _load_config(cls) -> ServiceConfig: | |
log(f"📥 Loading service config from {cls._CONFIG_PATH.name} …") | |
raw = cls._CONFIG_PATH.read_text(encoding="utf-8") | |
json_str = cls._strip_jsonc(raw) | |
try: | |
data = json.loads(json_str) | |
cfg = ServiceConfig.model_validate(data) | |
log("✅ Service config loaded successfully.") | |
return cfg | |
except (json.JSONDecodeError, ValidationError) as exc: | |
log(f"❌ Config validation error: {exc}") | |
raise | |
# -------- robust JSONC stripper ---------- | |
def _strip_jsonc(text: str) -> str: | |
"""Remove // line comments and /* block comments */ without touching string literals.""" | |
OUT, IN_STR, ESC, IN_SLASH, IN_BLOCK = 0, 1, 2, 3, 4 | |
state = OUT | |
res = [] | |
i = 0 | |
while i < len(text): | |
ch = text[i] | |
if state == OUT: | |
if ch == '"': | |
state = IN_STR | |
res.append(ch) | |
elif ch == '/': | |
# Could be // or /* – peek next char | |
nxt = text[i + 1] if i + 1 < len(text) else "" | |
if nxt == '/': | |
state = IN_SLASH | |
i += 1 # skip nxt in loop | |
elif nxt == '*': | |
state = IN_BLOCK | |
i += 1 | |
else: | |
res.append(ch) | |
else: | |
res.append(ch) | |
elif state == IN_STR: | |
res.append(ch) | |
if ch == '\\': | |
state = ESC # escape next | |
elif ch == '"': | |
state = OUT | |
elif state == ESC: | |
res.append(ch) | |
state = IN_STR | |
elif state == IN_SLASH: | |
if ch == '\n': | |
res.append(ch) # keep newline | |
state = OUT | |
elif state == IN_BLOCK: | |
if ch == '*' and i + 1 < len(text) and text[i + 1] == '/': | |
i += 1 # skip / | |
state = OUT | |
i += 1 | |
return ''.join(res) | |
# --------------- exports --------------- | |
RetryConfig = RetryConfig | |
ParameterConfig = ParameterConfig | |
IntentConfig = IntentConfig | |
VersionConfig = VersionConfig | |
APIConfig = APIConfig | |
ProjectConfig = ProjectConfig | |
ServiceConfig = ServiceConfig | |