ciyidogan commited on
Commit
13a2bd1
·
verified ·
1 Parent(s): f7b10e8

Update flare-ui/src/app/components/environment/environment.component.ts

Browse files
flare-ui/src/app/components/environment/environment.component.ts CHANGED
@@ -1,9 +1,22 @@
1
  import { Component, OnInit } from '@angular/core';
2
- import { FormBuilder, FormGroup, Validators } from '@angular/forms';
3
  import { MatSnackBar } from '@angular/material/snack-bar';
4
- import { ApiService } from '../services/api.service';
5
- import { finalize } from 'rxjs/operators';
 
 
 
 
 
 
 
 
 
 
 
 
6
 
 
7
  interface ProviderConfig {
8
  type: string;
9
  name: string;
@@ -21,202 +34,195 @@ interface ProviderSettings {
21
  settings: any;
22
  }
23
 
 
 
 
 
 
 
 
 
24
  @Component({
25
  selector: 'app-environment',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  templateUrl: './environment.component.html',
27
  styleUrls: ['./environment.component.scss']
28
  })
29
  export class EnvironmentComponent implements OnInit {
30
- environmentForm!: FormGroup;
31
  loading = false;
32
  saving = false;
33
- showInternalPrompt = false;
34
- showParameterCollection = false;
35
 
36
  // Provider lists
37
- allProviders: ProviderConfig[] = [];
38
  llmProviders: ProviderConfig[] = [];
39
  ttsProviders: ProviderConfig[] = [];
40
  sttProviders: ProviderConfig[] = [];
41
 
42
- // Current provider configs
43
  currentLLMProvider?: ProviderConfig;
44
  currentTTSProvider?: ProviderConfig;
45
  currentSTTProvider?: ProviderConfig;
 
 
 
 
46
 
47
  constructor(
48
  private fb: FormBuilder,
49
  private apiService: ApiService,
 
50
  private snackBar: MatSnackBar
51
- ) {}
52
-
53
- ngOnInit(): void {
54
- this.initializeForm();
55
- this.loadEnvironment();
56
- }
57
-
58
- initializeForm(): void {
59
- this.environmentForm = this.fb.group({
60
  // LLM Provider
61
  llm_provider_name: ['', Validators.required],
62
  llm_provider_api_key: [''],
63
  llm_provider_endpoint: [''],
64
- internal_prompt: [''],
65
-
66
- // Parameter Collection Config
67
- max_params_per_question: [2, [Validators.min(1), Validators.max(5)]],
68
- retry_unanswered: [true],
69
- collection_prompt: [''],
70
 
71
  // TTS Provider
72
- tts_provider_name: ['no_tts'],
73
  tts_provider_api_key: [''],
74
  tts_provider_endpoint: [''],
75
- tts_use_ssml: [false],
76
 
77
  // STT Provider
78
- stt_provider_name: ['no_stt'],
79
  stt_provider_api_key: [''],
80
- stt_provider_endpoint: [''],
81
- stt_speech_timeout_ms: [2000],
82
- stt_noise_reduction_level: [2],
83
- stt_vad_sensitivity: [0.5],
84
- stt_language: ['tr-TR'],
85
- stt_model: ['latest_long'],
86
- stt_use_enhanced: [true],
87
- stt_enable_punctuation: [true],
88
- stt_interim_results: [true]
89
  });
 
90
 
91
- // Subscribe to provider changes
92
- this.environmentForm.get('llm_provider_name')?.valueChanges.subscribe(name => {
93
- this.onLLMProviderChange(name);
94
- });
95
-
96
- this.environmentForm.get('tts_provider_name')?.valueChanges.subscribe(name => {
97
- this.onTTSProviderChange(name);
98
- });
99
-
100
- this.environmentForm.get('stt_provider_name')?.valueChanges.subscribe(name => {
101
- this.onSTTProviderChange(name);
102
- });
103
  }
104
 
105
- loadEnvironment(): void {
106
  this.loading = true;
107
- this.apiService.getEnvironment()
108
- .pipe(finalize(() => this.loading = false))
109
- .subscribe({
110
- next: (data) => {
111
- // Store providers
112
- this.allProviders = data.providers || [];
113
- this.llmProviders = this.allProviders.filter(p => p.type === 'llm');
114
- this.ttsProviders = [{
115
- type: 'tts',
116
- name: 'no_tts',
117
- display_name: 'No TTS',
118
- requires_endpoint: false,
119
- requires_api_key: false,
120
- requires_repo_info: false
121
- }, ...this.allProviders.filter(p => p.type === 'tts')];
122
- this.sttProviders = [{
123
- type: 'stt',
124
- name: 'no_stt',
125
- display_name: 'No STT',
126
- requires_endpoint: false,
127
- requires_api_key: false,
128
- requires_repo_info: false
129
- }, ...this.allProviders.filter(p => p.type === 'stt')];
130
-
131
- // Set form values
132
- this.environmentForm.patchValue({
133
- // LLM Provider
134
- llm_provider_name: data.llm_provider?.name || '',
135
- llm_provider_api_key: data.llm_provider?.api_key || '',
136
- llm_provider_endpoint: data.llm_provider?.endpoint || '',
137
- internal_prompt: data.llm_provider?.settings?.internal_prompt || '',
138
-
139
- // Parameter Collection
140
- max_params_per_question: data.llm_provider?.settings?.parameter_collection_config?.max_params_per_question || 2,
141
- retry_unanswered: data.llm_provider?.settings?.parameter_collection_config?.retry_unanswered ?? true,
142
- collection_prompt: data.llm_provider?.settings?.parameter_collection_config?.collection_prompt || '',
143
-
144
- // TTS Provider
145
- tts_provider_name: data.tts_provider?.name || 'no_tts',
146
- tts_provider_api_key: data.tts_provider?.api_key || '',
147
- tts_provider_endpoint: data.tts_provider?.endpoint || '',
148
- tts_use_ssml: data.tts_provider?.settings?.use_ssml || false,
149
-
150
- // STT Provider
151
- stt_provider_name: data.stt_provider?.name || 'no_stt',
152
- stt_provider_api_key: data.stt_provider?.api_key || '',
153
- stt_provider_endpoint: data.stt_provider?.endpoint || '',
154
- stt_speech_timeout_ms: data.stt_provider?.settings?.speech_timeout_ms || 2000,
155
- stt_noise_reduction_level: data.stt_provider?.settings?.noise_reduction_level || 2,
156
- stt_vad_sensitivity: data.stt_provider?.settings?.vad_sensitivity || 0.5,
157
- stt_language: data.stt_provider?.settings?.language || 'tr-TR',
158
- stt_model: data.stt_provider?.settings?.model || 'latest_long',
159
- stt_use_enhanced: data.stt_provider?.settings?.use_enhanced ?? true,
160
- stt_enable_punctuation: data.stt_provider?.settings?.enable_punctuation ?? true,
161
- stt_interim_results: data.stt_provider?.settings?.interim_results ?? true
162
  });
163
 
164
- // Trigger provider change handlers
165
- this.onLLMProviderChange(data.llm_provider?.name || '');
166
- this.onTTSProviderChange(data.tts_provider?.name || 'no_tts');
167
- this.onSTTProviderChange(data.stt_provider?.name || 'no_stt');
168
- },
169
- error: (error) => {
170
- this.snackBar.open('Failed to load environment configuration', 'Close', {
171
- duration: 3000,
172
- panelClass: ['error-snackbar']
 
 
 
173
  });
 
174
  }
175
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
177
 
178
- onLLMProviderChange(name: string): void {
179
- this.currentLLMProvider = this.llmProviders.find(p => p.name === name);
 
 
 
 
180
 
181
  if (this.currentLLMProvider) {
182
- // Update validators
183
- const apiKeyControl = this.environmentForm.get('llm_provider_api_key');
184
- const endpointControl = this.environmentForm.get('llm_provider_endpoint');
185
-
186
  if (this.currentLLMProvider.requires_api_key) {
 
187
  apiKeyControl?.setValidators([Validators.required]);
188
  } else {
 
189
  apiKeyControl?.clearValidators();
190
  }
191
 
 
192
  if (this.currentLLMProvider.requires_endpoint) {
193
- endpointControl?.setValidators([Validators.required, Validators.pattern('https?://.+')]);
 
194
  } else {
 
195
  endpointControl?.clearValidators();
196
  }
197
 
198
  apiKeyControl?.updateValueAndValidity();
199
  endpointControl?.updateValueAndValidity();
200
  }
 
 
 
201
  }
202
 
203
- onTTSProviderChange(name: string): void {
204
- this.currentTTSProvider = this.ttsProviders.find(p => p.name === name);
 
 
 
205
 
206
  if (this.currentTTSProvider) {
207
- const apiKeyControl = this.environmentForm.get('tts_provider_api_key');
208
- const endpointControl = this.environmentForm.get('tts_provider_endpoint');
209
-
210
- if (this.currentTTSProvider.requires_api_key && name !== 'no_tts') {
211
  apiKeyControl?.setValidators([Validators.required]);
212
  } else {
 
213
  apiKeyControl?.clearValidators();
 
214
  }
215
 
216
- if (this.currentTTSProvider.requires_endpoint && name !== 'no_tts') {
217
- endpointControl?.setValidators([Validators.required, Validators.pattern('https?://.+')]);
 
 
218
  } else {
 
219
  endpointControl?.clearValidators();
 
220
  }
221
 
222
  apiKeyControl?.updateValueAndValidity();
@@ -224,23 +230,31 @@ export class EnvironmentComponent implements OnInit {
224
  }
225
  }
226
 
227
- onSTTProviderChange(name: string): void {
228
- this.currentSTTProvider = this.sttProviders.find(p => p.name === name);
 
 
 
229
 
230
  if (this.currentSTTProvider) {
231
- const apiKeyControl = this.environmentForm.get('stt_provider_api_key');
232
- const endpointControl = this.environmentForm.get('stt_provider_endpoint');
233
-
234
- if (this.currentSTTProvider.requires_api_key && name !== 'no_stt') {
235
  apiKeyControl?.setValidators([Validators.required]);
236
  } else {
 
237
  apiKeyControl?.clearValidators();
 
238
  }
239
 
240
- if (this.currentSTTProvider.requires_endpoint && name !== 'no_stt') {
241
- endpointControl?.setValidators([Validators.required, Validators.pattern('https?://.+')]);
 
 
242
  } else {
 
243
  endpointControl?.clearValidators();
 
244
  }
245
 
246
  apiKeyControl?.updateValueAndValidity();
@@ -248,154 +262,204 @@ export class EnvironmentComponent implements OnInit {
248
  }
249
  }
250
 
251
- save(): void {
252
- if (this.environmentForm.invalid) {
253
- Object.keys(this.environmentForm.controls).forEach(key => {
254
- const control = this.environmentForm.get(key);
255
- if (control && control.invalid) {
256
- control.markAsTouched();
257
- }
258
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  return;
260
  }
261
 
262
  this.saving = true;
263
- const formValue = this.environmentForm.value;
264
 
265
- // Build environment update payload
266
- const payload = {
 
 
267
  llm_provider: {
268
  name: formValue.llm_provider_name,
269
  api_key: formValue.llm_provider_api_key || null,
270
  endpoint: formValue.llm_provider_endpoint || null,
271
  settings: {
272
- internal_prompt: formValue.internal_prompt || null,
273
- parameter_collection_config: {
274
- max_params_per_question: formValue.max_params_per_question,
275
- retry_unanswered: formValue.retry_unanswered,
276
- collection_prompt: formValue.collection_prompt || null
277
- }
278
  }
279
  },
280
  tts_provider: {
281
  name: formValue.tts_provider_name,
282
  api_key: formValue.tts_provider_api_key || null,
283
  endpoint: formValue.tts_provider_endpoint || null,
284
- settings: {
285
- use_ssml: formValue.tts_use_ssml
286
- }
287
  },
288
  stt_provider: {
289
  name: formValue.stt_provider_name,
290
  api_key: formValue.stt_provider_api_key || null,
291
  endpoint: formValue.stt_provider_endpoint || null,
292
- settings: {
293
- speech_timeout_ms: formValue.stt_speech_timeout_ms,
294
- noise_reduction_level: formValue.stt_noise_reduction_level,
295
- vad_sensitivity: formValue.stt_vad_sensitivity,
296
- language: formValue.stt_language,
297
- model: formValue.stt_model,
298
- use_enhanced: formValue.stt_use_enhanced,
299
- enable_punctuation: formValue.stt_enable_punctuation,
300
- interim_results: formValue.stt_interim_results
301
- }
302
- }
303
  };
304
-
305
- this.apiService.updateEnvironment(payload)
306
- .pipe(finalize(() => this.saving = false))
307
- .subscribe({
308
- next: () => {
309
- this.snackBar.open('Environment configuration saved successfully', 'Close', {
310
- duration: 3000,
311
- panelClass: ['success-snackbar']
312
- });
313
- },
314
- error: (error) => {
315
- this.snackBar.open(
316
- error.error?.detail || 'Failed to save environment configuration',
317
- 'Close',
318
- {
319
- duration: 5000,
320
- panelClass: ['error-snackbar']
321
- }
322
- );
323
- }
324
- });
325
  }
326
 
327
- testConnection(): void {
328
- const endpoint = this.environmentForm.get('llm_provider_endpoint')?.value;
329
- const apiKey = this.environmentForm.get('llm_provider_api_key')?.value;
330
- const provider = this.environmentForm.get('llm_provider_name')?.value;
331
-
332
- if (!endpoint) {
333
- this.snackBar.open('Please enter an endpoint URL', 'Close', {
334
- duration: 3000,
335
- panelClass: ['error-snackbar']
336
- });
337
- return;
338
- }
339
-
340
- this.loading = true;
341
- this.apiService.testConnection({ endpoint, api_key: apiKey, provider })
342
- .pipe(finalize(() => this.loading = false))
343
- .subscribe({
344
- next: (result) => {
345
- if (result.success) {
346
- this.snackBar.open('Connection successful!', 'Close', {
347
- duration: 3000,
348
- panelClass: ['success-snackbar']
349
- });
350
- } else {
351
- this.snackBar.open(result.message || 'Connection failed', 'Close', {
352
- duration: 5000,
353
- panelClass: ['error-snackbar']
354
- });
355
- }
356
- },
357
- error: (error) => {
358
- this.snackBar.open('Connection test failed', 'Close', {
359
- duration: 3000,
360
- panelClass: ['error-snackbar']
361
- });
362
- }
363
- });
364
  }
365
 
366
- toggleInternalPrompt(): void {
367
- this.showInternalPrompt = !this.showInternalPrompt;
 
 
 
 
 
368
  }
369
 
370
- toggleParameterCollection(): void {
371
- this.showParameterCollection = !this.showParameterCollection;
 
 
 
 
 
372
  }
373
 
374
- isLLMEndpointRequired(): boolean {
375
- return this.currentLLMProvider?.requires_endpoint || false;
 
 
 
 
 
 
376
  }
377
 
378
- isLLMApiKeyRequired(): boolean {
379
- return this.currentLLMProvider?.requires_api_key || false;
 
 
 
 
 
 
 
 
 
 
 
380
  }
381
 
382
- isTTSEnabled(): boolean {
383
- return this.environmentForm.get('tts_provider_name')?.value !== 'no_tts';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  }
385
 
386
- isSTTEnabled(): boolean {
387
- return this.environmentForm.get('stt_provider_name')?.value !== 'no_stt';
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  }
389
 
390
- getLLMProviderDescription(): string {
391
- return this.currentLLMProvider?.description || '';
392
  }
393
 
394
- getTTSProviderDescription(): string {
395
- return this.currentTTSProvider?.description || '';
396
  }
397
 
398
- getSTTProviderDescription(): string {
399
- return this.currentSTTProvider?.description || '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  }
401
  }
 
1
  import { Component, OnInit } from '@angular/core';
2
+ import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
3
  import { MatSnackBar } from '@angular/material/snack-bar';
4
+ import { ApiService } from '../../services/api.service';
5
+ import { EnvironmentService } from '../../services/environment.service';
6
+ import { CommonModule } from '@angular/common';
7
+ import { MatCardModule } from '@angular/material/card';
8
+ import { MatFormFieldModule } from '@angular/material/form-field';
9
+ import { MatInputModule } from '@angular/material/input';
10
+ import { MatSelectModule } from '@angular/material/select';
11
+ import { MatButtonModule } from '@angular/material/button';
12
+ import { MatIconModule } from '@angular/material/icon';
13
+ import { MatSliderModule } from '@angular/material/slider';
14
+ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
15
+ import { MatExpansionModule } from '@angular/material/expansion';
16
+ import { MatDividerModule } from '@angular/material/divider';
17
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
18
 
19
+ // Provider interfaces
20
  interface ProviderConfig {
21
  type: string;
22
  name: string;
 
34
  settings: any;
35
  }
36
 
37
+ interface EnvironmentConfig {
38
+ llm_provider: ProviderSettings;
39
+ tts_provider: ProviderSettings;
40
+ stt_provider: ProviderSettings;
41
+ providers: ProviderConfig[];
42
+ parameter_collection_config: any;
43
+ }
44
+
45
  @Component({
46
  selector: 'app-environment',
47
+ standalone: true,
48
+ imports: [
49
+ CommonModule,
50
+ ReactiveFormsModule,
51
+ MatCardModule,
52
+ MatFormFieldModule,
53
+ MatInputModule,
54
+ MatSelectModule,
55
+ MatButtonModule,
56
+ MatIconModule,
57
+ MatSliderModule,
58
+ MatSlideToggleModule,
59
+ MatExpansionModule,
60
+ MatDividerModule,
61
+ MatProgressSpinnerModule
62
+ ],
63
  templateUrl: './environment.component.html',
64
  styleUrls: ['./environment.component.scss']
65
  })
66
  export class EnvironmentComponent implements OnInit {
67
+ form: FormGroup;
68
  loading = false;
69
  saving = false;
 
 
70
 
71
  // Provider lists
 
72
  llmProviders: ProviderConfig[] = [];
73
  ttsProviders: ProviderConfig[] = [];
74
  sttProviders: ProviderConfig[] = [];
75
 
76
+ // Current provider configurations
77
  currentLLMProvider?: ProviderConfig;
78
  currentTTSProvider?: ProviderConfig;
79
  currentSTTProvider?: ProviderConfig;
80
+
81
+ // Settings
82
+ parameterCollectionConfig: any = {};
83
+ internalPrompt = '';
84
 
85
  constructor(
86
  private fb: FormBuilder,
87
  private apiService: ApiService,
88
+ private environmentService: EnvironmentService,
89
  private snackBar: MatSnackBar
90
+ ) {
91
+ this.form = this.fb.group({
 
 
 
 
 
 
 
92
  // LLM Provider
93
  llm_provider_name: ['', Validators.required],
94
  llm_provider_api_key: [''],
95
  llm_provider_endpoint: [''],
 
 
 
 
 
 
96
 
97
  // TTS Provider
98
+ tts_provider_name: ['no_tts', Validators.required],
99
  tts_provider_api_key: [''],
100
  tts_provider_endpoint: [''],
 
101
 
102
  // STT Provider
103
+ stt_provider_name: ['no_stt', Validators.required],
104
  stt_provider_api_key: [''],
105
+ stt_provider_endpoint: ['']
 
 
 
 
 
 
 
 
106
  });
107
+ }
108
 
109
+ ngOnInit() {
110
+ this.loadEnvironment();
 
 
 
 
 
 
 
 
 
 
111
  }
112
 
113
+ loadEnvironment() {
114
  this.loading = true;
115
+ this.apiService.getEnvironment().subscribe({
116
+ next: (data: EnvironmentConfig) => {
117
+ // Separate providers by type
118
+ this.llmProviders = data.providers.filter(p => p.type === 'llm');
119
+ this.ttsProviders = data.providers.filter(p => p.type === 'tts');
120
+ this.sttProviders = data.providers.filter(p => p.type === 'stt');
121
+
122
+ // Load current settings
123
+ if (data.llm_provider) {
124
+ this.form.patchValue({
125
+ llm_provider_name: data.llm_provider.name,
126
+ llm_provider_api_key: data.llm_provider.api_key || '',
127
+ llm_provider_endpoint: data.llm_provider.endpoint || ''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  });
129
 
130
+ // Extract settings
131
+ this.internalPrompt = data.llm_provider.settings?.internal_prompt || '';
132
+ this.parameterCollectionConfig = data.llm_provider.settings?.parameter_collection_config || this.getDefaultParameterCollectionConfig();
133
+
134
+ this.onLLMProviderChange(data.llm_provider.name);
135
+ }
136
+
137
+ if (data.tts_provider) {
138
+ this.form.patchValue({
139
+ tts_provider_name: data.tts_provider.name,
140
+ tts_provider_api_key: data.tts_provider.api_key || '',
141
+ tts_provider_endpoint: data.tts_provider.endpoint || ''
142
  });
143
+ this.onTTSProviderChange(data.tts_provider.name);
144
  }
145
+
146
+ if (data.stt_provider) {
147
+ this.form.patchValue({
148
+ stt_provider_name: data.stt_provider.name,
149
+ stt_provider_api_key: data.stt_provider.api_key || '',
150
+ stt_provider_endpoint: data.stt_provider.endpoint || ''
151
+ });
152
+ this.onSTTProviderChange(data.stt_provider.name);
153
+ }
154
+
155
+ this.loading = false;
156
+ },
157
+ error: (err) => {
158
+ this.snackBar.open('Failed to load environment configuration', 'Close', {
159
+ duration: 5000,
160
+ panelClass: 'error-snackbar'
161
+ });
162
+ this.loading = false;
163
+ }
164
+ });
165
  }
166
 
167
+ onLLMProviderChange(providerName: string) {
168
+ this.currentLLMProvider = this.llmProviders.find(p => p.name === providerName);
169
+
170
+ // Enable/disable fields based on provider requirements
171
+ const apiKeyControl = this.form.get('llm_provider_api_key');
172
+ const endpointControl = this.form.get('llm_provider_endpoint');
173
 
174
  if (this.currentLLMProvider) {
175
+ // API Key field
 
 
 
176
  if (this.currentLLMProvider.requires_api_key) {
177
+ apiKeyControl?.enable();
178
  apiKeyControl?.setValidators([Validators.required]);
179
  } else {
180
+ apiKeyControl?.disable();
181
  apiKeyControl?.clearValidators();
182
  }
183
 
184
+ // Endpoint field
185
  if (this.currentLLMProvider.requires_endpoint) {
186
+ endpointControl?.enable();
187
+ endpointControl?.setValidators([Validators.required]);
188
  } else {
189
+ endpointControl?.disable();
190
  endpointControl?.clearValidators();
191
  }
192
 
193
  apiKeyControl?.updateValueAndValidity();
194
  endpointControl?.updateValueAndValidity();
195
  }
196
+
197
+ // Update environment service
198
+ this.updateEnvironmentService();
199
  }
200
 
201
+ onTTSProviderChange(providerName: string) {
202
+ this.currentTTSProvider = this.ttsProviders.find(p => p.name === providerName);
203
+
204
+ const apiKeyControl = this.form.get('tts_provider_api_key');
205
+ const endpointControl = this.form.get('tts_provider_endpoint');
206
 
207
  if (this.currentTTSProvider) {
208
+ // API Key field
209
+ if (this.currentTTSProvider.requires_api_key) {
210
+ apiKeyControl?.enable();
 
211
  apiKeyControl?.setValidators([Validators.required]);
212
  } else {
213
+ apiKeyControl?.disable();
214
  apiKeyControl?.clearValidators();
215
+ apiKeyControl?.setValue('');
216
  }
217
 
218
+ // Endpoint field
219
+ if (this.currentTTSProvider.requires_endpoint) {
220
+ endpointControl?.enable();
221
+ endpointControl?.setValidators([Validators.required]);
222
  } else {
223
+ endpointControl?.disable();
224
  endpointControl?.clearValidators();
225
+ endpointControl?.setValue('');
226
  }
227
 
228
  apiKeyControl?.updateValueAndValidity();
 
230
  }
231
  }
232
 
233
+ onSTTProviderChange(providerName: string) {
234
+ this.currentSTTProvider = this.sttProviders.find(p => p.name === providerName);
235
+
236
+ const apiKeyControl = this.form.get('stt_provider_api_key');
237
+ const endpointControl = this.form.get('stt_provider_endpoint');
238
 
239
  if (this.currentSTTProvider) {
240
+ // API Key field
241
+ if (this.currentSTTProvider.requires_api_key) {
242
+ apiKeyControl?.enable();
 
243
  apiKeyControl?.setValidators([Validators.required]);
244
  } else {
245
+ apiKeyControl?.disable();
246
  apiKeyControl?.clearValidators();
247
+ apiKeyControl?.setValue('');
248
  }
249
 
250
+ // Endpoint field
251
+ if (this.currentSTTProvider.requires_endpoint) {
252
+ endpointControl?.enable();
253
+ endpointControl?.setValidators([Validators.required]);
254
  } else {
255
+ endpointControl?.disable();
256
  endpointControl?.clearValidators();
257
+ endpointControl?.setValue('');
258
  }
259
 
260
  apiKeyControl?.updateValueAndValidity();
 
262
  }
263
  }
264
 
265
+ getDefaultParameterCollectionConfig() {
266
+ return {
267
+ enabled: true,
268
+ max_params_per_question: 2,
269
+ show_all_required: true,
270
+ ask_optional_params: false,
271
+ group_related_params: true,
272
+ min_confidence_score: 0.7,
273
+ collection_prompt: `You are a smart parameter collection assistant.
274
+ Based on the conversation and the parameters needed, generate a natural question to collect missing parameters.
275
+
276
+ Rules:
277
+ 1. Ask for maximum {{max_params}} parameters in one question
278
+ 2. Group parameters that naturally go together (like from/to cities, dates)
279
+ 3. If some parameters were asked before but not answered, include them again
280
+ 4. Be natural and conversational in {{project_language}}
281
+ 5. Use context from the conversation to make the question flow naturally
282
+
283
+ Generate ONLY the question, nothing else.`
284
+ };
285
+ }
286
+
287
+ save() {
288
+ if (this.form.invalid) {
289
+ this.form.markAllAsTouched();
290
  return;
291
  }
292
 
293
  this.saving = true;
 
294
 
295
+ const formValue = this.form.value;
296
+
297
+ // Build provider settings
298
+ const saveData = {
299
  llm_provider: {
300
  name: formValue.llm_provider_name,
301
  api_key: formValue.llm_provider_api_key || null,
302
  endpoint: formValue.llm_provider_endpoint || null,
303
  settings: {
304
+ internal_prompt: this.internalPrompt,
305
+ parameter_collection_config: this.parameterCollectionConfig
 
 
 
 
306
  }
307
  },
308
  tts_provider: {
309
  name: formValue.tts_provider_name,
310
  api_key: formValue.tts_provider_api_key || null,
311
  endpoint: formValue.tts_provider_endpoint || null,
312
+ settings: {}
 
 
313
  },
314
  stt_provider: {
315
  name: formValue.stt_provider_name,
316
  api_key: formValue.stt_provider_api_key || null,
317
  endpoint: formValue.stt_provider_endpoint || null,
318
+ settings: {}
319
+ },
320
+ parameter_collection_config: this.parameterCollectionConfig
 
 
 
 
 
 
 
 
321
  };
322
+
323
+ this.apiService.updateEnvironment(saveData).subscribe({
324
+ next: () => {
325
+ // Update environment service
326
+ this.updateEnvironmentService();
327
+
328
+ this.snackBar.open('Environment configuration saved successfully', 'Close', {
329
+ duration: 3000
330
+ });
331
+ this.saving = false;
332
+ },
333
+ error: (err) => {
334
+ this.snackBar.open(
335
+ err.error?.detail || 'Failed to save configuration',
336
+ 'Close',
337
+ { duration: 5000, panelClass: 'error-snackbar' }
338
+ );
339
+ this.saving = false;
340
+ }
341
+ });
 
342
  }
343
 
344
+ private updateEnvironmentService() {
345
+ // Update environment service with current form values
346
+ const formValue = this.form.value;
347
+ this.environmentService.setLLMProvider(formValue.llm_provider_name);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
 
350
+ getLLMProviderIcon(provider: ProviderConfig): string {
351
+ const iconMap: { [key: string]: string } = {
352
+ 'spark': 'flash_on',
353
+ 'gpt4o': 'psychology',
354
+ 'gpt4o-mini': 'psychology_alt'
355
+ };
356
+ return iconMap[provider.name] || 'smart_toy';
357
  }
358
 
359
+ getTTSProviderIcon(provider: ProviderConfig): string {
360
+ const iconMap: { [key: string]: string } = {
361
+ 'no_tts': 'volume_off',
362
+ 'elevenlabs': 'record_voice_over',
363
+ 'blaze': 'campaign'
364
+ };
365
+ return iconMap[provider.name] || 'volume_up';
366
  }
367
 
368
+ getSTTProviderIcon(provider: ProviderConfig): string {
369
+ const iconMap: { [key: string]: string } = {
370
+ 'no_stt': 'mic_off',
371
+ 'google': 'g_translate',
372
+ 'azure': 'cloud',
373
+ 'flicker': 'light_mode'
374
+ };
375
+ return iconMap[provider.name] || 'mic';
376
  }
377
 
378
+ getApiKeyLabel(providerType: string): string {
379
+ const provider = providerType === 'llm' ? this.currentLLMProvider :
380
+ providerType === 'tts' ? this.currentTTSProvider :
381
+ this.currentSTTProvider;
382
+
383
+ if (!provider) return 'API Key';
384
+
385
+ // Special cases
386
+ if (provider.name === 'google' && providerType === 'stt') {
387
+ return 'Service Account JSON Path';
388
+ }
389
+
390
+ return 'API Key';
391
  }
392
 
393
+ getApiKeyPlaceholder(providerType: string): string {
394
+ const provider = providerType === 'llm' ? this.currentLLMProvider :
395
+ providerType === 'tts' ? this.currentTTSProvider :
396
+ this.currentSTTProvider;
397
+
398
+ if (!provider) return 'Enter API key';
399
+
400
+ // Provider-specific placeholders
401
+ const placeholders: { [key: string]: string } = {
402
+ 'spark': 'Enter Spark token',
403
+ 'gpt4o': 'sk-...',
404
+ 'gpt4o-mini': 'sk-...',
405
+ 'google': '/path/to/credentials.json',
406
+ 'azure': 'Enter Azure subscription key',
407
+ 'elevenlabs': 'Enter ElevenLabs API key'
408
+ };
409
+
410
+ return placeholders[provider.name] || 'Enter API key';
411
  }
412
 
413
+ getEndpointPlaceholder(providerType: string): string {
414
+ const provider = providerType === 'llm' ? this.currentLLMProvider :
415
+ providerType === 'tts' ? this.currentTTSProvider :
416
+ this.currentSTTProvider;
417
+
418
+ if (!provider) return 'https://...';
419
+
420
+ // Provider-specific placeholders
421
+ const placeholders: { [key: string]: string } = {
422
+ 'spark': 'http://localhost:7861',
423
+ 'blaze': 'https://blaze-tts.example.com',
424
+ 'flicker': 'https://flicker-stt.example.com'
425
+ };
426
+
427
+ return placeholders[provider.name] || 'https://...';
428
  }
429
 
430
+ formatSliderLabel(value: number): string {
431
+ return `${value}`;
432
  }
433
 
434
+ resetCollectionPrompt() {
435
+ this.parameterCollectionConfig.collection_prompt = this.getDefaultParameterCollectionConfig().collection_prompt;
436
  }
437
 
438
+ testConnection() {
439
+ if (!this.currentLLMProvider?.requires_endpoint) {
440
+ this.snackBar.open('This provider does not require endpoint testing', 'Close', {
441
+ duration: 3000
442
+ });
443
+ return;
444
+ }
445
+
446
+ const endpoint = this.form.get('llm_provider_endpoint')?.value;
447
+ if (!endpoint) {
448
+ this.snackBar.open('Please enter an endpoint URL', 'Close', {
449
+ duration: 3000
450
+ });
451
+ return;
452
+ }
453
+
454
+ this.snackBar.open('Testing connection...', undefined, {
455
+ duration: 2000
456
+ });
457
+
458
+ // TODO: Implement actual connection test
459
+ setTimeout(() => {
460
+ this.snackBar.open('Connection successful!', 'Close', {
461
+ duration: 3000
462
+ });
463
+ }, 2000);
464
  }
465
  }