File size: 11,953 Bytes
abb0e3b
 
 
3047008
7d1a49d
 
abb0e3b
bbc337b
8fe6b8c
 
91a9883
 
 
 
 
8fe6b8c
abb0e3b
bbc337b
abb0e3b
 
 
 
 
 
bbc337b
abb0e3b
 
 
 
 
 
 
61eaacc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
abb0e3b
 
bbc337b
abb0e3b
 
 
 
 
 
bbc337b
25583bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f889f6d
25583bb
 
 
 
 
 
 
abb0e3b
 
25583bb
 
abb0e3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbc337b
abb0e3b
 
 
 
bbc337b
abb0e3b
 
 
 
 
bbc337b
abb0e3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbc337b
abb0e3b
 
 
 
 
 
 
 
 
 
e59e68d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
abb0e3b
 
 
bbc337b
abb0e3b
 
 
 
 
 
 
 
 
 
 
bbc337b
abb0e3b
 
 
 
 
 
 
bbc337b
abb0e3b
 
 
 
 
 
 
 
 
68dd4eb
abb0e3b
68dd4eb
abb0e3b
43c2252
 
abb0e3b
 
bbc337b
abb0e3b
 
 
 
 
 
 
 
 
 
 
 
 
68dd4eb
abb0e3b
68dd4eb
abb0e3b
 
 
 
bbc337b
abb0e3b
 
 
 
 
bbc337b
abb0e3b
 
 
 
 
 
 
 
bbc337b
abb0e3b
 
 
22bf2d3
abb0e3b
 
bbc337b
abb0e3b
 
 
 
 
 
 
 
 
 
 
 
68dd4eb
abb0e3b
68dd4eb
abb0e3b
 
 
 
bbc337b
4f987f8
68dd4eb
abb0e3b
 
 
 
 
 
 
bbc337b
b483f1d
abb0e3b
 
 
 
68dd4eb
abb0e3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
"""
Configuration Models for Flare Platform
"""
from pydantic import BaseModel, Field, field_serializer
from datetime import datetime
from typing import Optional, List, Dict, Any

class BaseModelWithDatetime(BaseModel):
    """Base model with consistent datetime serialization"""
    
    class Config:
        # Datetime'ları her zaman ISO 8601 formatında serialize et
        json_encoders = {
            datetime: lambda v: v.isoformat() if v else None
        }
        
# ===================== User & Auth =====================
class UserConfig(BaseModelWithDatetime):
    username: str
    password_hash: str
    salt: Optional[str] = None


# ===================== Provider Models =====================
class ProviderDefinition(BaseModelWithDatetime):
    type: str  # llm, tts, stt
    name: str
    display_name: str
    requires_endpoint: bool
    requires_api_key: bool
    requires_repo_info: Optional[bool] = False
    description: str
    features: Optional[Dict[str, Any]] = Field(default_factory=dict)
    
    def has_feature(self, feature_name: str) -> bool:
        """Check if provider has a specific feature"""
        return feature_name in self.features if self.features else False
    
    def get_feature(self, feature_name: str, default: Any = None) -> Any:
        """Get feature value with default"""
        if not self.features:
            return default
        return self.features.get(feature_name, default)
    
    def get_feature_bool(self, feature_name: str, default: bool = False) -> bool:
        """Get boolean feature value"""
        if not self.features:
            return default
        value = self.features.get(feature_name, default)
        if isinstance(value, bool):
            return value
        if isinstance(value, str):
            return value.lower() in ('true', '1', 'yes', 'on')
        return bool(value)
    
    def get_feature_int(self, feature_name: str, default: int = 0) -> int:
        """Get integer feature value"""
        if not self.features:
            return default
        value = self.features.get(feature_name, default)
        try:
            return int(value)
        except (ValueError, TypeError):
            return default
    
    def get_feature_str(self, feature_name: str, default: str = "") -> str:
        """Get string feature value"""
        if not self.features:
            return default
        value = self.features.get(feature_name, default)
        return str(value) if value is not None else default


class ProviderSettings(BaseModelWithDatetime):
    name: str
    api_key: Optional[str] = None
    endpoint: Optional[str] = None
    settings: Optional[Dict[str, Any]] = Field(default_factory=dict)

