Spaces:
Building
Building
Update config_provider.py
Browse files- config_provider.py +107 -86
config_provider.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
"""
|
2 |
-
Flare – ConfigProvider (
|
3 |
"""
|
4 |
|
5 |
from __future__ import annotations
|
@@ -26,7 +26,7 @@ class GlobalConfig(BaseModel):
|
|
26 |
})
|
27 |
|
28 |
# STT configurations
|
29 |
-
stt_engine: str = Field("no_stt", pattern=r"^(no_stt|google|azure|amazon|gpt4o_realtime)$")
|
30 |
stt_engine_api_key: Optional[str] = None
|
31 |
stt_settings: Optional[Dict[str, Any]] = Field(default_factory=lambda: {
|
32 |
"speech_timeout_ms": 2000,
|
@@ -141,28 +141,11 @@ class APIAuthConfig(BaseModel):
|
|
141 |
token_refresh_endpoint: Optional[HttpUrl] = None
|
142 |
token_refresh_body: Dict[str, Any] = {}
|
143 |
|
144 |
-
@field_validator('token_request_body', 'token_refresh_body', mode='before')
|
145 |
-
def parse_json_strings(cls, v):
|
146 |
-
"""Parse JSON strings to dict"""
|
147 |
-
if isinstance(v, str):
|
148 |
-
try:
|
149 |
-
return json.loads(v)
|
150 |
-
except json.JSONDecodeError:
|
151 |
-
log(f"⚠️ Invalid JSON string in auth config: {v}")
|
152 |
-
return {}
|
153 |
-
return v
|
154 |
-
|
155 |
class Config:
|
156 |
extra = "allow"
|
157 |
populate_by_name = True
|
158 |
|
159 |
|
160 |
-
class ResponseMappingConfig(BaseModel):
|
161 |
-
"""Response mapping configuration"""
|
162 |
-
variable_name: str = Field(..., pattern=r"^[a-z_][a-z0-9_]*$")
|
163 |
-
type: str = Field(..., pattern=r"^(int|float|bool|str|date)$")
|
164 |
-
json_path: str
|
165 |
-
|
166 |
class APIConfig(BaseModel):
|
167 |
name: str
|
168 |
url: HttpUrl
|
@@ -171,33 +154,15 @@ class APIConfig(BaseModel):
|
|
171 |
body_template: Dict[str, Any] = {}
|
172 |
timeout_seconds: int = 10
|
173 |
retry: RetryConfig = RetryConfig()
|
174 |
-
proxy: Optional[
|
175 |
auth: Optional[APIAuthConfig] = None
|
176 |
response_prompt: Optional[str] = None
|
177 |
-
response_mappings: List[ResponseMappingConfig] = [] # Yeni alan
|
178 |
-
|
179 |
-
# Audit fields
|
180 |
-
last_update_date: Optional[str] = None
|
181 |
-
last_update_user: Optional[str] = None
|
182 |
-
created_date: Optional[str] = None
|
183 |
-
created_by: Optional[str] = None
|
184 |
-
deleted: bool = False
|
185 |
-
|
186 |
-
@field_validator('headers', 'body_template', mode='before')
|
187 |
-
def parse_json_strings(cls, v):
|
188 |
-
"""Parse JSON strings to dict"""
|
189 |
-
if isinstance(v, str):
|
190 |
-
try:
|
191 |
-
return json.loads(v)
|
192 |
-
except json.JSONDecodeError:
|
193 |
-
log(f"⚠️ Invalid JSON string in API config: {v}")
|
194 |
-
return {}
|
195 |
-
return v
|
196 |
|
197 |
class Config:
|
198 |
extra = "allow"
|
199 |
populate_by_name = True
|
200 |
|
|
|
201 |
# ---------------- Intent / Param ---------
|
202 |
class ParameterConfig(BaseModel):
|
203 |
name: str
|
@@ -243,22 +208,12 @@ class LLMConfig(BaseModel):
|
|
243 |
|
244 |
|
245 |
class VersionConfig(BaseModel):
|
246 |
-
id: int
|
247 |
-
version_number: int
|
248 |
caption: Optional[str] = ""
|
249 |
published: bool = False
|
250 |
general_prompt: str
|
251 |
-
llm: LLMConfig
|
252 |
-
intents: List[IntentConfig]
|
253 |
-
|
254 |
-
# Audit fields
|
255 |
-
last_update_date: Optional[str] = None
|
256 |
-
last_update_user: Optional[str] = None
|
257 |
-
created_date: Optional[str] = None
|
258 |
-
created_by: Optional[str] = None
|
259 |
-
deleted: bool = False
|
260 |
-
publish_date: Optional[str] = None
|
261 |
-
published_by: Optional[str] = None
|
262 |
|
263 |
class Config:
|
264 |
extra = "allow"
|
@@ -269,22 +224,9 @@ class ProjectConfig(BaseModel):
|
|
269 |
id: Optional[int] = None
|
270 |
name: str
|
271 |
caption: Optional[str] = ""
|
272 |
-
icon: Optional[str] = "folder"
|
273 |
-
description: Optional[str] = ""
|
274 |
enabled: bool = True
|
275 |
-
|
276 |
-
supported_languages: List[str] = Field(default_factory=lambda: ["tr"])
|
277 |
-
timezone: str = "Europe/Istanbul"
|
278 |
-
region: str = "tr-TR"
|
279 |
-
version_id_counter: int = 1
|
280 |
versions: List[VersionConfig]
|
281 |
-
|
282 |
-
# Audit fields
|
283 |
-
last_update_date: Optional[str] = None
|
284 |
-
last_update_user: Optional[str] = None
|
285 |
-
created_date: Optional[str] = None
|
286 |
-
created_by: Optional[str] = None
|
287 |
-
deleted: bool = False
|
288 |
|
289 |
class Config:
|
290 |
extra = "allow"
|
@@ -292,9 +234,8 @@ class ProjectConfig(BaseModel):
|
|
292 |
|
293 |
# ---------------- Activity Log -----------
|
294 |
class ActivityLogEntry(BaseModel):
|
295 |
-
id: int
|
296 |
timestamp: str
|
297 |
-
|
298 |
action: str
|
299 |
entity_type: str
|
300 |
entity_id: Optional[int] = None
|
@@ -368,6 +309,27 @@ class ConfigProvider:
|
|
368 |
cls._check_environment_setup()
|
369 |
return cls._instance
|
370 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
371 |
@classmethod
|
372 |
def _load(cls) -> ServiceConfig:
|
373 |
"""Load configuration from service_config.jsonc"""
|
@@ -380,6 +342,62 @@ class ConfigProvider:
|
|
380 |
with open(cls._CONFIG_PATH, 'r', encoding='utf-8') as f:
|
381 |
config_data = commentjson.load(f)
|
382 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
383 |
# Create ServiceConfig instance
|
384 |
service_config = ServiceConfig(**config_data)
|
385 |
|
@@ -417,23 +435,26 @@ class ConfigProvider:
|
|
417 |
|
418 |
if missing_secrets:
|
419 |
log(f"⚠️ Running in {config.work_mode} mode. Missing secrets: {', '.join(missing_secrets)}")
|
420 |
-
log("
|
421 |
-
|
422 |
-
log(f"✅ Running in {config.work_mode} mode with all required secrets")
|
423 |
-
|
424 |
-
elif config.is_on_premise():
|
425 |
# On-premise mode - check for .env file
|
426 |
-
env_path = Path(".env"
|
427 |
-
if env_path.exists():
|
428 |
-
from dotenv import load_dotenv
|
429 |
-
load_dotenv()
|
430 |
-
log("✅ Running in on-premise mode with .env file")
|
431 |
-
else:
|
432 |
log("⚠️ Running in on-premise mode but .env file not found")
|
433 |
-
log("
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
"""
|
2 |
+
Flare – ConfigProvider (TTS/STT support)
|
3 |
"""
|
4 |
|
5 |
from __future__ import annotations
|
|
|
26 |
})
|
27 |
|
28 |
# STT configurations
|
29 |
+
stt_engine: str = Field("no_stt", pattern=r"^(no_stt|google|azure|amazon|gpt4o_realtime|flicker)$")
|
30 |
stt_engine_api_key: Optional[str] = None
|
31 |
stt_settings: Optional[Dict[str, Any]] = Field(default_factory=lambda: {
|
32 |
"speech_timeout_ms": 2000,
|
|
|
141 |
token_refresh_endpoint: Optional[HttpUrl] = None
|
142 |
token_refresh_body: Dict[str, Any] = {}
|
143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
class Config:
|
145 |
extra = "allow"
|
146 |
populate_by_name = True
|
147 |
|
148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
class APIConfig(BaseModel):
|
150 |
name: str
|
151 |
url: HttpUrl
|
|
|
154 |
body_template: Dict[str, Any] = {}
|
155 |
timeout_seconds: int = 10
|
156 |
retry: RetryConfig = RetryConfig()
|
157 |
+
proxy: Optional[str | ProxyConfig] = None
|
158 |
auth: Optional[APIAuthConfig] = None
|
159 |
response_prompt: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
160 |
|
161 |
class Config:
|
162 |
extra = "allow"
|
163 |
populate_by_name = True
|
164 |
|
165 |
+
|
166 |
# ---------------- Intent / Param ---------
|
167 |
class ParameterConfig(BaseModel):
|
168 |
name: str
|
|
|
208 |
|
209 |
|
210 |
class VersionConfig(BaseModel):
|
211 |
+
id: int = Field(..., alias="version_number")
|
|
|
212 |
caption: Optional[str] = ""
|
213 |
published: bool = False
|
214 |
general_prompt: str
|
215 |
+
llm: "LLMConfig"
|
216 |
+
intents: List["IntentConfig"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
|
218 |
class Config:
|
219 |
extra = "allow"
|
|
|
224 |
id: Optional[int] = None
|
225 |
name: str
|
226 |
caption: Optional[str] = ""
|
|
|
|
|
227 |
enabled: bool = True
|
228 |
+
last_version_number: Optional[int] = None
|
|
|
|
|
|
|
|
|
229 |
versions: List[VersionConfig]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
|
231 |
class Config:
|
232 |
extra = "allow"
|
|
|
234 |
|
235 |
# ---------------- Activity Log -----------
|
236 |
class ActivityLogEntry(BaseModel):
|
|
|
237 |
timestamp: str
|
238 |
+
username: str
|
239 |
action: str
|
240 |
entity_type: str
|
241 |
entity_id: Optional[int] = None
|
|
|
309 |
cls._check_environment_setup()
|
310 |
return cls._instance
|
311 |
|
312 |
+
@classmethod
|
313 |
+
def reload(cls) -> ServiceConfig:
|
314 |
+
"""Force reload configuration from file"""
|
315 |
+
cls._instance = None
|
316 |
+
return cls.get()
|
317 |
+
|
318 |
+
@classmethod
|
319 |
+
def update_config(cls, config_dict: dict):
|
320 |
+
"""Update the current configuration with new values"""
|
321 |
+
if cls._instance is None:
|
322 |
+
cls.get()
|
323 |
+
|
324 |
+
# Update global config
|
325 |
+
if 'config' in config_dict:
|
326 |
+
for key, value in config_dict['config'].items():
|
327 |
+
if hasattr(cls._instance.global_config, key):
|
328 |
+
setattr(cls._instance.global_config, key, value)
|
329 |
+
|
330 |
+
# Save to file
|
331 |
+
cls._instance.save()
|
332 |
+
|
333 |
@classmethod
|
334 |
def _load(cls) -> ServiceConfig:
|
335 |
"""Load configuration from service_config.jsonc"""
|
|
|
342 |
with open(cls._CONFIG_PATH, 'r', encoding='utf-8') as f:
|
343 |
config_data = commentjson.load(f)
|
344 |
|
345 |
+
# Ensure required fields exist in config data
|
346 |
+
if 'config' not in config_data:
|
347 |
+
config_data['config'] = {}
|
348 |
+
|
349 |
+
config_section = config_data['config']
|
350 |
+
|
351 |
+
# Set defaults for missing fields
|
352 |
+
config_section.setdefault('work_mode', 'hfcloud')
|
353 |
+
config_section.setdefault('spark_endpoint', 'http://localhost:7861')
|
354 |
+
config_section.setdefault('cloud_token', None)
|
355 |
+
config_section.setdefault('internal_prompt', None)
|
356 |
+
config_section.setdefault('tts_engine', 'no_tts')
|
357 |
+
config_section.setdefault('tts_engine_api_key', None)
|
358 |
+
config_section.setdefault('tts_settings', {'use_ssml': False})
|
359 |
+
config_section.setdefault('stt_engine', 'no_stt')
|
360 |
+
config_section.setdefault('stt_engine_api_key', None)
|
361 |
+
config_section.setdefault('stt_settings', {
|
362 |
+
'speech_timeout_ms': 2000,
|
363 |
+
'noise_reduction_level': 2,
|
364 |
+
'vad_sensitivity': 0.5,
|
365 |
+
'language': 'tr-TR',
|
366 |
+
'model': 'latest_long',
|
367 |
+
'use_enhanced': True,
|
368 |
+
'enable_punctuation': True,
|
369 |
+
'interim_results': True
|
370 |
+
})
|
371 |
+
config_section.setdefault('users', [])
|
372 |
+
|
373 |
+
# Convert string body/headers to dict if needed
|
374 |
+
for api in config_data.get('apis', []):
|
375 |
+
if isinstance(api.get('headers'), str):
|
376 |
+
try:
|
377 |
+
api['headers'] = json.loads(api['headers'])
|
378 |
+
except:
|
379 |
+
api['headers'] = {}
|
380 |
+
|
381 |
+
if isinstance(api.get('body_template'), str):
|
382 |
+
try:
|
383 |
+
api['body_template'] = json.loads(api['body_template'])
|
384 |
+
except:
|
385 |
+
api['body_template'] = {}
|
386 |
+
|
387 |
+
# Handle auth section
|
388 |
+
if api.get('auth'):
|
389 |
+
auth = api['auth']
|
390 |
+
if isinstance(auth.get('token_request_body'), str):
|
391 |
+
try:
|
392 |
+
auth['token_request_body'] = json.loads(auth['token_request_body'])
|
393 |
+
except:
|
394 |
+
auth['token_request_body'] = {}
|
395 |
+
if isinstance(auth.get('token_refresh_body'), str):
|
396 |
+
try:
|
397 |
+
auth['token_refresh_body'] = json.loads(auth['token_refresh_body'])
|
398 |
+
except:
|
399 |
+
auth['token_refresh_body'] = {}
|
400 |
+
|
401 |
# Create ServiceConfig instance
|
402 |
service_config = ServiceConfig(**config_data)
|
403 |
|
|
|
435 |
|
436 |
if missing_secrets:
|
437 |
log(f"⚠️ Running in {config.work_mode} mode. Missing secrets: {', '.join(missing_secrets)}")
|
438 |
+
log("Please set these as HuggingFace Space Secrets for cloud deployment.")
|
439 |
+
else:
|
|
|
|
|
|
|
440 |
# On-premise mode - check for .env file
|
441 |
+
env_path = Path(__file__).parent / ".env"
|
442 |
+
if not env_path.exists():
|
|
|
|
|
|
|
|
|
443 |
log("⚠️ Running in on-premise mode but .env file not found")
|
444 |
+
log("Creating default .env file...")
|
445 |
+
|
446 |
+
default_env = """# Flare Environment Configuration
|
447 |
+
JWT_SECRET=flare-admin-secret-key-change-in-production
|
448 |
+
JWT_ALGORITHM=HS256
|
449 |
+
JWT_EXPIRATION_HOURS=24
|
450 |
+
FLARE_TOKEN_KEY=flare-token-encryption-key
|
451 |
+
SPARK_TOKEN=your-spark-token-here
|
452 |
+
"""
|
453 |
+
env_path.write_text(default_env)
|
454 |
+
log("✅ Default .env file created. Please update with your actual values.")
|
455 |
+
|
456 |
+
|
457 |
+
# Forward references
|
458 |
+
GlobalConfig.model_rebuild()
|
459 |
+
VersionConfig.model_rebuild()
|
460 |
+
ServiceConfig.model_rebuild()
|