Spaces:
Running
Running
Update config_provider.py
Browse files- config_provider.py +827 -833
config_provider.py
CHANGED
@@ -1,854 +1,848 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
import
|
5 |
-
import
|
6 |
-
import
|
7 |
-
import
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
import
|
12 |
-
import
|
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 |
-
with cls._lock:
|
59 |
-
log_info("Reloading configuration...")
|
60 |
-
cls._instance = None
|
61 |
-
return cls.get()
|
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 |
-
if
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
try:
|
154 |
-
# Load current config for race condition check
|
155 |
-
try:
|
156 |
-
current_config = cls._load()
|
157 |
-
|
158 |
-
# Check for race condition
|
159 |
-
if config.last_update_date and current_config.last_update_date:
|
160 |
-
if not timestamps_equal(config.last_update_date, current_config.last_update_date):
|
161 |
-
raise RaceConditionError(
|
162 |
-
"Configuration was modified by another user",
|
163 |
-
current_user=username,
|
164 |
-
last_update_user=current_config.last_update_user,
|
165 |
-
last_update_date=current_config.last_update_date,
|
166 |
-
entity_type="configuration"
|
167 |
-
)
|
168 |
-
except ConfigurationError as e:
|
169 |
-
# Eğer mevcut config yüklenemiyorsa, race condition kontrolünü atla
|
170 |
-
log_warning(f"Could not load current config for race condition check: {e}")
|
171 |
-
current_config = None
|
172 |
-
|
173 |
-
# Update metadata
|
174 |
-
config.last_update_date = get_current_timestamp()
|
175 |
-
config.last_update_user = username
|
176 |
-
|
177 |
-
# Convert to JSON - Pydantic v2 kullanımı
|
178 |
-
data = config.model_dump(mode='json')
|
179 |
-
json_str = json.dumps(data, ensure_ascii=False, indent=2)
|
180 |
-
|
181 |
-
# Backup current file if exists
|
182 |
-
backup_path = None
|
183 |
-
if cls._CONFIG_PATH.exists():
|
184 |
-
backup_path = cls._CONFIG_PATH.with_suffix('.backup')
|
185 |
-
shutil.copy2(str(cls._CONFIG_PATH), str(backup_path))
|
186 |
-
log_debug(f"Created backup at {backup_path}")
|
187 |
-
|
188 |
-
try:
|
189 |
-
# Write to temporary file first
|
190 |
-
temp_path = cls._CONFIG_PATH.with_suffix('.tmp')
|
191 |
-
with open(temp_path, 'w', encoding='utf-8') as f:
|
192 |
-
f.write(json_str)
|
193 |
-
|
194 |
-
# Validate the temp file by trying to load it
|
195 |
-
with open(temp_path, 'r', encoding='utf-8') as f:
|
196 |
-
test_data = commentjson.load(f)
|
197 |
-
ServiceConfig.model_validate(test_data)
|
198 |
-
|
199 |
-
# If validation passes, replace the original
|
200 |
-
shutil.move(str(temp_path), str(cls._CONFIG_PATH))
|
201 |
-
|
202 |
-
# Delete backup if save successful
|
203 |
-
if backup_path and backup_path.exists():
|
204 |
-
backup_path.unlink()
|
205 |
-
|
206 |
-
except Exception as e:
|
207 |
-
# Restore from backup if something went wrong
|
208 |
-
if backup_path and backup_path.exists():
|
209 |
-
shutil.move(str(backup_path), str(cls._CONFIG_PATH))
|
210 |
-
log_error(f"Restored configuration from backup due to error: {e}")
|
211 |
-
raise
|
212 |
-
|
213 |
-
# Update cached instance
|
214 |
-
with cls._lock:
|
215 |
-
cls._instance = config
|
216 |
-
|
217 |
-
log_info(
|
218 |
-
"Configuration saved successfully",
|
219 |
-
user=username,
|
220 |
-
last_update=config.last_update_date
|
221 |
-
)
|
222 |
-
|
223 |
-
except Exception as e:
|
224 |
-
log_error("Failed to save configuration", error=str(e), user=username)
|
225 |
-
raise
|
226 |
|
227 |
-
|
|
|
|
|
|
|
228 |
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
if 'llm_provider' in update_data:
|
237 |
-
config.global_config.llm_provider = update_data['llm_provider']
|
238 |
-
|
239 |
-
if 'tts_provider' in update_data:
|
240 |
-
config.global_config.tts_provider = update_data['tts_provider']
|
241 |
-
|
242 |
-
if 'stt_provider' in update_data:
|
243 |
-
config.global_config.stt_provider = update_data['stt_provider']
|
244 |
-
|
245 |
-
# Log activity
|
246 |
-
cls._add_activity(
|
247 |
-
config, username, "UPDATE_ENVIRONMENT",
|
248 |
-
"environment", None, None,
|
249 |
-
f"Updated providers"
|
250 |
-
)
|
251 |
-
|
252 |
-
# Save
|
253 |
-
cls.save(config, username)
|
254 |
|
255 |
-
|
|
|
|
|
256 |
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
config = cls.get()
|
261 |
-
return next((p for p in config.projects if p.id == project_id), None)
|
262 |
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
# Check for duplicate name
|
270 |
-
if any(p.name == project_data['name'] for p in config.projects):
|
271 |
-
raise DuplicateResourceError("project", project_data['name'])
|
272 |
-
|
273 |
-
# Create project
|
274 |
-
project = ProjectConfig(
|
275 |
-
id=config.project_id_counter,
|
276 |
-
created_date=get_current_timestamp(),
|
277 |
-
created_by=username,
|
278 |
-
version_id_counter=1, # Başlangıç değeri
|
279 |
-
versions=[], # Boş başla
|
280 |
-
**project_data
|
281 |
-
)
|
282 |
-
|
283 |
-
# Create initial version with proper models
|
284 |
-
initial_version = VersionConfig(
|
285 |
-
no=1,
|
286 |
-
caption="Initial version",
|
287 |
-
description="Auto-generated initial version",
|
288 |
-
published=False, # Explicitly set to False
|
289 |
-
deleted=False,
|
290 |
-
general_prompt="You are a helpful assistant.",
|
291 |
-
welcome_prompt=None,
|
292 |
-
llm=LLMConfiguration(
|
293 |
-
repo_id="ytu-ce-cosmos/Turkish-Llama-8b-Instruct-v0.1",
|
294 |
-
generation_config=GenerationConfig(
|
295 |
-
max_new_tokens=512,
|
296 |
-
temperature=0.7,
|
297 |
-
top_p=0.9,
|
298 |
-
repetition_penalty=1.1,
|
299 |
-
do_sample=True
|
300 |
-
),
|
301 |
-
use_fine_tune=False,
|
302 |
-
fine_tune_zip=""
|
303 |
-
),
|
304 |
-
intents=[],
|
305 |
-
created_date=get_current_timestamp(),
|
306 |
-
created_by=username,
|
307 |
-
last_update_date=None,
|
308 |
-
last_update_user=None,
|
309 |
-
publish_date=None,
|
310 |
-
published_by=None
|
311 |
-
)
|
312 |
-
|
313 |
-
# Add initial version to project
|
314 |
-
project.versions.append(initial_version)
|
315 |
-
project.version_id_counter = 2 # Next version will be 2
|
316 |
-
|
317 |
-
# Update config
|
318 |
-
config.projects.append(project)
|
319 |
-
config.project_id_counter += 1
|
320 |
-
|
321 |
-
# Log activity
|
322 |
-
cls._add_activity(
|
323 |
-
config, username, "CREATE_PROJECT",
|
324 |
-
"project", project.id, project.name,
|
325 |
-
f"Created with initial version"
|
326 |
-
)
|
327 |
-
|
328 |
-
# Save
|
329 |
-
cls.save(config, username)
|
330 |
-
|
331 |
-
log_info(
|
332 |
-
"Project created with initial version",
|
333 |
-
project_id=project.id,
|
334 |
-
name=project.name,
|
335 |
-
user=username
|
336 |
-
)
|
337 |
-
|
338 |
-
return project
|
339 |
-
|
340 |
-
@classmethod
|
341 |
-
def update_project(cls, project_id: int, update_data: dict, username: str, expected_last_update: Optional[str] = None) -> ProjectConfig:
|
342 |
-
"""Update project with optimistic locking"""
|
343 |
-
with cls._lock:
|
344 |
-
config = cls.get()
|
345 |
-
project = cls.get_project(project_id)
|
346 |
-
|
347 |
-
if not project:
|
348 |
-
raise ResourceNotFoundError("project", project_id)
|
349 |
-
|
350 |
-
# Check race condition
|
351 |
-
if expected_last_update is not None and expected_last_update != '':
|
352 |
-
if project.last_update_date and not timestamps_equal(expected_last_update, project.last_update_date):
|
353 |
-
raise RaceConditionError(
|
354 |
-
f"Project '{project.name}' was modified by another user",
|
355 |
-
current_user=username,
|
356 |
-
last_update_user=project.last_update_user,
|
357 |
-
last_update_date=project.last_update_date,
|
358 |
-
entity_type="project",
|
359 |
-
entity_id=project_id
|
360 |
-
)
|
361 |
-
|
362 |
-
# Update fields
|
363 |
-
for key, value in update_data.items():
|
364 |
-
if hasattr(project, key) and key not in ['id', 'created_date', 'created_by', 'last_update_date', 'last_update_user']:
|
365 |
-
setattr(project, key, value)
|
366 |
-
|
367 |
-
project.last_update_date = get_current_timestamp()
|
368 |
-
project.last_update_user = username
|
369 |
-
|
370 |
-
# Log activity
|
371 |
-
cls._add_activity(
|
372 |
-
config, username, "UPDATE_PROJECT",
|
373 |
-
"project", project.id, project.name
|
374 |
-
)
|
375 |
-
|
376 |
-
# Save
|
377 |
-
cls.save(config, username)
|
378 |
-
|
379 |
-
log_info(
|
380 |
-
"Project updated",
|
381 |
-
project_id=project.id,
|
382 |
-
user=username
|
383 |
-
)
|
384 |
-
|
385 |
-
return project
|
386 |
|
387 |
-
|
388 |
-
|
389 |
-
"""Soft delete project"""
|
390 |
-
with cls._lock:
|
391 |
-
config = cls.get()
|
392 |
-
project = cls.get_project(project_id)
|
393 |
-
|
394 |
-
if not project:
|
395 |
-
raise ResourceNotFoundError("project", project_id)
|
396 |
-
|
397 |
-
project.deleted = True
|
398 |
-
project.last_update_date = get_current_timestamp()
|
399 |
-
project.last_update_user = username
|
400 |
-
|
401 |
-
# Log activity
|
402 |
-
cls._add_activity(
|
403 |
-
config, username, "DELETE_PROJECT",
|
404 |
-
"project", project.id, project.name
|
405 |
-
)
|
406 |
-
|
407 |
-
# Save
|
408 |
-
cls.save(config, username)
|
409 |
-
|
410 |
-
log_info(
|
411 |
-
"Project deleted",
|
412 |
-
project_id=project.id,
|
413 |
-
user=username
|
414 |
-
)
|
415 |
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
config = cls.get()
|
421 |
-
project = cls.get_project(project_id)
|
422 |
-
|
423 |
-
if not project:
|
424 |
-
raise ResourceNotFoundError("project", project_id)
|
425 |
-
|
426 |
-
project.enabled = not project.enabled
|
427 |
-
project.last_update_date = get_current_timestamp()
|
428 |
-
project.last_update_user = username
|
429 |
-
|
430 |
-
# Log activity
|
431 |
-
cls._add_activity(
|
432 |
-
config, username, "TOGGLE_PROJECT",
|
433 |
-
"project", project.id, project.name,
|
434 |
-
f"{'Enabled' if project.enabled else 'Disabled'}"
|
435 |
-
)
|
436 |
-
|
437 |
-
# Save
|
438 |
-
cls.save(config, username)
|
439 |
-
|
440 |
-
log_info(
|
441 |
-
"Project toggled",
|
442 |
-
project_id=project.id,
|
443 |
-
enabled=project.enabled,
|
444 |
-
user=username
|
445 |
-
)
|
446 |
-
|
447 |
-
return project.enabled
|
448 |
|
449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
with cls._lock:
|
455 |
-
config = cls.get()
|
456 |
-
project = cls.get_project(project_id)
|
457 |
-
|
458 |
-
if not project:
|
459 |
-
raise ResourceNotFoundError("project", project_id)
|
460 |
-
|
461 |
-
# Handle source version copy
|
462 |
-
if 'source_version_no' in version_data and version_data['source_version_no']:
|
463 |
-
source_version = next((v for v in project.versions if v.no == version_data['source_version_no']), None)
|
464 |
-
if source_version:
|
465 |
-
# Copy from source version
|
466 |
-
version_dict = source_version.model_dump()
|
467 |
-
# Remove fields that shouldn't be copied
|
468 |
-
for field in ['no', 'created_date', 'created_by', 'published', 'publish_date',
|
469 |
-
'published_by', 'last_update_date', 'last_update_user']:
|
470 |
-
version_dict.pop(field, None)
|
471 |
-
# Override with provided data
|
472 |
-
version_dict['caption'] = version_data.get('caption', f"Copy of {source_version.caption}")
|
473 |
-
else:
|
474 |
-
# Source not found, create blank
|
475 |
-
version_dict = {
|
476 |
-
'caption': version_data.get('caption', 'New Version'),
|
477 |
-
'general_prompt': '',
|
478 |
-
'welcome_prompt': None,
|
479 |
-
'llm': {
|
480 |
-
'repo_id': '',
|
481 |
-
'generation_config': {
|
482 |
-
'max_new_tokens': 512,
|
483 |
-
'temperature': 0.7,
|
484 |
-
'top_p': 0.95,
|
485 |
-
'repetition_penalty': 1.1
|
486 |
-
},
|
487 |
-
'use_fine_tune': False,
|
488 |
-
'fine_tune_zip': ''
|
489 |
-
},
|
490 |
-
'intents': []
|
491 |
-
}
|
492 |
-
else:
|
493 |
-
# Create blank version
|
494 |
-
version_dict = {
|
495 |
-
'caption': version_data.get('caption', 'New Version'),
|
496 |
-
'general_prompt': '',
|
497 |
-
'welcome_prompt': None,
|
498 |
-
'llm': {
|
499 |
-
'repo_id': '',
|
500 |
-
'generation_config': {
|
501 |
-
'max_new_tokens': 512,
|
502 |
-
'temperature': 0.7,
|
503 |
-
'top_p': 0.95,
|
504 |
-
'repetition_penalty': 1.1
|
505 |
-
},
|
506 |
-
'use_fine_tune': False,
|
507 |
-
'fine_tune_zip': ''
|
508 |
-
},
|
509 |
-
'intents': []
|
510 |
-
}
|
511 |
-
|
512 |
-
# Create version
|
513 |
-
version = VersionConfig(
|
514 |
-
no=project.version_id_counter,
|
515 |
-
published=False, # New versions are always unpublished
|
516 |
-
deleted=False,
|
517 |
-
created_date=get_current_timestamp(),
|
518 |
-
created_by=username,
|
519 |
-
**version_dict
|
520 |
-
)
|
521 |
-
|
522 |
-
# Update project
|
523 |
-
project.versions.append(version)
|
524 |
-
project.version_id_counter += 1
|
525 |
-
project.last_update_date = get_current_timestamp()
|
526 |
-
project.last_update_user = username
|
527 |
-
|
528 |
-
# Log activity
|
529 |
-
cls._add_activity(
|
530 |
-
config, username, "CREATE_VERSION",
|
531 |
-
"version", version.no, f"{project.name} v{version.no}",
|
532 |
-
f"Project: {project.name}"
|
533 |
-
)
|
534 |
-
|
535 |
-
# Save
|
536 |
-
cls.save(config, username)
|
537 |
-
|
538 |
-
log_info(
|
539 |
-
"Version created",
|
540 |
-
project_id=project.id,
|
541 |
-
version_no=version.no,
|
542 |
-
user=username
|
543 |
-
)
|
544 |
-
|
545 |
-
return version
|
546 |
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
def update_version(cls, project_id: int, version_no: int, update_data: dict, username: str, expected_last_update: Optional[str] = None) -> VersionConfig:
|
596 |
-
"""Update version with optimistic locking"""
|
597 |
-
with cls._lock:
|
598 |
-
config = cls.get()
|
599 |
-
project = cls.get_project(project_id)
|
600 |
-
|
601 |
-
if not project:
|
602 |
-
raise ResourceNotFoundError("project", project_id)
|
603 |
-
|
604 |
-
version = next((v for v in project.versions if v.no == version_no), None)
|
605 |
-
if not version:
|
606 |
-
raise ResourceNotFoundError("version", version_no)
|
607 |
-
|
608 |
-
# Published versions cannot be edited
|
609 |
-
if version.published:
|
610 |
-
raise ValidationError("Published versions cannot be modified")
|
611 |
-
|
612 |
-
# Check race condition
|
613 |
-
if expected_last_update is not None and expected_last_update != '':
|
614 |
-
if version.last_update_date and not timestamps_equal(expected_last_update, version.last_update_date):
|
615 |
-
raise RaceConditionError(
|
616 |
-
f"Version '{version.no}' was modified by another user",
|
617 |
-
current_user=username,
|
618 |
-
last_update_user=version.last_update_user,
|
619 |
-
last_update_date=version.last_update_date,
|
620 |
-
entity_type="version",
|
621 |
-
entity_id=f"{project_id}:{version_no}"
|
622 |
-
)
|
623 |
-
|
624 |
-
# Update fields
|
625 |
-
for key, value in update_data.items():
|
626 |
-
if hasattr(version, key) and key not in ['no', 'created_date', 'created_by', 'published', 'last_update_date']:
|
627 |
-
setattr(version, key, value)
|
628 |
-
|
629 |
-
version.last_update_date = get_current_timestamp()
|
630 |
-
version.last_update_user = username
|
631 |
-
|
632 |
-
# Update project last update
|
633 |
-
project.last_update_date = get_current_timestamp()
|
634 |
-
project.last_update_user = username
|
635 |
-
|
636 |
-
# Log activity
|
637 |
-
cls._add_activity(
|
638 |
-
config, username, "UPDATE_VERSION",
|
639 |
-
"version", f"{project.id}:{version.no}", f"{project.name} v{version.no}"
|
640 |
-
)
|
641 |
-
|
642 |
-
# Save
|
643 |
-
cls.save(config, username)
|
644 |
-
|
645 |
-
log_info(
|
646 |
-
"Version updated",
|
647 |
-
project_id=project.id,
|
648 |
-
version_no=version.no,
|
649 |
-
user=username
|
650 |
-
)
|
651 |
-
|
652 |
-
return version
|
653 |
|
654 |
-
|
655 |
-
|
656 |
-
"""Soft delete version"""
|
657 |
-
with cls._lock:
|
658 |
-
config = cls.get()
|
659 |
-
project = cls.get_project(project_id)
|
660 |
-
|
661 |
-
if not project:
|
662 |
-
raise ResourceNotFoundError("project", project_id)
|
663 |
-
|
664 |
-
version = next((v for v in project.versions if v.no == version_no), None)
|
665 |
-
if not version:
|
666 |
-
raise ResourceNotFoundError("version", version_no)
|
667 |
-
|
668 |
-
if version.published:
|
669 |
-
raise ValidationError("Cannot delete published version")
|
670 |
-
|
671 |
-
version.deleted = True
|
672 |
-
version.last_update_date = get_current_timestamp()
|
673 |
-
version.last_update_user = username
|
674 |
-
|
675 |
-
# Update project
|
676 |
-
project.last_update_date = get_current_timestamp()
|
677 |
-
project.last_update_user = username
|
678 |
-
|
679 |
-
# Log activity
|
680 |
-
cls._add_activity(
|
681 |
-
config, username, "DELETE_VERSION",
|
682 |
-
"version", f"{project.id}:{version.no}", f"{project.name} v{version.no}"
|
683 |
-
)
|
684 |
-
|
685 |
-
# Save
|
686 |
-
cls.save(config, username)
|
687 |
-
|
688 |
-
log_info(
|
689 |
-
"Version deleted",
|
690 |
-
project_id=project.id,
|
691 |
-
version_no=version.no,
|
692 |
-
user=username
|
693 |
-
)
|
694 |
|
695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
696 |
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
736 |
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
if hasattr(api, key) and key not in ['name', 'created_date', 'created_by', 'last_update_date']:
|
762 |
-
setattr(api, key, value)
|
763 |
-
|
764 |
-
api.last_update_date = get_current_timestamp()
|
765 |
-
api.last_update_user = username
|
766 |
-
|
767 |
-
# Rebuild index
|
768 |
-
config.build_index()
|
769 |
-
|
770 |
-
# Log activity
|
771 |
-
cls._add_activity(
|
772 |
-
config, username, "UPDATE_API",
|
773 |
-
"api", api.name, api.name
|
774 |
-
)
|
775 |
-
|
776 |
-
# Save
|
777 |
-
cls.save(config, username)
|
778 |
-
|
779 |
-
log_info(
|
780 |
-
"API updated",
|
781 |
-
api_name=api.name,
|
782 |
-
user=username
|
783 |
-
)
|
784 |
-
|
785 |
-
return api
|
786 |
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
818 |
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Component, Inject, OnInit } from '@angular/core';
|
2 |
+
import { CommonModule } from '@angular/common';
|
3 |
+
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule, FormArray, FormsModule } from '@angular/forms';
|
4 |
+
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
|
5 |
+
import { MatTabsModule } from '@angular/material/tabs';
|
6 |
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
7 |
+
import { MatInputModule } from '@angular/material/input';
|
8 |
+
import { MatSelectModule } from '@angular/material/select';
|
9 |
+
import { MatCheckboxModule } from '@angular/material/checkbox';
|
10 |
+
import { MatButtonModule } from '@angular/material/button';
|
11 |
+
import { MatIconModule } from '@angular/material/icon';
|
12 |
+
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
13 |
+
import { MatTableModule } from '@angular/material/table';
|
14 |
+
import { MatChipsModule } from '@angular/material/chips';
|
15 |
+
import { MatExpansionModule } from '@angular/material/expansion';
|
16 |
+
import { MatDividerModule } from '@angular/material/divider';
|
17 |
+
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
18 |
+
import { MatListModule } from '@angular/material/list';
|
19 |
+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
20 |
+
import { MatBadgeModule } from '@angular/material/badge';
|
21 |
+
import { ApiService, Project, Version } from '../../services/api.service';
|
22 |
+
import { LocaleManagerService } from '../../services/locale-manager.service';
|
23 |
+
import ConfirmDialogComponent from '../confirm-dialog/confirm-dialog.component';
|
24 |
+
|
25 |
+
// Interfaces for multi-language support
|
26 |
+
interface LocalizedExample {
|
27 |
+
locale_code: string;
|
28 |
+
example: string;
|
29 |
+
}
|
30 |
+
|
31 |
+
interface LocalizedCaption {
|
32 |
+
locale_code: string;
|
33 |
+
caption: string;
|
34 |
+
}
|
35 |
+
|
36 |
+
interface Locale {
|
37 |
+
code: string;
|
38 |
+
name: string;
|
39 |
+
}
|
40 |
+
|
41 |
+
@Component({
|
42 |
+
selector: 'app-version-edit-dialog',
|
43 |
+
standalone: true,
|
44 |
+
imports: [
|
45 |
+
CommonModule,
|
46 |
+
ReactiveFormsModule,
|
47 |
+
FormsModule,
|
48 |
+
MatDialogModule,
|
49 |
+
MatTabsModule,
|
50 |
+
MatFormFieldModule,
|
51 |
+
MatInputModule,
|
52 |
+
MatSelectModule,
|
53 |
+
MatCheckboxModule,
|
54 |
+
MatButtonModule,
|
55 |
+
MatIconModule,
|
56 |
+
MatSnackBarModule,
|
57 |
+
MatTableModule,
|
58 |
+
MatChipsModule,
|
59 |
+
MatExpansionModule,
|
60 |
+
MatDividerModule,
|
61 |
+
MatProgressBarModule,
|
62 |
+
MatListModule,
|
63 |
+
MatProgressSpinnerModule,
|
64 |
+
MatBadgeModule
|
65 |
+
],
|
66 |
+
templateUrl: './version-edit-dialog.component.html',
|
67 |
+
styleUrls: ['./version-edit-dialog.component.scss']
|
68 |
+
})
|
69 |
+
export default class VersionEditDialogComponent implements OnInit {
|
70 |
+
project: Project;
|
71 |
+
versions: Version[] = [];
|
72 |
+
selectedVersion: Version | null = null;
|
73 |
+
versionForm!: FormGroup;
|
74 |
+
|
75 |
+
loading = false;
|
76 |
+
saving = false;
|
77 |
+
publishing = false;
|
78 |
+
creating = false;
|
79 |
+
isDirty = false;
|
80 |
+
testing = false;
|
81 |
+
|
82 |
+
selectedTabIndex = 0;
|
83 |
+
testUserMessage = '';
|
84 |
+
testResult: any = null;
|
85 |
+
|
86 |
+
// Multi-language support
|
87 |
+
selectedExampleLocale: string = 'tr';
|
88 |
+
availableLocales: Locale[] = [];
|
89 |
+
|
90 |
+
constructor(
|
91 |
+
private fb: FormBuilder,
|
92 |
+
private apiService: ApiService,
|
93 |
+
private localeService: LocaleManagerService,
|
94 |
+
private snackBar: MatSnackBar,
|
95 |
+
private dialog: MatDialog,
|
96 |
+
public dialogRef: MatDialogRef<VersionEditDialogComponent>,
|
97 |
+
@Inject(MAT_DIALOG_DATA) public data: any
|
98 |
+
) {
|
99 |
+
this.project = data.project;
|
100 |
+
this.versions = [...this.project.versions].sort((a, b) => b.no - a.no);
|
101 |
+
this.selectedExampleLocale = this.project.default_locale || 'tr';
|
102 |
+
}
|
103 |
+
|
104 |
+
ngOnInit() {
|
105 |
+
this.initializeForm();
|
106 |
+
this.loadAvailableLocales();
|
107 |
|
108 |
+
// Select the latest unpublished version or the latest version
|
109 |
+
const unpublished = this.versions.find(v => !v.published);
|
110 |
+
this.selectedVersion = unpublished || this.versions[0] || null;
|
|
|
|
|
|
|
|
|
111 |
|
112 |
+
if (this.selectedVersion) {
|
113 |
+
this.loadVersion(this.selectedVersion);
|
114 |
+
}
|
115 |
+
|
116 |
+
this.versionForm.valueChanges.subscribe(() => {
|
117 |
+
this.isDirty = true;
|
118 |
+
});
|
119 |
+
}
|
120 |
+
|
121 |
+
initializeForm() {
|
122 |
+
this.versionForm = this.fb.group({
|
123 |
+
no: [{value: '', disabled: true}],
|
124 |
+
caption: ['', Validators.required],
|
125 |
+
published: [{value: false, disabled: true}],
|
126 |
+
general_prompt: ['', Validators.required],
|
127 |
+
welcome_prompt: [''], // Added welcome_prompt field
|
128 |
+
llm: this.fb.group({
|
129 |
+
repo_id: ['', Validators.required],
|
130 |
+
generation_config: this.fb.group({
|
131 |
+
max_new_tokens: [256, [Validators.required, Validators.min(1), Validators.max(2048)]],
|
132 |
+
temperature: [0.2, [Validators.required, Validators.min(0), Validators.max(2)]],
|
133 |
+
top_p: [0.8, [Validators.required, Validators.min(0), Validators.max(1)]],
|
134 |
+
repetition_penalty: [1.1, [Validators.required, Validators.min(1), Validators.max(2)]]
|
135 |
+
}),
|
136 |
+
use_fine_tune: [false],
|
137 |
+
fine_tune_zip: ['']
|
138 |
+
}),
|
139 |
+
intents: this.fb.array([]),
|
140 |
+
last_update_date: ['']
|
141 |
+
});
|
142 |
+
|
143 |
+
// Watch for fine-tune toggle
|
144 |
+
this.versionForm.get('llm.use_fine_tune')?.valueChanges.subscribe(useFineTune => {
|
145 |
+
const fineTuneControl = this.versionForm.get('llm.fine_tune_zip');
|
146 |
+
if (useFineTune) {
|
147 |
+
fineTuneControl?.setValidators([Validators.required]);
|
148 |
+
} else {
|
149 |
+
fineTuneControl?.clearValidators();
|
150 |
+
fineTuneControl?.setValue('');
|
151 |
+
}
|
152 |
+
fineTuneControl?.updateValueAndValidity();
|
153 |
+
});
|
154 |
+
}
|
155 |
+
|
156 |
+
async loadAvailableLocales() {
|
157 |
+
// Get supported locales from project
|
158 |
+
const supportedCodes = [
|
159 |
+
this.project.default_locale,
|
160 |
+
...(this.project.supported_locales || [])
|
161 |
+
].filter(Boolean);
|
162 |
+
|
163 |
+
// Get locale details
|
164 |
+
for (const code of supportedCodes) {
|
165 |
+
if (!code) continue; // Skip undefined/null values
|
166 |
+
|
167 |
+
try {
|
168 |
+
const localeInfo = await this.localeService.getLocaleDetails(code).toPromise();
|
169 |
+
if (localeInfo) {
|
170 |
+
this.availableLocales.push({
|
171 |
+
code: localeInfo.code,
|
172 |
+
name: localeInfo.name
|
173 |
+
});
|
174 |
+
}
|
175 |
+
} catch (error) {
|
176 |
+
// Use fallback for known locales
|
177 |
+
const fallbackNames: { [key: string]: string } = {
|
178 |
+
'tr': 'Türkçe',
|
179 |
+
'en': 'English',
|
180 |
+
'de': 'Deutsch',
|
181 |
+
'fr': 'Français',
|
182 |
+
'es': 'Español'
|
183 |
+
};
|
184 |
+
if (code && fallbackNames[code]) {
|
185 |
+
this.availableLocales.push({
|
186 |
+
code: code,
|
187 |
+
name: fallbackNames[code]
|
188 |
+
});
|
189 |
+
}
|
190 |
+
}
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
getAvailableLocales(): Locale[] {
|
195 |
+
return this.availableLocales;
|
196 |
+
}
|
197 |
+
|
198 |
+
getLocaleName(localeCode: string): string {
|
199 |
+
const locale = this.availableLocales.find(l => l.code === localeCode);
|
200 |
+
return locale?.name || localeCode;
|
201 |
+
}
|
202 |
+
|
203 |
+
loadVersion(version: Version) {
|
204 |
+
this.selectedVersion = version;
|
205 |
|
206 |
+
// Form değerlerini set et
|
207 |
+
this.versionForm.patchValue({
|
208 |
+
no: version.no,
|
209 |
+
caption: version.caption || '',
|
210 |
+
published: version.published || false,
|
211 |
+
general_prompt: (version as any).general_prompt || '',
|
212 |
+
welcome_prompt: (version as any).welcome_prompt || '', // Added welcome_prompt
|
213 |
+
last_update_date: version.last_update_date || ''
|
214 |
+
});
|
215 |
+
|
216 |
+
// LLM config'i ayrı set et
|
217 |
+
if ((version as any).llm) {
|
218 |
+
this.versionForm.patchValue({
|
219 |
+
llm: {
|
220 |
+
repo_id: (version as any).llm.repo_id || '',
|
221 |
+
generation_config: (version as any).llm.generation_config || {
|
222 |
+
max_new_tokens: 512,
|
223 |
+
temperature: 0.7,
|
224 |
+
top_p: 0.95,
|
225 |
+
repetition_penalty: 1.1
|
226 |
+
},
|
227 |
+
use_fine_tune: (version as any).llm.use_fine_tune || false,
|
228 |
+
fine_tune_zip: (version as any).llm.fine_tune_zip || ''
|
229 |
+
}
|
230 |
+
});
|
231 |
+
}
|
232 |
+
|
233 |
+
// Clear and rebuild intents
|
234 |
+
this.intents.clear();
|
235 |
+
((version as any).intents || []).forEach((intent: any) => {
|
236 |
+
this.intents.push(this.createIntentFormGroup(intent));
|
237 |
+
});
|
238 |
+
|
239 |
+
this.isDirty = false;
|
240 |
+
}
|
241 |
+
|
242 |
+
async loadVersions() {
|
243 |
+
this.loading = true;
|
244 |
+
try {
|
245 |
+
const project = await this.apiService.getProject(this.project.id).toPromise();
|
246 |
+
if (project) {
|
247 |
+
this.project = project;
|
248 |
+
this.versions = [...project.versions].sort((a, b) => b.no - a.no);
|
249 |
|
250 |
+
// Re-select current version if it still exists
|
251 |
+
if (this.selectedVersion) {
|
252 |
+
const currentVersion = this.versions.find(v => v.no === this.selectedVersion!.no);
|
253 |
+
if (currentVersion) {
|
254 |
+
this.loadVersion(currentVersion);
|
255 |
+
} else if (this.versions.length > 0) {
|
256 |
+
this.loadVersion(this.versions[0]);
|
257 |
+
}
|
258 |
+
} else if (this.versions.length > 0) {
|
259 |
+
this.loadVersion(this.versions[0]);
|
260 |
+
}
|
261 |
+
}
|
262 |
+
} catch (error) {
|
263 |
+
this.snackBar.open('Failed to reload versions', 'Close', { duration: 3000 });
|
264 |
+
} finally {
|
265 |
+
this.loading = false;
|
266 |
+
}
|
267 |
+
}
|
268 |
+
|
269 |
+
createIntentFormGroup(intent: any = {}): FormGroup {
|
270 |
+
const group = this.fb.group({
|
271 |
+
name: [intent.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9-]+$/)]],
|
272 |
+
caption: [intent.caption || ''],
|
273 |
+
detection_prompt: [intent.detection_prompt || '', Validators.required],
|
274 |
+
examples: [intent.examples || []], // Store as array, not FormArray
|
275 |
+
parameters: this.fb.array([]),
|
276 |
+
action: [intent.action || '', Validators.required],
|
277 |
+
fallback_timeout_prompt: [intent.fallback_timeout_prompt || ''],
|
278 |
+
fallback_error_prompt: [intent.fallback_error_prompt || '']
|
279 |
+
});
|
280 |
+
|
281 |
+
// Parameters'ı ayrı olarak ekle
|
282 |
+
if (intent.parameters && Array.isArray(intent.parameters)) {
|
283 |
+
const parametersArray = group.get('parameters') as FormArray;
|
284 |
+
intent.parameters.forEach((param: any) => {
|
285 |
+
parametersArray.push(this.createParameterFormGroup(param));
|
286 |
+
});
|
287 |
+
}
|
288 |
+
|
289 |
+
return group;
|
290 |
+
}
|
291 |
+
|
292 |
+
createParameterFormGroup(param: any = {}): FormGroup {
|
293 |
+
return this.fb.group({
|
294 |
+
name: [param.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9_]+$/)]],
|
295 |
+
caption: [param.caption || []],
|
296 |
+
type: [param.type || 'str', Validators.required],
|
297 |
+
required: [param.required !== false],
|
298 |
+
variable_name: [param.variable_name || '', Validators.required],
|
299 |
+
extraction_prompt: [param.extraction_prompt || ''],
|
300 |
+
validation_regex: [param.validation_regex || ''],
|
301 |
+
invalid_prompt: [param.invalid_prompt || ''],
|
302 |
+
type_error_prompt: [param.type_error_prompt || '']
|
303 |
+
});
|
304 |
+
}
|
305 |
+
|
306 |
+
get intents() {
|
307 |
+
return this.versionForm.get('intents') as FormArray;
|
308 |
+
}
|
309 |
+
|
310 |
+
getIntentParameters(intentIndex: number): FormArray {
|
311 |
+
return this.intents.at(intentIndex).get('parameters') as FormArray;
|
312 |
+
}
|
313 |
+
|
314 |
+
// LocalizedExample support methods
|
315 |
+
getLocalizedExamples(examples: any[], locale: string): LocalizedExample[] {
|
316 |
+
if (!examples || !Array.isArray(examples)) return [];
|
317 |
|
318 |
+
// Check if examples are in new format
|
319 |
+
if (examples.length > 0 && typeof examples[0] === 'object' && 'locale_code' in examples[0]) {
|
320 |
+
return examples.filter(ex => ex.locale_code === locale);
|
321 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
322 |
|
323 |
+
// Old format - convert to new
|
324 |
+
if (typeof examples[0] === 'string') {
|
325 |
+
return examples.map(ex => ({ locale_code: locale, example: ex }));
|
326 |
+
}
|
327 |
|
328 |
+
return [];
|
329 |
+
}
|
330 |
+
|
331 |
+
getParameterCaptionDisplay(captions: LocalizedCaption[]): string {
|
332 |
+
if (!captions || !Array.isArray(captions) || captions.length === 0) {
|
333 |
+
return '(No caption)';
|
334 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
335 |
|
336 |
+
// Try to find caption for selected locale
|
337 |
+
const selectedCaption = captions.find(c => c.locale_code === this.selectedExampleLocale);
|
338 |
+
if (selectedCaption) return selectedCaption.caption;
|
339 |
|
340 |
+
// Try default locale
|
341 |
+
const defaultCaption = captions.find(c => c.locale_code === this.project.default_locale);
|
342 |
+
if (defaultCaption) return defaultCaption.caption;
|
|
|
|
|
343 |
|
344 |
+
// Return first available caption
|
345 |
+
return captions[0].caption;
|
346 |
+
}
|
347 |
+
|
348 |
+
addLocalizedExample(intentIndex: number, example: string) {
|
349 |
+
if (!example.trim()) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
|
351 |
+
const intent = this.intents.at(intentIndex);
|
352 |
+
const currentExamples = intent.get('examples')?.value || [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
353 |
|
354 |
+
// Check if already exists
|
355 |
+
const exists = currentExamples.some((ex: any) =>
|
356 |
+
ex.locale_code === this.selectedExampleLocale && ex.example === example.trim()
|
357 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
358 |
|
359 |
+
if (!exists) {
|
360 |
+
const newExamples = [...currentExamples, {
|
361 |
+
locale_code: this.selectedExampleLocale,
|
362 |
+
example: example.trim()
|
363 |
+
}];
|
364 |
+
intent.patchValue({ examples: newExamples });
|
365 |
+
this.isDirty = true;
|
366 |
+
}
|
367 |
+
}
|
368 |
+
|
369 |
+
removeLocalizedExample(intentIndex: number, exampleToRemove: LocalizedExample) {
|
370 |
+
const intent = this.intents.at(intentIndex);
|
371 |
+
const currentExamples = intent.get('examples')?.value || [];
|
372 |
|
373 |
+
const newExamples = currentExamples.filter((ex: any) =>
|
374 |
+
!(ex.locale_code === exampleToRemove.locale_code && ex.example === exampleToRemove.example)
|
375 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
376 |
|
377 |
+
intent.patchValue({ examples: newExamples });
|
378 |
+
this.isDirty = true;
|
379 |
+
}
|
380 |
+
|
381 |
+
addParameter(intentIndex: number) {
|
382 |
+
const parameters = this.getIntentParameters(intentIndex);
|
383 |
+
parameters.push(this.createParameterFormGroup());
|
384 |
+
this.isDirty = true;
|
385 |
+
}
|
386 |
+
|
387 |
+
removeParameter(intentIndex: number, paramIndex: number) {
|
388 |
+
const parameters = this.getIntentParameters(intentIndex);
|
389 |
+
parameters.removeAt(paramIndex);
|
390 |
+
this.isDirty = true;
|
391 |
+
}
|
392 |
+
|
393 |
+
// Check if version can be edited
|
394 |
+
get canEdit(): boolean {
|
395 |
+
return !this.selectedVersion?.published;
|
396 |
+
}
|
397 |
+
|
398 |
+
addIntent() {
|
399 |
+
this.intents.push(this.createIntentFormGroup());
|
400 |
+
this.isDirty = true;
|
401 |
+
}
|
402 |
+
|
403 |
+
removeIntent(index: number) {
|
404 |
+
const intent = this.intents.at(index).value;
|
405 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
406 |
+
width: '400px',
|
407 |
+
data: {
|
408 |
+
title: 'Delete Intent',
|
409 |
+
message: `Are you sure you want to delete intent "${intent.name}"?`,
|
410 |
+
confirmText: 'Delete',
|
411 |
+
confirmColor: 'warn'
|
412 |
+
}
|
413 |
+
});
|
414 |
+
|
415 |
+
dialogRef.afterClosed().subscribe(confirmed => {
|
416 |
+
if (confirmed) {
|
417 |
+
this.intents.removeAt(index);
|
418 |
+
this.isDirty = true;
|
419 |
+
}
|
420 |
+
});
|
421 |
+
}
|
422 |
+
|
423 |
+
async editIntent(intentIndex: number) {
|
424 |
+
const { default: IntentEditDialogComponent } = await import('../intent-edit-dialog/intent-edit-dialog.component');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
425 |
|
426 |
+
const intent = this.intents.at(intentIndex);
|
427 |
+
const currentValue = intent.value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
|
429 |
+
// Intent verilerini dialog'a gönder
|
430 |
+
const dialogRef = this.dialog.open(IntentEditDialogComponent, {
|
431 |
+
width: '90vw',
|
432 |
+
maxWidth: '1000px',
|
433 |
+
data: {
|
434 |
+
intent: {
|
435 |
+
...currentValue,
|
436 |
+
examples: currentValue.examples || [],
|
437 |
+
parameters: currentValue.parameters || []
|
438 |
+
},
|
439 |
+
project: this.project,
|
440 |
+
apis: await this.getAvailableAPIs()
|
441 |
+
}
|
442 |
+
});
|
443 |
+
|
444 |
+
dialogRef.afterClosed().subscribe(result => {
|
445 |
+
if (result) {
|
446 |
+
// Update intent with result
|
447 |
+
intent.patchValue({
|
448 |
+
name: result.name,
|
449 |
+
caption: result.caption,
|
450 |
+
detection_prompt: result.detection_prompt,
|
451 |
+
examples: result.examples || [],
|
452 |
+
action: result.action,
|
453 |
+
fallback_timeout_prompt: result.fallback_timeout_prompt,
|
454 |
+
fallback_error_prompt: result.fallback_error_prompt
|
455 |
+
});
|
456 |
+
|
457 |
+
// Update parameters
|
458 |
+
const parametersArray = intent.get('parameters') as FormArray;
|
459 |
+
parametersArray.clear();
|
460 |
+
(result.parameters || []).forEach((param: any) => {
|
461 |
+
parametersArray.push(this.createParameterFormGroup(param));
|
462 |
+
});
|
463 |
+
|
464 |
+
this.isDirty = true;
|
465 |
+
}
|
466 |
+
});
|
467 |
+
}
|
468 |
+
|
469 |
+
async getAvailableAPIs(): Promise<any[]> {
|
470 |
+
try {
|
471 |
+
return await this.apiService.getAPIs().toPromise() || [];
|
472 |
+
} catch {
|
473 |
+
return [];
|
474 |
+
}
|
475 |
+
}
|
476 |
+
|
477 |
+
async createVersion() {
|
478 |
+
const publishedVersions = this.versions.filter(v => v.published);
|
479 |
|
480 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
481 |
+
width: '500px',
|
482 |
+
data: {
|
483 |
+
title: 'Create New Version',
|
484 |
+
message: 'Which published version would you like to use as a base for the new version?',
|
485 |
+
showDropdown: true,
|
486 |
+
dropdownOptions: publishedVersions.map(v => ({
|
487 |
+
value: v.no,
|
488 |
+
label: `Version ${v.no} - ${v.caption || 'No description'}`
|
489 |
+
})),
|
490 |
+
dropdownPlaceholder: 'Select published version (or leave empty for blank)',
|
491 |
+
confirmText: 'Create',
|
492 |
+
cancelText: 'Cancel'
|
493 |
+
}
|
494 |
+
});
|
495 |
+
|
496 |
+
dialogRef.afterClosed().subscribe(async (result) => {
|
497 |
+
if (result?.confirmed) {
|
498 |
+
this.creating = true;
|
499 |
+
try {
|
500 |
+
let newVersionData;
|
501 |
+
|
502 |
+
if (result.selectedValue) {
|
503 |
+
// Copy from selected version - we need to get the full version data
|
504 |
+
const sourceVersion = this.versions.find(v => v.no === result.selectedValue);
|
505 |
+
if (sourceVersion) {
|
506 |
+
// Load the full version data from the current form if it's the selected version
|
507 |
+
if (sourceVersion.no === this.selectedVersion?.no) {
|
508 |
+
const formValue = this.versionForm.getRawValue();
|
509 |
+
newVersionData = {
|
510 |
+
...formValue,
|
511 |
+
no: undefined,
|
512 |
+
published: false,
|
513 |
+
last_update_date: undefined,
|
514 |
+
caption: `Copy of ${sourceVersion.caption || `version ${sourceVersion.no}`}`
|
515 |
+
};
|
516 |
+
} else {
|
517 |
+
// For other versions, we only have basic info, so create minimal copy
|
518 |
+
newVersionData = {
|
519 |
+
caption: `Copy of ${sourceVersion.caption || `version ${sourceVersion.no}`}`,
|
520 |
+
general_prompt: '',
|
521 |
+
llm: {
|
522 |
+
repo_id: '',
|
523 |
+
generation_config: {
|
524 |
+
max_new_tokens: 512,
|
525 |
+
temperature: 0.7,
|
526 |
+
top_p: 0.95,
|
527 |
+
repetition_penalty: 1.1
|
528 |
+
},
|
529 |
+
use_fine_tune: false,
|
530 |
+
fine_tune_zip: ''
|
531 |
+
},
|
532 |
+
intents: []
|
533 |
+
};
|
534 |
+
}
|
535 |
+
}
|
536 |
+
} else {
|
537 |
+
// Create blank version
|
538 |
+
newVersionData = {
|
539 |
+
caption: `Version ${this.versions.length + 1}`,
|
540 |
+
general_prompt: '',
|
541 |
+
llm: {
|
542 |
+
repo_id: '',
|
543 |
+
generation_config: {
|
544 |
+
max_new_tokens: 512,
|
545 |
+
temperature: 0.7,
|
546 |
+
top_p: 0.95,
|
547 |
+
repetition_penalty: 1.1
|
548 |
+
},
|
549 |
+
use_fine_tune: false,
|
550 |
+
fine_tune_zip: ''
|
551 |
+
},
|
552 |
+
intents: []
|
553 |
+
};
|
554 |
+
}
|
555 |
+
|
556 |
+
if (newVersionData) {
|
557 |
+
await this.apiService.createVersion(this.project.id, newVersionData).toPromise();
|
558 |
+
await this.loadVersions();
|
559 |
+
this.snackBar.open('Version created successfully!', 'Close', { duration: 3000 });
|
560 |
+
}
|
561 |
+
} catch (error) {
|
562 |
+
this.snackBar.open('Failed to create version', 'Close', { duration: 3000 });
|
563 |
+
} finally {
|
564 |
+
this.creating = false;
|
565 |
+
}
|
566 |
+
}
|
567 |
+
});
|
568 |
+
}
|
569 |
+
|
570 |
+
async saveVersion() {
|
571 |
+
if (!this.selectedVersion || !this.canEdit) {
|
572 |
+
this.snackBar.open('Cannot save published version', 'Close', { duration: 3000 });
|
573 |
+
return;
|
574 |
+
}
|
575 |
|
576 |
+
if (this.versionForm.invalid) {
|
577 |
+
const invalidFields: string[] = [];
|
578 |
+
Object.keys(this.versionForm.controls).forEach(key => {
|
579 |
+
const control = this.versionForm.get(key);
|
580 |
+
if (control && control.invalid) {
|
581 |
+
invalidFields.push(key);
|
582 |
+
}
|
583 |
+
});
|
584 |
+
|
585 |
+
this.intents.controls.forEach((intent, index) => {
|
586 |
+
if (intent.invalid) {
|
587 |
+
invalidFields.push(`Intent ${index + 1}`);
|
588 |
+
}
|
589 |
+
});
|
590 |
+
|
591 |
+
this.snackBar.open(`Please fix validation errors in: ${invalidFields.join(', ')}`, 'Close', {
|
592 |
+
duration: 5000
|
593 |
+
});
|
594 |
+
return;
|
595 |
+
}
|
596 |
+
|
597 |
+
const currentVersion = this.selectedVersion!;
|
598 |
+
|
599 |
+
this.saving = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
600 |
|
601 |
+
try {
|
602 |
+
const formValue = this.versionForm.getRawValue();
|
603 |
+
|
604 |
+
// updateData'yı backend'in beklediği formatta hazırla
|
605 |
+
const updateData = {
|
606 |
+
caption: formValue.caption,
|
607 |
+
general_prompt: formValue.general_prompt || '',
|
608 |
+
welcome_prompt: formValue.welcome_prompt || '', // Added welcome_prompt
|
609 |
+
llm: formValue.llm,
|
610 |
+
intents: formValue.intents.map((intent: any) => ({
|
611 |
+
name: intent.name,
|
612 |
+
caption: intent.caption,
|
613 |
+
detection_prompt: intent.detection_prompt,
|
614 |
+
examples: Array.isArray(intent.examples) ? intent.examples : [],
|
615 |
+
parameters: Array.isArray(intent.parameters) ? intent.parameters.map((param: any) => ({
|
616 |
+
name: param.name,
|
617 |
+
caption: param.caption,
|
618 |
+
type: param.type,
|
619 |
+
required: param.required,
|
620 |
+
variable_name: param.variable_name,
|
621 |
+
extraction_prompt: param.extraction_prompt,
|
622 |
+
validation_regex: param.validation_regex,
|
623 |
+
invalid_prompt: param.invalid_prompt,
|
624 |
+
type_error_prompt: param.type_error_prompt
|
625 |
+
})) : [],
|
626 |
+
action: intent.action,
|
627 |
+
fallback_timeout_prompt: intent.fallback_timeout_prompt,
|
628 |
+
fallback_error_prompt: intent.fallback_error_prompt
|
629 |
+
})),
|
630 |
+
last_update_date: currentVersion.last_update_date || ''
|
631 |
+
};
|
632 |
+
|
633 |
+
console.log('Saving version data:', JSON.stringify(updateData, null, 2));
|
634 |
+
|
635 |
+
const result = await this.apiService.updateVersion(
|
636 |
+
this.project.id,
|
637 |
+
currentVersion.no,
|
638 |
+
updateData
|
639 |
+
).toPromise();
|
640 |
+
|
641 |
+
this.snackBar.open('Version saved successfully', 'Close', { duration: 3000 });
|
642 |
+
|
643 |
+
this.isDirty = false;
|
644 |
+
|
645 |
+
if (result) {
|
646 |
+
this.selectedVersion = result;
|
647 |
+
this.versionForm.patchValue({
|
648 |
+
last_update_date: result.last_update_date
|
649 |
+
});
|
650 |
+
}
|
651 |
+
|
652 |
+
await this.loadVersions();
|
653 |
+
|
654 |
+
} catch (error: any) {
|
655 |
+
console.error('Save error:', error);
|
656 |
+
|
657 |
+
if (error.status === 409) {
|
658 |
+
// Race condition handling
|
659 |
+
await this.handleRaceCondition(currentVersion);
|
660 |
+
} else if (error.status === 400 && error.error?.detail?.includes('Published versions')) {
|
661 |
+
this.snackBar.open('Published versions cannot be modified. Create a new version instead.', 'Close', {
|
662 |
+
duration: 5000,
|
663 |
+
panelClass: 'error-snackbar'
|
664 |
+
});
|
665 |
+
} else {
|
666 |
+
const errorMessage = error.error?.detail || error.message || 'Failed to save version';
|
667 |
+
this.snackBar.open(errorMessage, 'Close', {
|
668 |
+
duration: 5000,
|
669 |
+
panelClass: 'error-snackbar'
|
670 |
+
});
|
671 |
+
}
|
672 |
+
} finally {
|
673 |
+
this.saving = false;
|
674 |
+
}
|
675 |
+
}
|
676 |
+
|
677 |
+
// Race condition handling
|
678 |
+
private async handleRaceCondition(currentVersion: Version) {
|
679 |
+
const formValue = this.versionForm.getRawValue();
|
680 |
|
681 |
+
const retryUpdateData = {
|
682 |
+
caption: formValue.caption,
|
683 |
+
general_prompt: formValue.general_prompt || '',
|
684 |
+
llm: formValue.llm,
|
685 |
+
intents: formValue.intents.map((intent: any) => ({
|
686 |
+
name: intent.name,
|
687 |
+
caption: intent.caption,
|
688 |
+
detection_prompt: intent.detection_prompt,
|
689 |
+
examples: Array.isArray(intent.examples) ? intent.examples : [],
|
690 |
+
parameters: Array.isArray(intent.parameters) ? intent.parameters : [],
|
691 |
+
action: intent.action,
|
692 |
+
fallback_timeout_prompt: intent.fallback_timeout_prompt,
|
693 |
+
fallback_error_prompt: intent.fallback_error_prompt
|
694 |
+
})),
|
695 |
+
last_update_date: currentVersion.last_update_date || ''
|
696 |
+
};
|
697 |
+
|
698 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
699 |
+
width: '500px',
|
700 |
+
data: {
|
701 |
+
title: 'Version Modified',
|
702 |
+
message: 'This version was modified by another user. Do you want to reload and lose your changes, or force save?',
|
703 |
+
confirmText: 'Force Save',
|
704 |
+
cancelText: 'Reload',
|
705 |
+
confirmColor: 'warn'
|
706 |
+
}
|
707 |
+
});
|
708 |
+
|
709 |
+
dialogRef.afterClosed().subscribe(async (forceSave) => {
|
710 |
+
if (forceSave) {
|
711 |
+
try {
|
712 |
+
await this.apiService.updateVersion(
|
713 |
+
this.project.id,
|
714 |
+
currentVersion.no,
|
715 |
+
retryUpdateData,
|
716 |
+
true
|
717 |
+
).toPromise();
|
718 |
+
this.snackBar.open('Version force saved', 'Close', { duration: 3000 });
|
719 |
+
await this.loadVersions();
|
720 |
+
} catch (err: any) {
|
721 |
+
this.snackBar.open(err.error?.detail || 'Force save failed', 'Close', {
|
722 |
+
duration: 5000,
|
723 |
+
panelClass: 'error-snackbar'
|
724 |
+
});
|
725 |
+
}
|
726 |
+
} else {
|
727 |
+
await this.loadVersions();
|
728 |
+
}
|
729 |
+
});
|
730 |
+
}
|
731 |
+
|
732 |
+
async publishVersion() {
|
733 |
+
if (!this.selectedVersion) return;
|
734 |
+
|
735 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
736 |
+
width: '500px',
|
737 |
+
data: {
|
738 |
+
title: 'Publish Version',
|
739 |
+
message: `Are you sure you want to publish version "${this.selectedVersion.caption}"? This will unpublish all other versions.`,
|
740 |
+
confirmText: 'Publish',
|
741 |
+
confirmColor: 'primary'
|
742 |
+
}
|
743 |
+
});
|
744 |
+
|
745 |
+
dialogRef.afterClosed().subscribe(async (confirmed) => {
|
746 |
+
if (confirmed && this.selectedVersion) {
|
747 |
+
this.publishing = true;
|
748 |
+
try {
|
749 |
+
await this.apiService.publishVersion(
|
750 |
+
this.project.id,
|
751 |
+
this.selectedVersion.no
|
752 |
+
).toPromise();
|
753 |
+
|
754 |
+
this.snackBar.open('Version published successfully', 'Close', { duration: 3000 });
|
755 |
+
|
756 |
+
// Reload to get updated data
|
757 |
+
await this.reloadProject();
|
758 |
+
|
759 |
+
} catch (error: any) {
|
760 |
+
this.snackBar.open(error.error?.detail || 'Failed to publish version', 'Close', {
|
761 |
+
duration: 5000,
|
762 |
+
panelClass: 'error-snackbar'
|
763 |
+
});
|
764 |
+
} finally {
|
765 |
+
this.publishing = false;
|
766 |
+
}
|
767 |
+
}
|
768 |
+
});
|
769 |
+
}
|
770 |
+
|
771 |
+
async deleteVersion() {
|
772 |
+
if (!this.selectedVersion || this.selectedVersion.published) return;
|
773 |
+
|
774 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
775 |
+
width: '400px',
|
776 |
+
data: {
|
777 |
+
title: 'Delete Version',
|
778 |
+
message: `Are you sure you want to delete version "${this.selectedVersion.caption}"?`,
|
779 |
+
confirmText: 'Delete',
|
780 |
+
confirmColor: 'warn'
|
781 |
+
}
|
782 |
+
});
|
783 |
+
|
784 |
+
dialogRef.afterClosed().subscribe(async (confirmed) => {
|
785 |
+
if (confirmed && this.selectedVersion) {
|
786 |
+
try {
|
787 |
+
await this.apiService.deleteVersion(
|
788 |
+
this.project.id,
|
789 |
+
this.selectedVersion.no
|
790 |
+
).toPromise();
|
791 |
+
|
792 |
+
this.snackBar.open('Version deleted successfully', 'Close', { duration: 3000 });
|
793 |
+
|
794 |
+
// Reload and select another version
|
795 |
+
await this.reloadProject();
|
796 |
+
|
797 |
+
if (this.versions.length > 0) {
|
798 |
+
this.loadVersion(this.versions[0]);
|
799 |
+
} else {
|
800 |
+
this.selectedVersion = null;
|
801 |
+
}
|
802 |
+
|
803 |
+
} catch (error: any) {
|
804 |
+
this.snackBar.open(error.error?.detail || 'Failed to delete version', 'Close', {
|
805 |
+
duration: 5000,
|
806 |
+
panelClass: 'error-snackbar'
|
807 |
+
});
|
808 |
+
}
|
809 |
+
}
|
810 |
+
});
|
811 |
+
}
|
812 |
+
|
813 |
+
async testIntentDetection() {
|
814 |
+
if (!this.testUserMessage.trim()) {
|
815 |
+
this.snackBar.open('Please enter a test message', 'Close', { duration: 3000 });
|
816 |
+
return;
|
817 |
+
}
|
818 |
+
|
819 |
+
this.testing = true;
|
820 |
+
this.testResult = null;
|
821 |
+
|
822 |
+
// Simulate intent detection test
|
823 |
+
setTimeout(() => {
|
824 |
+
// This is a mock - in real implementation, this would call the Spark service
|
825 |
+
const intents = this.versionForm.get('intents')?.value || [];
|
826 |
+
|
827 |
+
// Simple matching for demo
|
828 |
+
let detectedIntent = null;
|
829 |
+
let confidence = 0;
|
830 |
+
|
831 |
+
for (const intent of intents) {
|
832 |
+
// Check examples in all locales
|
833 |
+
const allExamples = intent.examples || [];
|
834 |
+
for (const example of allExamples) {
|
835 |
+
const exampleText = typeof example === 'string' ? example : example.example;
|
836 |
+
if (this.testUserMessage.toLowerCase().includes(exampleText.toLowerCase())) {
|
837 |
+
detectedIntent = intent.name;
|
838 |
+
confidence = 0.95;
|
839 |
+
break;
|
840 |
+
}
|
841 |
+
}
|
842 |
+
if (detectedIntent) break;
|
843 |
+
}
|
844 |
+
|
845 |
+
// Random detection for demo
|
846 |
+
if (!detectedIntent && intents.length > 0) {
|
847 |
+
const randomIntent = intents[Math.floor(Math.random() * intents.length)];
|
848 |
+
detectedIntent
|