# ===================== Global Config =====================
class GlobalConfig(BaseModelWithDatetime):
    llm_provider: ProviderSettings = Field(
        default_factory=lambda: ProviderSettings(
            name="spark_cloud",
            api_key="",
            endpoint="http://localhost:8080",
            settings={}
        )
    )
    tts_provider: ProviderSettings = Field(
        default_factory=lambda: ProviderSettings(
            name="no_tts",
            api_key="",
            endpoint=None,
            settings={}
        )
    )
    stt_provider: ProviderSettings = Field(
        default_factory=lambda: ProviderSettings(
            name="no_stt",
            api_key="",
            endpoint=None,
            settings={}
        )
    )
    providers: List[ProviderDefinition] = Field(default_factory=list)
    users: List[UserConfig] = Field(default_factory=list)
    last_update_date: Optional[str] = None
    last_update_user: Optional[str] = None
    
    def is_cloud_mode(self) -> bool:
        """Check if running in cloud mode"""
        import os
        return bool(os.environ.get("SPACE_ID"))
    
    def get_provider_config(self, provider_type: str, provider_name: str) -> Optional[ProviderDefinition]:
        """Get provider definition by type and name"""
        return next(
            (p for p in self.providers if p.type == provider_type and p.name == provider_name),
            None
        )


# ===================== Localization Models =====================
class LocalizedExample(BaseModelWithDatetime):
    locale_code: str
    example: str


class LocalizedCaption(BaseModelWithDatetime):
    locale_code: str
    caption: str


# ===================== Parameter Models =====================
class ParameterConfig(BaseModelWithDatetime):
    name: str
    caption: List[LocalizedCaption]
    type: str  # str, int, float, bool, date
    required: bool = True
    variable_name: str
    extraction_prompt: Optional[str] = None
    validation_regex: Optional[str] = None
    invalid_prompt: Optional[str] = None
    type_error_prompt: Optional[str] = None
    
    def canonical_type(self) -> str:
        """Get canonical type name"""
        return self.type.lower()
    
    def get_caption_for_locale(self, locale: str) -> str:
        """Get caption for specific locale"""
        for cap in self.caption:
            if cap.locale_code == locale:
                return cap.caption
        # Fallback to first caption
        return self.caption[0].caption if self.caption else self.name


# ===================== Intent Models =====================
class IntentConfig(BaseModelWithDatetime):
    name: str
    caption: str
    requiresApproval: Optional[bool] = False
    dependencies: List[str] = Field(default_factory=list)
    examples: List[LocalizedExample] = Field(default_factory=list)
    detection_prompt: str
    parameters: List[ParameterConfig] = Field(default_factory=list)
    action: str
    fallback_timeout_prompt: Optional[str] = None
    fallback_error_prompt: Optional[str] = None
    
    def get_examples_for_locale(self, locale: str) -> List[str]:
        """Get examples for specific locale"""
        examples = []
        for ex in self.examples:
            if ex.locale_code == locale:
                examples.append(ex.example)
        
        # Fallback to any available examples if locale not found
        if not examples and self.examples:
            # Try language part only (tr-TR -> tr)
            if '-' in locale:
                lang_code = locale.split('-')[0]
                for ex in self.examples:
                    if ex.locale_code.startswith(lang_code):
                        examples.append(ex.example)
        
        # If still no examples, return all examples
        if not examples:
            examples = [ex.example for ex in self.examples]
            
        return examples
    
    def get_examples_for_locale(self, locale: str) -> List[str]:
        """Get examples for specific locale"""
        examples = []
        for ex in self.examples:
            if ex.locale_code == locale:
                examples.append(ex.example)
        
        # Fallback to any available examples if locale not found
        if not examples and self.examples:
            # Try language part only (tr-TR -> tr)
            if '-' in locale:
                lang_code = locale.split('-')[0]
                for ex in self.examples:
                    if ex.locale_code.startswith(lang_code):
                        examples.append(ex.example)
        
        # If still no examples, return all examples
        if not examples:
            examples = [ex.example for ex in self.examples]
            
        return examples


