Spaces:
Running
Running
Update flare-ui/src/app/dialogs/version-edit-dialog/version-edit-dialog.component.ts
Browse files
flare-ui/src/app/dialogs/version-edit-dialog/version-edit-dialog.component.ts
CHANGED
@@ -19,6 +19,12 @@ import { MatListModule } from '@angular/material/list';
|
|
19 |
import { ApiService, Project, Version } from '../../services/api.service';
|
20 |
import ConfirmDialogComponent from '../confirm-dialog/confirm-dialog.component';
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
@Component({
|
23 |
selector: 'app-version-edit-dialog',
|
24 |
standalone: true,
|
@@ -61,6 +67,9 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
61 |
testUserMessage = '';
|
62 |
testResult: any = null;
|
63 |
testing = false;
|
|
|
|
|
|
|
64 |
|
65 |
constructor(
|
66 |
private fb: FormBuilder,
|
@@ -72,6 +81,7 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
72 |
) {
|
73 |
this.project = data.project;
|
74 |
this.versions = [...this.project.versions].sort((a, b) => b.no - a.no);
|
|
|
75 |
}
|
76 |
|
77 |
ngOnInit() {
|
@@ -88,7 +98,6 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
88 |
this.versionForm.valueChanges.subscribe(() => {
|
89 |
this.isDirty = true;
|
90 |
});
|
91 |
-
|
92 |
}
|
93 |
|
94 |
initializeForm() {
|
@@ -133,7 +142,7 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
133 |
no: version.no,
|
134 |
caption: version.caption || '',
|
135 |
published: version.published || false,
|
136 |
-
general_prompt: (version as any).general_prompt || '',
|
137 |
last_update_date: version.last_update_date || ''
|
138 |
});
|
139 |
|
@@ -194,23 +203,15 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
194 |
const group = this.fb.group({
|
195 |
name: [intent.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9-]+$/)]],
|
196 |
caption: [intent.caption || ''],
|
197 |
-
locale: [intent.locale || 'tr-TR'],
|
198 |
detection_prompt: [intent.detection_prompt || '', Validators.required],
|
199 |
-
examples:
|
200 |
parameters: this.fb.array([]),
|
201 |
action: [intent.action || '', Validators.required],
|
202 |
fallback_timeout_prompt: [intent.fallback_timeout_prompt || ''],
|
203 |
fallback_error_prompt: [intent.fallback_error_prompt || '']
|
204 |
});
|
205 |
|
206 |
-
//
|
207 |
-
if (intent.examples && Array.isArray(intent.examples)) {
|
208 |
-
const examplesArray = group.get('examples') as FormArray;
|
209 |
-
intent.examples.forEach((example: any) => {
|
210 |
-
examplesArray.push(this.fb.control(example));
|
211 |
-
});
|
212 |
-
}
|
213 |
-
|
214 |
if (intent.parameters && Array.isArray(intent.parameters)) {
|
215 |
const parametersArray = group.get('parameters') as FormArray;
|
216 |
intent.parameters.forEach((param: any) => {
|
@@ -221,13 +222,6 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
221 |
return group;
|
222 |
}
|
223 |
|
224 |
-
private populateIntentParameters(intentFormGroup: FormGroup, parameters: any[]) {
|
225 |
-
const parametersArray = intentFormGroup.get('parameters') as FormArray;
|
226 |
-
parameters.forEach(param => {
|
227 |
-
parametersArray.push(this.createParameterFormGroup(param));
|
228 |
-
});
|
229 |
-
}
|
230 |
-
|
231 |
createParameterFormGroup(param: any = {}): FormGroup {
|
232 |
return this.fb.group({
|
233 |
name: [param.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9_]+$/)]],
|
@@ -250,8 +244,59 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
250 |
return this.intents.at(intentIndex).get('parameters') as FormArray;
|
251 |
}
|
252 |
|
253 |
-
|
254 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
255 |
}
|
256 |
|
257 |
addIntent() {
|
@@ -300,30 +345,25 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
300 |
|
301 |
dialogRef.afterClosed().subscribe(result => {
|
302 |
if (result) {
|
303 |
-
//
|
304 |
intent.patchValue({
|
305 |
name: result.name,
|
306 |
caption: result.caption,
|
307 |
-
locale: result.locale,
|
308 |
detection_prompt: result.detection_prompt,
|
|
|
309 |
action: result.action,
|
310 |
fallback_timeout_prompt: result.fallback_timeout_prompt,
|
311 |
fallback_error_prompt: result.fallback_error_prompt
|
312 |
});
|
313 |
|
314 |
-
//
|
315 |
-
const examplesArray = intent.get('examples') as FormArray;
|
316 |
-
examplesArray.clear();
|
317 |
-
(result.examples || []).forEach((example: string) => {
|
318 |
-
examplesArray.push(this.fb.control(example));
|
319 |
-
});
|
320 |
-
|
321 |
-
// Parameters'ı güncelle
|
322 |
const parametersArray = intent.get('parameters') as FormArray;
|
323 |
parametersArray.clear();
|
324 |
(result.parameters || []).forEach((param: any) => {
|
325 |
parametersArray.push(this.createParameterFormGroup(param));
|
326 |
});
|
|
|
|
|
327 |
}
|
328 |
});
|
329 |
}
|
@@ -338,18 +378,6 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
338 |
parameters.removeAt(paramIndex);
|
339 |
}
|
340 |
|
341 |
-
addExample(intentIndex: number, example: string) {
|
342 |
-
if (example.trim()) {
|
343 |
-
const examples = this.getIntentExamples(intentIndex);
|
344 |
-
examples.push(this.fb.control(example));
|
345 |
-
}
|
346 |
-
}
|
347 |
-
|
348 |
-
removeExample(intentIndex: number, exampleIndex: number) {
|
349 |
-
const examples = this.getIntentExamples(intentIndex);
|
350 |
-
examples.removeAt(exampleIndex);
|
351 |
-
}
|
352 |
-
|
353 |
async createVersion() {
|
354 |
const publishedVersions = this.versions.filter(v => v.published);
|
355 |
|
@@ -359,7 +387,7 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
359 |
title: 'Create New Version',
|
360 |
message: 'Which published version would you like to use as a base for the new version?',
|
361 |
showDropdown: true,
|
362 |
-
dropdownOptions: publishedVersions.map(v => ({
|
363 |
value: v.no,
|
364 |
label: `Version ${v.no} - ${v.caption || 'No description'}`
|
365 |
})),
|
@@ -427,8 +455,8 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
427 |
}
|
428 |
|
429 |
async saveVersion() {
|
430 |
-
if (!this.selectedVersion) {
|
431 |
-
this.snackBar.open('
|
432 |
return;
|
433 |
}
|
434 |
|
@@ -463,7 +491,7 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
463 |
// updateData'yı backend'in beklediği formatta hazırla
|
464 |
const updateData = {
|
465 |
caption: formValue.caption,
|
466 |
-
general_prompt: formValue.general_prompt || '',
|
467 |
llm: formValue.llm,
|
468 |
intents: formValue.intents.map((intent: any) => ({
|
469 |
name: intent.name,
|
@@ -513,8 +541,13 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
513 |
console.error('Save error:', error);
|
514 |
|
515 |
if (error.status === 409) {
|
516 |
-
// Race condition handling
|
517 |
await this.handleRaceCondition(currentVersion);
|
|
|
|
|
|
|
|
|
|
|
518 |
} else {
|
519 |
const errorMessage = error.error?.detail || error.message || 'Failed to save version';
|
520 |
this.snackBar.open(errorMessage, 'Close', {
|
@@ -527,7 +560,7 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
527 |
}
|
528 |
}
|
529 |
|
530 |
-
// Race condition handling
|
531 |
private async handleRaceCondition(currentVersion: Version) {
|
532 |
const formValue = this.versionForm.getRawValue();
|
533 |
|
@@ -682,8 +715,11 @@ export default class VersionEditDialogComponent implements OnInit {
|
|
682 |
let confidence = 0;
|
683 |
|
684 |
for (const intent of intents) {
|
685 |
-
|
686 |
-
|
|
|
|
|
|
|
687 |
detectedIntent = intent.name;
|
688 |
confidence = 0.95;
|
689 |
break;
|
|
|
19 |
import { ApiService, Project, Version } from '../../services/api.service';
|
20 |
import ConfirmDialogComponent from '../confirm-dialog/confirm-dialog.component';
|
21 |
|
22 |
+
// Interfaces for multi-language support
|
23 |
+
interface LocalizedExample {
|
24 |
+
locale_code: string;
|
25 |
+
example: string;
|
26 |
+
}
|
27 |
+
|
28 |
@Component({
|
29 |
selector: 'app-version-edit-dialog',
|
30 |
standalone: true,
|
|
|
67 |
testUserMessage = '';
|
68 |
testResult: any = null;
|
69 |
testing = false;
|
70 |
+
|
71 |
+
// Locale for examples
|
72 |
+
selectedExampleLocale: string = 'tr';
|
73 |
|
74 |
constructor(
|
75 |
private fb: FormBuilder,
|
|
|
81 |
) {
|
82 |
this.project = data.project;
|
83 |
this.versions = [...this.project.versions].sort((a, b) => b.no - a.no);
|
84 |
+
this.selectedExampleLocale = this.project.default_locale || 'tr';
|
85 |
}
|
86 |
|
87 |
ngOnInit() {
|
|
|
98 |
this.versionForm.valueChanges.subscribe(() => {
|
99 |
this.isDirty = true;
|
100 |
});
|
|
|
101 |
}
|
102 |
|
103 |
initializeForm() {
|
|
|
142 |
no: version.no,
|
143 |
caption: version.caption || '',
|
144 |
published: version.published || false,
|
145 |
+
general_prompt: (version as any).general_prompt || '',
|
146 |
last_update_date: version.last_update_date || ''
|
147 |
});
|
148 |
|
|
|
203 |
const group = this.fb.group({
|
204 |
name: [intent.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9-]+$/)]],
|
205 |
caption: [intent.caption || ''],
|
|
|
206 |
detection_prompt: [intent.detection_prompt || '', Validators.required],
|
207 |
+
examples: [intent.examples || []], // Store as array, not FormArray
|
208 |
parameters: this.fb.array([]),
|
209 |
action: [intent.action || '', Validators.required],
|
210 |
fallback_timeout_prompt: [intent.fallback_timeout_prompt || ''],
|
211 |
fallback_error_prompt: [intent.fallback_error_prompt || '']
|
212 |
});
|
213 |
|
214 |
+
// Parameters'ı ayrı olarak ekle
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
if (intent.parameters && Array.isArray(intent.parameters)) {
|
216 |
const parametersArray = group.get('parameters') as FormArray;
|
217 |
intent.parameters.forEach((param: any) => {
|
|
|
222 |
return group;
|
223 |
}
|
224 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
createParameterFormGroup(param: any = {}): FormGroup {
|
226 |
return this.fb.group({
|
227 |
name: [param.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9_]+$/)]],
|
|
|
244 |
return this.intents.at(intentIndex).get('parameters') as FormArray;
|
245 |
}
|
246 |
|
247 |
+
// LocalizedExample support methods
|
248 |
+
getLocalizedExamples(examples: any[], locale: string): LocalizedExample[] {
|
249 |
+
if (!examples || !Array.isArray(examples)) return [];
|
250 |
+
|
251 |
+
// Check if examples are in new format
|
252 |
+
if (examples.length > 0 && typeof examples[0] === 'object' && 'locale_code' in examples[0]) {
|
253 |
+
return examples.filter(ex => ex.locale_code === locale);
|
254 |
+
}
|
255 |
+
|
256 |
+
// Old format - convert to new
|
257 |
+
if (typeof examples[0] === 'string') {
|
258 |
+
return examples.map(ex => ({ locale_code: locale, example: ex }));
|
259 |
+
}
|
260 |
+
|
261 |
+
return [];
|
262 |
+
}
|
263 |
+
|
264 |
+
addLocalizedExample(intentIndex: number, example: string) {
|
265 |
+
if (!example.trim()) return;
|
266 |
+
|
267 |
+
const intent = this.intents.at(intentIndex);
|
268 |
+
const currentExamples = intent.get('examples')?.value || [];
|
269 |
+
|
270 |
+
// Check if already exists
|
271 |
+
const exists = currentExamples.some((ex: any) =>
|
272 |
+
ex.locale_code === this.selectedExampleLocale && ex.example === example.trim()
|
273 |
+
);
|
274 |
+
|
275 |
+
if (!exists) {
|
276 |
+
const newExamples = [...currentExamples, {
|
277 |
+
locale_code: this.selectedExampleLocale,
|
278 |
+
example: example.trim()
|
279 |
+
}];
|
280 |
+
intent.patchValue({ examples: newExamples });
|
281 |
+
this.isDirty = true;
|
282 |
+
}
|
283 |
+
}
|
284 |
+
|
285 |
+
removeLocalizedExample(intentIndex: number, exampleToRemove: LocalizedExample) {
|
286 |
+
const intent = this.intents.at(intentIndex);
|
287 |
+
const currentExamples = intent.get('examples')?.value || [];
|
288 |
+
|
289 |
+
const newExamples = currentExamples.filter((ex: any) =>
|
290 |
+
!(ex.locale_code === exampleToRemove.locale_code && ex.example === exampleToRemove.example)
|
291 |
+
);
|
292 |
+
|
293 |
+
intent.patchValue({ examples: newExamples });
|
294 |
+
this.isDirty = true;
|
295 |
+
}
|
296 |
+
|
297 |
+
// Check if version can be edited
|
298 |
+
get canEdit(): boolean {
|
299 |
+
return !this.selectedVersion?.published;
|
300 |
}
|
301 |
|
302 |
addIntent() {
|
|
|
345 |
|
346 |
dialogRef.afterClosed().subscribe(result => {
|
347 |
if (result) {
|
348 |
+
// Update intent with result
|
349 |
intent.patchValue({
|
350 |
name: result.name,
|
351 |
caption: result.caption,
|
|
|
352 |
detection_prompt: result.detection_prompt,
|
353 |
+
examples: result.examples || [],
|
354 |
action: result.action,
|
355 |
fallback_timeout_prompt: result.fallback_timeout_prompt,
|
356 |
fallback_error_prompt: result.fallback_error_prompt
|
357 |
});
|
358 |
|
359 |
+
// Update parameters
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
360 |
const parametersArray = intent.get('parameters') as FormArray;
|
361 |
parametersArray.clear();
|
362 |
(result.parameters || []).forEach((param: any) => {
|
363 |
parametersArray.push(this.createParameterFormGroup(param));
|
364 |
});
|
365 |
+
|
366 |
+
this.isDirty = true;
|
367 |
}
|
368 |
});
|
369 |
}
|
|
|
378 |
parameters.removeAt(paramIndex);
|
379 |
}
|
380 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
381 |
async createVersion() {
|
382 |
const publishedVersions = this.versions.filter(v => v.published);
|
383 |
|
|
|
387 |
title: 'Create New Version',
|
388 |
message: 'Which published version would you like to use as a base for the new version?',
|
389 |
showDropdown: true,
|
390 |
+
dropdownOptions: publishedVersions.map(v => ({
|
391 |
value: v.no,
|
392 |
label: `Version ${v.no} - ${v.caption || 'No description'}`
|
393 |
})),
|
|
|
455 |
}
|
456 |
|
457 |
async saveVersion() {
|
458 |
+
if (!this.selectedVersion || !this.canEdit) {
|
459 |
+
this.snackBar.open('Cannot save published version', 'Close', { duration: 3000 });
|
460 |
return;
|
461 |
}
|
462 |
|
|
|
491 |
// updateData'yı backend'in beklediği formatta hazırla
|
492 |
const updateData = {
|
493 |
caption: formValue.caption,
|
494 |
+
general_prompt: formValue.general_prompt || '',
|
495 |
llm: formValue.llm,
|
496 |
intents: formValue.intents.map((intent: any) => ({
|
497 |
name: intent.name,
|
|
|
541 |
console.error('Save error:', error);
|
542 |
|
543 |
if (error.status === 409) {
|
544 |
+
// Race condition handling
|
545 |
await this.handleRaceCondition(currentVersion);
|
546 |
+
} else if (error.status === 400 && error.error?.detail?.includes('Published versions')) {
|
547 |
+
this.snackBar.open('Published versions cannot be modified. Create a new version instead.', 'Close', {
|
548 |
+
duration: 5000,
|
549 |
+
panelClass: 'error-snackbar'
|
550 |
+
});
|
551 |
} else {
|
552 |
const errorMessage = error.error?.detail || error.message || 'Failed to save version';
|
553 |
this.snackBar.open(errorMessage, 'Close', {
|
|
|
560 |
}
|
561 |
}
|
562 |
|
563 |
+
// Race condition handling
|
564 |
private async handleRaceCondition(currentVersion: Version) {
|
565 |
const formValue = this.versionForm.getRawValue();
|
566 |
|
|
|
715 |
let confidence = 0;
|
716 |
|
717 |
for (const intent of intents) {
|
718 |
+
// Check examples in all locales
|
719 |
+
const allExamples = intent.examples || [];
|
720 |
+
for (const example of allExamples) {
|
721 |
+
const exampleText = typeof example === 'string' ? example : example.example;
|
722 |
+
if (this.testUserMessage.toLowerCase().includes(exampleText.toLowerCase())) {
|
723 |
detectedIntent = intent.name;
|
724 |
confidence = 0.95;
|
725 |
break;
|