Spaces:
Building
Building
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
@@ -1,83 +1,553 @@
|
|
1 |
-
import { Component, Inject } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
import { MatButtonModule } from '@angular/material/button';
|
5 |
import { MatIconModule } from '@angular/material/icon';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
@Component({
|
8 |
selector: 'app-version-edit-dialog',
|
9 |
standalone: true,
|
10 |
-
imports: [
|
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 |
close() {
|
81 |
-
this.dialogRef.close(
|
82 |
}
|
83 |
}
|
|
|
1 |
+
import { Component, Inject, OnInit } from '@angular/core';
|
2 |
import { CommonModule } from '@angular/common';
|
3 |
+
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule, FormArray } 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 { 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,
|
25 |
+
imports: [
|
26 |
+
CommonModule,
|
27 |
+
ReactiveFormsModule,
|
28 |
+
MatDialogModule,
|
29 |
+
MatTabsModule,
|
30 |
+
MatFormFieldModule,
|
31 |
+
MatInputModule,
|
32 |
+
MatSelectModule,
|
33 |
+
MatCheckboxModule,
|
34 |
+
MatButtonModule,
|
35 |
+
MatIconModule,
|
36 |
+
MatSnackBarModule,
|
37 |
+
MatTableModule,
|
38 |
+
MatChipsModule,
|
39 |
+
MatExpansionModule,
|
40 |
+
MatDividerModule,
|
41 |
+
MatProgressBarModule,
|
42 |
+
MatListModule
|
43 |
+
],
|
44 |
+
templateUrl: './version-edit-dialog.component.html',
|
45 |
+
styleUrls: ['./version-edit-dialog.component.scss']
|
46 |
+
})
|
47 |
+
export default class VersionEditDialogComponent implements OnInit {
|
48 |
+
project: Project;
|
49 |
+
versions: Version[] = [];
|
50 |
+
selectedVersion: Version | null = null;
|
51 |
+
versionForm!: FormGroup;
|
52 |
+
|
53 |
+
loading = false;
|
54 |
+
saving = false;
|
55 |
+
publishing = false;
|
56 |
+
creating = false;
|
57 |
+
|
58 |
+
selectedTabIndex = 0;
|
59 |
+
testUserMessage = '';
|
60 |
+
testResult: any = null;
|
61 |
+
testing = false;
|
62 |
+
|
63 |
+
constructor(
|
64 |
+
private fb: FormBuilder,
|
65 |
+
private apiService: ApiService,
|
66 |
+
private snackBar: MatSnackBar,
|
67 |
+
private dialog: MatDialog,
|
68 |
+
public dialogRef: MatDialogRef<VersionEditDialogComponent>,
|
69 |
+
@Inject(MAT_DIALOG_DATA) public data: any
|
70 |
+
) {
|
71 |
+
this.project = data.project;
|
72 |
+
this.versions = [...this.project.versions].sort((a, b) => b.id - a.id);
|
73 |
+
}
|
74 |
+
|
75 |
+
ngOnInit() {
|
76 |
+
this.initializeForm();
|
77 |
+
|
78 |
+
// Select the latest unpublished version or the latest version
|
79 |
+
const unpublished = this.versions.find(v => !v.published);
|
80 |
+
this.selectedVersion = unpublished || this.versions[0] || null;
|
81 |
+
|
82 |
+
if (this.selectedVersion) {
|
83 |
+
this.loadVersion(this.selectedVersion);
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
initializeForm() {
|
88 |
+
this.versionForm = this.fb.group({
|
89 |
+
id: [{value: '', disabled: true}],
|
90 |
+
caption: ['', Validators.required],
|
91 |
+
published: [{value: false, disabled: true}],
|
92 |
+
general_prompt: ['', Validators.required],
|
93 |
+
llm: this.fb.group({
|
94 |
+
repo_id: ['', Validators.required],
|
95 |
+
generation_config: this.fb.group({
|
96 |
+
max_new_tokens: [256, [Validators.required, Validators.min(1), Validators.max(2048)]],
|
97 |
+
temperature: [0.2, [Validators.required, Validators.min(0), Validators.max(2)]],
|
98 |
+
top_p: [0.8, [Validators.required, Validators.min(0), Validators.max(1)]],
|
99 |
+
repetition_penalty: [1.1, [Validators.required, Validators.min(1), Validators.max(2)]]
|
100 |
+
}),
|
101 |
+
use_fine_tune: [false],
|
102 |
+
fine_tune_zip: ['']
|
103 |
+
}),
|
104 |
+
intents: this.fb.array([]),
|
105 |
+
last_update_date: ['']
|
106 |
+
});
|
107 |
+
|
108 |
+
// Watch for fine-tune toggle
|
109 |
+
this.versionForm.get('llm.use_fine_tune')?.valueChanges.subscribe(useFineTune => {
|
110 |
+
const fineTuneControl = this.versionForm.get('llm.fine_tune_zip');
|
111 |
+
if (useFineTune) {
|
112 |
+
fineTuneControl?.setValidators([Validators.required]);
|
113 |
+
} else {
|
114 |
+
fineTuneControl?.clearValidators();
|
115 |
+
fineTuneControl?.setValue('');
|
116 |
+
}
|
117 |
+
fineTuneControl?.updateValueAndValidity();
|
118 |
+
});
|
119 |
+
}
|
120 |
+
|
121 |
+
loadVersion(version: Version) {
|
122 |
+
this.selectedVersion = version;
|
123 |
|
124 |
+
// Clear intents array
|
125 |
+
const intentsArray = this.versionForm.get('intents') as FormArray;
|
126 |
+
while (intentsArray.length !== 0) {
|
127 |
+
intentsArray.removeAt(0);
|
128 |
+
}
|
129 |
+
|
130 |
+
// Populate form
|
131 |
+
this.versionForm.patchValue({
|
132 |
+
id: version.id,
|
133 |
+
caption: version.caption,
|
134 |
+
published: version.published,
|
135 |
+
general_prompt: version.general_prompt || '',
|
136 |
+
llm: version.llm || {
|
137 |
+
repo_id: '',
|
138 |
+
generation_config: {
|
139 |
+
max_new_tokens: 256,
|
140 |
+
temperature: 0.2,
|
141 |
+
top_p: 0.8,
|
142 |
+
repetition_penalty: 1.1
|
143 |
+
},
|
144 |
+
use_fine_tune: false,
|
145 |
+
fine_tune_zip: ''
|
146 |
+
},
|
147 |
+
last_update_date: version.last_update_date || ''
|
148 |
+
});
|
149 |
+
|
150 |
+
// Populate intents
|
151 |
+
if (version.intents) {
|
152 |
+
version.intents.forEach(intent => {
|
153 |
+
intentsArray.push(this.createIntentFormGroup(intent));
|
154 |
+
});
|
155 |
+
}
|
156 |
+
|
157 |
+
// Enable/disable form based on published status
|
158 |
+
if (version.published) {
|
159 |
+
this.versionForm.disable();
|
160 |
+
this.snackBar.open('This version is published and cannot be edited', 'OK', { duration: 3000 });
|
161 |
+
} else {
|
162 |
+
this.versionForm.enable();
|
163 |
+
this.versionForm.get('id')?.disable();
|
164 |
+
this.versionForm.get('published')?.disable();
|
165 |
+
}
|
166 |
+
}
|
167 |
+
|
168 |
+
createIntentFormGroup(intent: any = {}): FormGroup {
|
169 |
+
const parametersArray = this.fb.array([]);
|
170 |
+
|
171 |
+
if (intent.parameters) {
|
172 |
+
intent.parameters.forEach((param: any) => {
|
173 |
+
parametersArray.push(this.createParameterFormGroup(param));
|
174 |
+
});
|
175 |
+
}
|
176 |
+
|
177 |
+
return this.fb.group({
|
178 |
+
name: [intent.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9-]+$/)]],
|
179 |
+
caption: [intent.caption || ''],
|
180 |
+
locale: [intent.locale || 'tr-TR'],
|
181 |
+
detection_prompt: [intent.detection_prompt || '', Validators.required],
|
182 |
+
examples: this.fb.array(intent.examples || []),
|
183 |
+
parameters: parametersArray,
|
184 |
+
action: [intent.action || '', Validators.required],
|
185 |
+
fallback_timeout_prompt: [intent.fallback_timeout_prompt || ''],
|
186 |
+
fallback_error_prompt: [intent.fallback_error_prompt || '']
|
187 |
+
});
|
188 |
+
}
|
189 |
+
|
190 |
+
createParameterFormGroup(param: any = {}): FormGroup {
|
191 |
+
return this.fb.group({
|
192 |
+
name: [param.name || '', [Validators.required, Validators.pattern(/^[a-zA-Z0-9_]+$/)]],
|
193 |
+
caption: [param.caption || ''],
|
194 |
+
type: [param.type || 'str', Validators.required],
|
195 |
+
required: [param.required !== false],
|
196 |
+
variable_name: [param.variable_name || '', Validators.required],
|
197 |
+
extraction_prompt: [param.extraction_prompt || ''],
|
198 |
+
validation_regex: [param.validation_regex || ''],
|
199 |
+
invalid_prompt: [param.invalid_prompt || ''],
|
200 |
+
type_error_prompt: [param.type_error_prompt || '']
|
201 |
+
});
|
202 |
+
}
|
203 |
+
|
204 |
+
get intents() {
|
205 |
+
return this.versionForm.get('intents') as FormArray;
|
206 |
+
}
|
207 |
+
|
208 |
+
getIntentParameters(intentIndex: number): FormArray {
|
209 |
+
return this.intents.at(intentIndex).get('parameters') as FormArray;
|
210 |
+
}
|
211 |
+
|
212 |
+
getIntentExamples(intentIndex: number): FormArray {
|
213 |
+
return this.intents.at(intentIndex).get('examples') as FormArray;
|
214 |
+
}
|
215 |
+
|
216 |
+
addIntent() {
|
217 |
+
this.intents.push(this.createIntentFormGroup());
|
218 |
+
}
|
219 |
+
|
220 |
+
removeIntent(index: number) {
|
221 |
+
const intent = this.intents.at(index).value;
|
222 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
223 |
+
width: '400px',
|
224 |
+
data: {
|
225 |
+
title: 'Delete Intent',
|
226 |
+
message: `Are you sure you want to delete intent "${intent.name}"?`,
|
227 |
+
confirmText: 'Delete',
|
228 |
+
confirmColor: 'warn'
|
229 |
+
}
|
230 |
+
});
|
231 |
+
|
232 |
+
dialogRef.afterClosed().subscribe(confirmed => {
|
233 |
+
if (confirmed) {
|
234 |
+
this.intents.removeAt(index);
|
235 |
+
}
|
236 |
+
});
|
237 |
+
}
|
238 |
+
|
239 |
+
async editIntent(intentIndex: number) {
|
240 |
+
const { default: IntentEditDialogComponent } = await import('../intent-edit-dialog/intent-edit-dialog.component');
|
241 |
|
242 |
+
const intent = this.intents.at(intentIndex);
|
243 |
+
const dialogRef = this.dialog.open(IntentEditDialogComponent, {
|
244 |
+
width: '90vw',
|
245 |
+
maxWidth: '1000px',
|
246 |
+
data: {
|
247 |
+
intent: intent.value,
|
248 |
+
project: this.project,
|
249 |
+
apis: await this.getAvailableAPIs()
|
250 |
+
}
|
251 |
+
});
|
252 |
+
|
253 |
+
dialogRef.afterClosed().subscribe(result => {
|
254 |
+
if (result) {
|
255 |
+
// Update the intent in the form array
|
256 |
+
intent.patchValue(result);
|
257 |
+
}
|
258 |
+
});
|
259 |
+
}
|
260 |
+
|
261 |
+
addParameter(intentIndex: number) {
|
262 |
+
const parameters = this.getIntentParameters(intentIndex);
|
263 |
+
parameters.push(this.createParameterFormGroup());
|
264 |
+
}
|
265 |
+
|
266 |
+
removeParameter(intentIndex: number, paramIndex: number) {
|
267 |
+
const parameters = this.getIntentParameters(intentIndex);
|
268 |
+
parameters.removeAt(paramIndex);
|
269 |
+
}
|
270 |
+
|
271 |
+
addExample(intentIndex: number, example: string) {
|
272 |
+
if (example.trim()) {
|
273 |
+
const examples = this.getIntentExamples(intentIndex);
|
274 |
+
examples.push(this.fb.control(example));
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
removeExample(intentIndex: number, exampleIndex: number) {
|
279 |
+
const examples = this.getIntentExamples(intentIndex);
|
280 |
+
examples.removeAt(exampleIndex);
|
281 |
+
}
|
282 |
+
|
283 |
+
async createVersion() {
|
284 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
285 |
+
width: '500px',
|
286 |
+
data: {
|
287 |
+
title: 'Create New Version',
|
288 |
+
message: 'Which version would you like to use as a base for the new version?',
|
289 |
+
confirmText: 'Create',
|
290 |
+
showVersionSelect: true,
|
291 |
+
versions: this.versions
|
292 |
+
}
|
293 |
+
});
|
294 |
+
|
295 |
+
dialogRef.afterClosed().subscribe(async (sourceVersionId) => {
|
296 |
+
if (sourceVersionId) {
|
297 |
+
this.creating = true;
|
298 |
+
try {
|
299 |
+
const result = await this.apiService.createVersion(this.project.id, {
|
300 |
+
source_version_id: sourceVersionId,
|
301 |
+
caption: 'New Version'
|
302 |
+
}).toPromise();
|
303 |
+
|
304 |
+
this.snackBar.open('Version created successfully', 'Close', { duration: 3000 });
|
305 |
+
|
306 |
+
// Reload project to get new version
|
307 |
+
await this.reloadProject();
|
308 |
+
|
309 |
+
// Select the new version
|
310 |
+
const newVersion = this.versions.find(v => v.id === result.id);
|
311 |
+
if (newVersion) {
|
312 |
+
this.loadVersion(newVersion);
|
313 |
+
}
|
314 |
+
} catch (error: any) {
|
315 |
+
this.snackBar.open(error.error?.detail || 'Failed to create version', 'Close', {
|
316 |
+
duration: 5000,
|
317 |
+
panelClass: 'error-snackbar'
|
318 |
+
});
|
319 |
+
} finally {
|
320 |
+
this.creating = false;
|
321 |
+
}
|
322 |
+
}
|
323 |
+
});
|
324 |
+
}
|
325 |
+
|
326 |
+
async saveVersion() {
|
327 |
+
if (this.versionForm.invalid || !this.selectedVersion) {
|
328 |
+
this.snackBar.open('Please fix all validation errors', 'Close', { duration: 3000 });
|
329 |
+
return;
|
330 |
}
|
331 |
|
332 |
+
this.saving = true;
|
333 |
+
try {
|
334 |
+
const formValue = this.versionForm.getRawValue();
|
335 |
+
|
336 |
+
// Prepare intents data
|
337 |
+
const intents = formValue.intents.map((intent: any) => ({
|
338 |
+
...intent,
|
339 |
+
examples: intent.examples || []
|
340 |
+
}));
|
341 |
|
342 |
+
const updateData = {
|
343 |
+
caption: formValue.caption,
|
344 |
+
general_prompt: formValue.general_prompt,
|
345 |
+
llm: formValue.llm,
|
346 |
+
intents: intents,
|
347 |
+
last_update_date: formValue.last_update_date
|
348 |
+
};
|
349 |
+
|
350 |
+
await this.apiService.updateVersion(
|
351 |
+
this.project.id,
|
352 |
+
this.selectedVersion.id,
|
353 |
+
updateData
|
354 |
+
).toPromise();
|
355 |
+
|
356 |
+
this.snackBar.open('Version saved successfully', 'Close', { duration: 3000 });
|
357 |
+
|
358 |
+
// Update last_update_date
|
359 |
+
const updatedVersion = { ...this.selectedVersion, ...updateData };
|
360 |
+
this.loadVersion(updatedVersion);
|
361 |
+
|
362 |
+
} catch (error: any) {
|
363 |
+
if (error.status === 409) {
|
364 |
+
this.snackBar.open('Version was modified by another user. Please reload.', 'Close', {
|
365 |
+
duration: 5000,
|
366 |
+
panelClass: 'error-snackbar'
|
367 |
+
});
|
368 |
+
await this.reloadProject();
|
369 |
+
} else {
|
370 |
+
this.snackBar.open(error.error?.detail || 'Failed to save version', 'Close', {
|
371 |
+
duration: 5000,
|
372 |
+
panelClass: 'error-snackbar'
|
373 |
+
});
|
374 |
+
}
|
375 |
+
} finally {
|
376 |
+
this.saving = false;
|
377 |
+
}
|
378 |
+
}
|
379 |
+
|
380 |
+
async publishVersion() {
|
381 |
+
if (!this.selectedVersion) return;
|
382 |
+
|
383 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
384 |
+
width: '500px',
|
385 |
+
data: {
|
386 |
+
title: 'Publish Version',
|
387 |
+
message: `Are you sure you want to publish version "${this.selectedVersion.caption}"? This will unpublish all other versions.`,
|
388 |
+
confirmText: 'Publish',
|
389 |
+
confirmColor: 'primary'
|
390 |
}
|
391 |
+
});
|
392 |
+
|
393 |
+
dialogRef.afterClosed().subscribe(async (confirmed) => {
|
394 |
+
if (confirmed && this.selectedVersion) {
|
395 |
+
this.publishing = true;
|
396 |
+
try {
|
397 |
+
await this.apiService.publishVersion(
|
398 |
+
this.project.id,
|
399 |
+
this.selectedVersion.id
|
400 |
+
).toPromise();
|
401 |
+
|
402 |
+
this.snackBar.open('Version published successfully', 'Close', { duration: 3000 });
|
403 |
+
|
404 |
+
// Reload to get updated data
|
405 |
+
await this.reloadProject();
|
406 |
+
|
407 |
+
} catch (error: any) {
|
408 |
+
this.snackBar.open(error.error?.detail || 'Failed to publish version', 'Close', {
|
409 |
+
duration: 5000,
|
410 |
+
panelClass: 'error-snackbar'
|
411 |
+
});
|
412 |
+
} finally {
|
413 |
+
this.publishing = false;
|
414 |
+
}
|
415 |
+
}
|
416 |
+
});
|
417 |
+
}
|
418 |
+
|
419 |
+
async deleteVersion() {
|
420 |
+
if (!this.selectedVersion || this.selectedVersion.published) return;
|
421 |
|
422 |
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
423 |
+
width: '400px',
|
424 |
+
data: {
|
425 |
+
title: 'Delete Version',
|
426 |
+
message: `Are you sure you want to delete version "${this.selectedVersion.caption}"?`,
|
427 |
+
confirmText: 'Delete',
|
428 |
+
confirmColor: 'warn'
|
429 |
}
|
430 |
+
});
|
431 |
|
432 |
+
dialogRef.afterClosed().subscribe(async (confirmed) => {
|
433 |
+
if (confirmed && this.selectedVersion) {
|
434 |
+
try {
|
435 |
+
await this.apiService.deleteVersion(
|
436 |
+
this.project.id,
|
437 |
+
this.selectedVersion.id
|
438 |
+
).toPromise();
|
439 |
+
|
440 |
+
this.snackBar.open('Version deleted successfully', 'Close', { duration: 3000 });
|
441 |
+
|
442 |
+
// Reload and select another version
|
443 |
+
await this.reloadProject();
|
444 |
+
|
445 |
+
if (this.versions.length > 0) {
|
446 |
+
this.loadVersion(this.versions[0]);
|
447 |
+
} else {
|
448 |
+
this.selectedVersion = null;
|
449 |
+
}
|
450 |
+
|
451 |
+
} catch (error: any) {
|
452 |
+
this.snackBar.open(error.error?.detail || 'Failed to delete version', 'Close', {
|
453 |
+
duration: 5000,
|
454 |
+
panelClass: 'error-snackbar'
|
455 |
+
});
|
456 |
+
}
|
457 |
}
|
458 |
+
});
|
459 |
+
}
|
460 |
|
461 |
+
async testIntentDetection() {
|
462 |
+
if (!this.testUserMessage.trim()) {
|
463 |
+
this.snackBar.open('Please enter a test message', 'Close', { duration: 3000 });
|
464 |
+
return;
|
465 |
+
}
|
466 |
|
467 |
+
this.testing = true;
|
468 |
+
this.testResult = null;
|
469 |
+
|
470 |
+
// Simulate intent detection test
|
471 |
+
setTimeout(() => {
|
472 |
+
// This is a mock - in real implementation, this would call the Spark service
|
473 |
+
const intents = this.versionForm.get('intents')?.value || [];
|
474 |
+
|
475 |
+
// Simple matching for demo
|
476 |
+
let detectedIntent = null;
|
477 |
+
let confidence = 0;
|
478 |
+
|
479 |
+
for (const intent of intents) {
|
480 |
+
for (const example of intent.examples || []) {
|
481 |
+
if (this.testUserMessage.toLowerCase().includes(example.toLowerCase())) {
|
482 |
+
detectedIntent = intent.name;
|
483 |
+
confidence = 0.95;
|
484 |
+
break;
|
485 |
+
}
|
486 |
}
|
487 |
+
if (detectedIntent) break;
|
488 |
+
}
|
489 |
+
|
490 |
+
// Random detection for demo
|
491 |
+
if (!detectedIntent && intents.length > 0) {
|
492 |
+
const randomIntent = intents[Math.floor(Math.random() * intents.length)];
|
493 |
+
detectedIntent = randomIntent.name;
|
494 |
+
confidence = 0.65;
|
495 |
+
}
|
496 |
+
|
497 |
+
this.testResult = {
|
498 |
+
success: true,
|
499 |
+
intent: detectedIntent,
|
500 |
+
confidence: confidence,
|
501 |
+
parameters: detectedIntent ? this.extractTestParameters(detectedIntent) : []
|
502 |
+
};
|
503 |
+
|
504 |
+
this.testing = false;
|
505 |
+
}, 1500);
|
506 |
+
}
|
507 |
+
|
508 |
+
private extractTestParameters(intentName: string): any[] {
|
509 |
+
// Mock parameter extraction
|
510 |
+
const intent = this.intents.value.find((i: any) => i.name === intentName);
|
511 |
+
if (!intent) return [];
|
512 |
+
|
513 |
+
return intent.parameters.map((param: any) => ({
|
514 |
+
name: param.name,
|
515 |
+
value: param.type === 'date' ? '2025-06-15' : 'test_value',
|
516 |
+
extracted: Math.random() > 0.3
|
517 |
+
}));
|
518 |
+
}
|
519 |
+
|
520 |
+
async getAvailableAPIs(): Promise<any[]> {
|
521 |
+
try {
|
522 |
+
return await this.apiService.getAPIs().toPromise() || [];
|
523 |
+
} catch {
|
524 |
+
return [];
|
525 |
+
}
|
526 |
+
}
|
527 |
+
|
528 |
+
private async reloadProject() {
|
529 |
+
this.loading = true;
|
530 |
+
try {
|
531 |
+
const projects = await this.apiService.getProjects().toPromise() || [];
|
532 |
+
const updatedProject = projects.find(p => p.id === this.project.id);
|
533 |
+
|
534 |
+
if (updatedProject) {
|
535 |
+
this.project = updatedProject;
|
536 |
+
this.versions = [...updatedProject.versions].sort((a, b) => b.id - a.id);
|
537 |
}
|
538 |
+
} catch (error) {
|
539 |
+
console.error('Failed to reload project:', error);
|
540 |
+
} finally {
|
541 |
+
this.loading = false;
|
542 |
}
|
543 |
+
}
|
544 |
+
|
545 |
+
compareVersions() {
|
546 |
+
// TODO: Implement version comparison
|
547 |
+
this.snackBar.open('Version comparison coming soon', 'Close', { duration: 3000 });
|
548 |
+
}
|
|
|
549 |
|
550 |
close() {
|
551 |
+
this.dialogRef.close(true);
|
552 |
}
|
553 |
}
|