# ===================== LLM Configuration =====================
class GenerationConfig(BaseModelWithDatetime):
    max_new_tokens: int = 512
    temperature: float = 0.7
    top_p: float = 0.9
    top_k: Optional[int] = None
    repetition_penalty: Optional[float] = None
    do_sample: Optional[bool] = True
    num_beams: Optional[int] = None
    length_penalty: Optional[float] = None
    early_stopping: Optional[bool] = None


class LLMConfiguration(BaseModelWithDatetime):
    repo_id: str
    generation_config: GenerationConfig = Field(default_factory=GenerationConfig)
    use_fine_tune: bool = False
    fine_tune_zip: Optional[str] = ""


# ===================== Version Models =====================
class VersionConfig(BaseModelWithDatetime):
    no: int
    caption: str
    description: Optional[str] = None
    published: bool = False
    deleted: bool = False
    general_prompt: str
    welcome_prompt: Optional[str] = None
    llm: LLMConfiguration
    intents: List[IntentConfig] = Field(default_factory=list)
    created_date: str
    created_by: str
    publish_date: Optional[str] = None
    published_by: Optional[str] = None
    last_update_date: Optional[str] = None
    last_update_user: Optional[str] = None

# ===================== Project Models =====================
class ProjectConfig(BaseModelWithDatetime):
    id: int
    name: str
    caption: str
    icon: Optional[str] = "folder"
    description: Optional[str] = None
    enabled: bool = True
    default_locale: str = "tr"
    supported_locales: List[str] = Field(default_factory=lambda: ["tr"])
    timezone: str = "Europe/Istanbul"
    region: str = "tr-TR"
    versions: List[VersionConfig] = Field(default_factory=list)
    version_id_counter: int = 1
    deleted: bool = False
    created_date: str
    created_by: str
    last_update_date: Optional[str] = None
    last_update_user: Optional[str] = None


# ===================== API Models =====================
class RetryConfig(BaseModelWithDatetime):
    retry_count: int = 3
    backoff_seconds: int = 2
    strategy: str = "static"  # static, exponential


class AuthConfig(BaseModelWithDatetime):
    enabled: bool
    token_endpoint: Optional[str] = None
    response_token_path: Optional[str] = None
    token_request_body: Optional[Dict[str, Any]] = None
    token_refresh_endpoint: Optional[str] = None
    token_refresh_body: Optional[Dict[str, Any]] = None


class ResponseMapping(BaseModelWithDatetime):
    variable_name: str
    type: str  # str, int, float, bool, date
    json_path: str
    caption: List[LocalizedCaption]


class APIConfig(BaseModelWithDatetime):
    name: str
    url: str
    method: str = "POST"
    headers: Dict[str, str] = Field(default_factory=dict)
    body_template: Dict[str, Any] = Field(default_factory=dict)
    timeout_seconds: int = 10
    retry: RetryConfig = Field(default_factory=RetryConfig)
    proxy: Optional[str] = None
    auth: Optional[AuthConfig] = None
    response_prompt: Optional[str] = None
    response_mappings: List[ResponseMapping] = Field(default_factory=list)
    deleted: bool = False
    created_date: Optional[str] = None
    created_by: Optional[str] = None
    last_update_date: Optional[str] = None
    last_update_user: Optional[str] = None


# ===================== Activity Log =====================
class ActivityLogEntry(BaseModelWithDatetime):
    id: Optional[int] = None 
    timestamp: str
    username: str
    action: str
    entity_type: str
    entity_name: Optional[str] = None
    details: Optional[str] = None

# ===================== Root Configuration =====================
class ServiceConfig(BaseModelWithDatetime):
    global_config: GlobalConfig = Field(alias="config")
    projects: List[ProjectConfig] = Field(default_factory=list)
    apis: List[APIConfig] = Field(default_factory=list)
    activity_log: List[ActivityLogEntry] = Field(default_factory=list)
    project_id_counter: int = 1
    last_update_date: Optional[str] = None
    last_update_user: Optional[str] = None
    
    class Config:
        populate_by_name = True
    
    def build_index(self) -> None:
        """Build indexes for quick lookup"""
        # This method can be extended to build various indexes
        pass
    
    def get_api(self, api_name: str) -> Optional[APIConfig]:
        """Get API config by name"""
        return next((api for api in self.apis if api.name == api_name), None)


# For backward compatibility - alias
GlobalConfiguration = GlobalConfig