ciyidogan commited on
Commit
1a93df8
·
verified ·
1 Parent(s): 85cf094

Upload environment.component.ts

Browse files
flare-ui/src/app/components/environment/environment.component.ts CHANGED
@@ -1,862 +1,493 @@
1
- import { Component, inject, OnInit } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup } from '@angular/forms';
4
- import { MatCardModule } from '@angular/material/card';
5
- import { MatFormFieldModule } from '@angular/material/form-field';
6
- import { MatInputModule } from '@angular/material/input';
7
- import { MatSelectModule } from '@angular/material/select';
8
- import { MatButtonModule } from '@angular/material/button';
9
- import { MatIconModule } from '@angular/material/icon';
10
- import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
11
- import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
12
- import { MatExpansionModule } from '@angular/material/expansion';
13
- import { MatSliderModule } from '@angular/material/slider';
14
- import { MatTooltipModule } from '@angular/material/tooltip';
15
- import { MatCheckboxModule } from '@angular/material/checkbox';
16
- import { ApiService, Environment, STTSettings } from '../../services/api.service';
17
- import { EnvironmentService } from '../../services/environment.service';
18
-
19
- @Component({
20
- selector: 'app-environment',
21
- standalone: true,
22
- imports: [
23
- CommonModule,
24
- FormsModule,
25
- ReactiveFormsModule, // Bunu ekle
26
- MatCardModule,
27
- MatFormFieldModule,
28
- MatInputModule,
29
- MatSelectModule,
30
- MatButtonModule,
31
- MatIconModule,
32
- MatProgressSpinnerModule,
33
- MatSnackBarModule,
34
- MatExpansionModule,
35
- MatSliderModule,
36
- MatTooltipModule,
37
- MatCheckboxModule
38
- ],
39
- template: `
40
- <div class="environment-container">
41
- <mat-card>
42
- <mat-card-header>
43
- <mat-card-title>
44
- <mat-icon>settings</mat-icon>
45
- Environment Configuration
46
- </mat-card-title>
47
- </mat-card-header>
48
-
49
- <mat-card-content>
50
- @if (isGPTMode()) {
51
- <mat-card class="info-card">
52
- <mat-card-content>
53
- <mat-icon>info</mat-icon>
54
- <div>
55
- <strong>{{ environment.work_mode === 'gpt4o' ? 'GPT-4o' : 'GPT-4o Mini' }} Mode</strong>
56
- <p>This mode uses OpenAI's API which has usage-based pricing.</p>
57
- <p>Approximate cost: {{ environment.work_mode === 'gpt4o' ? '$0.01-0.03' : '$0.001-0.003' }} per conversation turn.</p>
58
- </div>
59
- </mat-card-content>
60
- </mat-card>
61
- }
62
-
63
- <form (ngSubmit)="save()" #envForm="ngForm">
64
- <mat-form-field appearance="outline" class="full-width">
65
- <mat-label>Work Mode</mat-label>
66
- <mat-select
67
- name="workMode"
68
- formControlName="environment.work_mode"
69
- (selectionChange)="onWorkModeChange()"
70
- required
71
- [disabled]="loading"
72
- >
73
- <mat-option value="hfcloud">HF Cloud</mat-option>
74
- <mat-option value="cloud">Cloud</mat-option>
75
- <mat-option value="on-premise">On-Premise</mat-option>
76
- <mat-option value="gpt4o">GPT-4o</mat-option>
77
- <mat-option value="gpt4o-mini">GPT-4o Mini</mat-option>
78
- </mat-select>
79
- <mat-icon matPrefix>cloud</mat-icon>
80
- </mat-form-field>
81
-
82
- <mat-form-field appearance="outline" class="full-width">
83
- <mat-label>{{ getTokenLabel() }}</mat-label>
84
- <input
85
- matInput
86
- type="password"
87
- name="cloudToken"
88
- formControlName="environment.cloud_token"
89
- [disabled]="loading || environment.work_mode === 'on-premise'"
90
- [placeholder]="getTokenPlaceholder()"
91
- >
92
- <mat-icon matPrefix>vpn_key</mat-icon>
93
- <mat-hint>{{ isGPTMode() ? 'Required for OpenAI API access' : 'Required for HF Cloud and Cloud modes' }}</mat-hint>
94
- </mat-form-field>
95
-
96
- <mat-form-field appearance="outline" class="full-width">
97
- <mat-label>Spark Endpoint</mat-label>
98
- <input
99
- matInput
100
- type="url"
101
- name="sparkEndpoint"
102
- formControlName="environment.spark_endpoint"
103
- [required]="!isGPTMode()"
104
- [disabled]="loading || isGPTMode()"
105
- placeholder="https://spark-service.example.com"
106
- >
107
- <mat-icon matPrefix>link</mat-icon>
108
- <button
109
- mat-icon-button
110
- matSuffix
111
- type="button"
112
- (click)="testConnection()"
113
- [disabled]="loading || !environment.spark_endpoint || isGPTMode()"
114
- matTooltip="Test Connection"
115
- >
116
- <mat-icon>wifi_tethering</mat-icon>
117
- </button>
118
- @if (isGPTMode()) {
119
- <mat-hint>Not required for GPT mode</mat-hint>
120
- }
121
- </mat-form-field>
122
-
123
- <!-- TTS Configuration -->
124
- <div class="engine-row">
125
- <mat-form-field appearance="outline" class="engine-field">
126
- <mat-label>TTS Engine</mat-label>
127
- <mat-select
128
- name="ttsEngine"
129
- formControlName="environment.tts_engine"
130
- (selectionChange)="onTTSEngineChange()"
131
- [disabled]="loading"
132
- >
133
- <mat-option value="no_tts">No TTS</mat-option>
134
- <mat-option value="elevenlabs">ElevenLabs</mat-option>
135
- <mat-option value="blaze">Blaze (Coming Soon)</mat-option>
136
- </mat-select>
137
- <mat-icon matPrefix>record_voice_over</mat-icon>
138
- </mat-form-field>
139
-
140
- <mat-form-field appearance="outline" class="api-key-field">
141
- <mat-label>TTS API Key</mat-label>
142
- <input
143
- matInput
144
- type="password"
145
- name="ttsApiKey"
146
- formControlName="environment.tts_engine_api_key"
147
- [disabled]="loading || environment.tts_engine === 'no_tts'"
148
- [required]="environment.tts_engine !== 'no_tts'"
149
- placeholder="Enter TTS API key"
150
- >
151
- <mat-icon matPrefix>key</mat-icon>
152
- </mat-form-field>
153
- </div>
154
-
155
- <!-- STT Configuration -->
156
- <div class="engine-row">
157
- <mat-form-field appearance="outline" class="engine-field">
158
- <mat-label>STT Engine</mat-label>
159
- <mat-select
160
- name="sttEngine"
161
- formControlName="environment.stt_engine"
162
- (selectionChange)="onSTTEngineChange()"
163
- [disabled]="loading"
164
- >
165
- <mat-option value="no_stt">No STT</mat-option>
166
- <mat-option value="google">Google Cloud Speech-to-Text</mat-option>
167
- <mat-option value="azure">Azure Speech Services (Coming Soon)</mat-option>
168
- <mat-option value="amazon">Amazon Transcribe (Coming Soon)</mat-option>
169
- <mat-option value="gpt4o_realtime">GPT-4o Realtime (Coming Soon)</mat-option>
170
- <mat-option value="flicker">Flicker (Coming Soon)</mat-option>
171
- </mat-select>
172
- <mat-icon matPrefix>mic</mat-icon>
173
- </mat-form-field>
174
-
175
- <mat-form-field appearance="outline" class="api-key-field">
176
- <mat-label>STT API Key / Credentials</mat-label>
177
- <input
178
- matInput
179
- type="password"
180
- name="sttApiKey"
181
- formControlName="environment.stt_engine_api_key"
182
- [disabled]="loading || environment.stt_engine === 'no_stt'"
183
- [required]="environment.stt_engine !== 'no_stt'"
184
- [placeholder]="getSTTKeyPlaceholder()"
185
- >
186
- <mat-icon matPrefix>key</mat-icon>
187
- <button mat-icon-button matSuffix
188
- *ngIf="environment.stt_engine === 'google'"
189
- (click)="fileInput.click()"
190
- [disabled]="loading"
191
- matTooltip="Upload Google credentials JSON">
192
- <mat-icon>upload_file</mat-icon>
193
- </button>
194
- <input #fileInput type="file" accept=".json"
195
- style="display: none"
196
- (change)="onSTTCredentialsFileSelected($event)">
197
- </mat-form-field>
198
- </div>
199
-
200
- <!-- STT Settings Panel -->
201
- <mat-expansion-panel class="stt-settings-panel"
202
- *ngIf="environment.stt_engine !== 'no_stt'"
203
- [expanded]="sttSettingsExpanded">
204
- <mat-expansion-panel-header>
205
- <mat-panel-title>
206
- <mat-icon>settings_voice</mat-icon>
207
- STT Advanced Settings
208
- </mat-panel-title>
209
- <mat-panel-description>
210
- Configure speech recognition parameters
211
- </mat-panel-description>
212
- </mat-expansion-panel-header>
213
-
214
- <!-- Panel içeriğini ngIf ile kontrol et -->
215
- <ng-container *ngIf="environment.stt_settings && sttSettingsExpanded">
216
- <div class="stt-settings-grid">
217
-
218
- <mat-form-field appearance="outline">
219
- <mat-label>Speech Timeout (ms)</mat-label>
220
- <input matInput type="number"
221
- name="speechTimeout"
222
- [value]="sttSettings.speech_timeout_ms"
223
- (input)="updateSTTSetting('speech_timeout_ms', +$any($event.target).value)"
224
- min="500" max="5000" step="100"
225
- [disabled]="loading">
226
- <mat-icon matPrefix>timer</mat-icon>
227
- <mat-hint>Silence duration to end speech (500-5000ms)</mat-hint>
228
- </mat-form-field>
229
-
230
- <mat-form-field appearance="outline">
231
- <mat-label>Noise Reduction Level</mat-label>
232
- <mat-select name="noiseReduction"
233
- [value]="sttSettings.noise_reduction_level"
234
- (selectionChange)="updateSTTSetting('noise_reduction_level', $event.value)"
235
- [disabled]="loading">
236
- <mat-option [value]="0">Off</mat-option>
237
- <mat-option [value]="1">Low</mat-option>
238
- <mat-option [value]="2">Medium</mat-option>
239
- <mat-option [value]="3">High</mat-option>
240
- </mat-select>
241
- <mat-icon matPrefix>noise_aware</mat-icon>
242
- </mat-form-field>
243
-
244
- <mat-form-field appearance="outline" class="full-width">
245
- <mat-label>VAD Sensitivity</mat-label>
246
- <mat-slider min="0" max="1" step="0.1"
247
- [discrete]="true"
248
- [displayWith]="formatVAD">
249
- <input matSliderThumb
250
- name="vadSensitivity"
251
- [value]="sttSettings.vad_sensitivity"
252
- (input)="updateSTTSetting('vad_sensitivity', +$any($event.target).value)"
253
- [disabled]="loading">
254
- </mat-slider>
255
- <mat-icon matPrefix>graphic_eq</mat-icon>
256
- <mat-hint>Voice activity detection sensitivity (0=low, 1=high)</mat-hint>
257
- </mat-form-field>
258
-
259
- <mat-form-field appearance="outline">
260
- <mat-label>Language</mat-label>
261
- <mat-select name="sttLanguage"
262
- [value]="sttSettings.language"
263
- (selectionChange)="updateSTTSetting('language', $event.value)"
264
- [disabled]="loading">
265
- <mat-option value="tr-TR">Turkish (tr-TR)</mat-option>
266
- <mat-option value="en-US">English (en-US)</mat-option>
267
- <mat-option value="de-DE">German (de-DE)</mat-option>
268
- <mat-option value="fr-FR">French (fr-FR)</mat-option>
269
- <mat-option value="es-ES">Spanish (es-ES)</mat-option>
270
- </mat-select>
271
- <mat-icon matPrefix>language</mat-icon>
272
- </mat-form-field>
273
-
274
- <mat-form-field appearance="outline"
275
- *ngIf="environment.stt_engine === 'google'">
276
- <mat-label>Model</mat-label>
277
- <mat-select name="sttModel"
278
- [value]="sttSettings.model"
279
- (selectionChange)="updateSTTSetting('model', $event.value)"
280
- [disabled]="loading">
281
- <mat-option value="latest_long">Latest Long (Best for conversations)</mat-option>
282
- <mat-option value="command_and_search">Command & Search (Short queries)</mat-option>
283
- <mat-option value="phone_call">Phone Call (Telephony)</mat-option>
284
- <mat-option value="video">Video (Multiple speakers)</mat-option>
285
- </mat-select>
286
- <mat-icon matPrefix>model_training</mat-icon>
287
- </mat-form-field>
288
-
289
- <div class="checkbox-group">
290
- <mat-checkbox name="useEnhanced"
291
- [checked]="sttSettings.use_enhanced"
292
- (change)="updateSTTSetting('use_enhanced', $event.checked)"
293
- [disabled]="loading || environment.stt_engine !== 'google'">
294
- Use Enhanced Model
295
- <mat-icon matTooltip="Better accuracy but higher cost" class="info-icon">info</mat-icon>
296
- </mat-checkbox>
297
-
298
- <mat-checkbox name="enablePunctuation"
299
- [checked]="sttSettings.enable_punctuation"
300
- (change)="updateSTTSetting('enable_punctuation', $event.checked)"
301
- [disabled]="loading">
302
- Automatic Punctuation
303
- </mat-checkbox>
304
-
305
- <mat-checkbox name="interimResults"
306
- [checked]="sttSettings.interim_results"
307
- (change)="updateSTTSetting('interim_results', $event.checked)"
308
- [disabled]="loading">
309
- Show Interim Results
310
- </mat-checkbox>
311
- </div>
312
- </div>
313
-
314
- <div class="stt-info-card" *ngIf="environment.stt_engine === 'google'">
315
- <mat-icon>info</mat-icon>
316
- <div>
317
- <strong>Google Cloud Setup Required:</strong>
318
- <ol>
319
- <li>Create a Google Cloud project</li>
320
- <li>Enable Speech-to-Text API</li>
321
- <li>Create service account & download JSON key</li>
322
- <li>Upload JSON key file as STT API Key</li>
323
- </ol>
324
- <p class="cost-info">Cost: ~$0.024/minute (standard), ~$0.036/minute (enhanced)</p>
325
- </div>
326
- </div>
327
-
328
- </ng-container>
329
- </mat-expansion-panel>
330
-
331
- <mat-expansion-panel class="prompt-panel">
332
- <mat-expansion-panel-header>
333
- <mat-panel-title>
334
- <mat-icon>psychology</mat-icon>
335
- Internal System Prompt
336
- </mat-panel-title>
337
- <mat-panel-description>
338
- Advanced configuration for LLM
339
- </mat-panel-description>
340
- </mat-expansion-panel-header>
341
-
342
- <mat-form-field appearance="outline" class="full-width">
343
- <mat-label>Internal Prompt</mat-label>
344
- <textarea
345
- matInput
346
- name="internalPrompt"
347
- formControlName="environment.internal_prompt"
348
- [disabled]="loading"
349
- rows="10"
350
- placeholder="Enter internal system prompt..."
351
- ></textarea>
352
- <mat-hint>This prompt will be prepended to all project prompts</mat-hint>
353
- </mat-form-field>
354
- </mat-expansion-panel>
355
-
356
- <div class="form-actions">
357
- <button
358
- mat-raised-button
359
- color="primary"
360
- type="submit"
361
- [disabled]="loading || !envForm.valid || saving"
362
- >
363
- @if (saving) {
364
- <mat-spinner diameter="20"></mat-spinner>
365
- Saving...
366
- } @else {
367
- <mat-icon>save</mat-icon>
368
- Save
369
- }
370
- </button>
371
-
372
- <button
373
- mat-raised-button
374
- type="button"
375
- (click)="reloadFromSpark()"
376
- [disabled]="loading || isGPTMode()"
377
- >
378
- <mat-icon>refresh</mat-icon>
379
- Reload from Spark
380
- </button>
381
- </div>
382
- </form>
383
- </mat-card-content>
384
- </mat-card>
385
- </div>
386
- `,
387
- styles: [`
388
- .environment-container {
389
- max-width: 800px;
390
- margin: 0 auto;
391
- }
392
-
393
- mat-card-header {
394
- margin-bottom: 24px;
395
-
396
- mat-card-title {
397
- display: flex;
398
- align-items: center;
399
- gap: 8px;
400
- font-size: 24px;
401
-
402
- mat-icon {
403
- font-size: 28px;
404
- width: 28px;
405
- height: 28px;
406
- }
407
- }
408
- }
409
-
410
- .info-card {
411
- margin-bottom: 20px;
412
- background-color: #e3f2fd;
413
-
414
- mat-card-content {
415
- display: flex;
416
- gap: 16px;
417
- align-items: flex-start;
418
-
419
- mat-icon {
420
- color: #1976d2;
421
- margin-top: 4px;
422
- }
423
-
424
- p {
425
- margin: 4px 0;
426
- font-size: 14px;
427
- }
428
- }
429
- }
430
-
431
- .full-width {
432
- width: 100%;
433
- margin-bottom: 20px;
434
- }
435
-
436
- .engine-row {
437
- display: flex;
438
- gap: 16px;
439
- align-items: flex-start;
440
- margin-bottom: 20px;
441
-
442
- .engine-field {
443
- flex: 1;
444
- }
445
-
446
- .api-key-field {
447
- flex: 1.5;
448
- }
449
- }
450
-
451
- .prompt-panel {
452
- margin-bottom: 20px;
453
-
454
- mat-expansion-panel-header {
455
- mat-panel-title {
456
- display: flex;
457
- align-items: center;
458
- gap: 8px;
459
-
460
- mat-icon {
461
- color: #666;
462
- }
463
- }
464
- }
465
- }
466
-
467
- .form-actions {
468
- display: flex;
469
- gap: 16px;
470
- margin-top: 24px;
471
- padding-top: 24px;
472
- border-top: 1px solid #e0e0e0;
473
-
474
- button {
475
-
476
- mat-spinner {
477
- margin-right: 8px;
478
- vertical-align: middle; // Spinner'ı hizalamak için ekleyin
479
- }
480
-
481
- mat-icon {
482
- vertical-align: middle; // Icon'u hizalamak için ekleyin
483
- margin-right: 4px; // Icon ile text arasına boşluk
484
- }
485
- }
486
- }
487
-
488
- .stt-settings-panel {
489
- margin-bottom: 20px;
490
-
491
- .stt-settings-grid {
492
- display: grid;
493
- grid-template-columns: 1fr 1fr;
494
- gap: 16px;
495
-
496
- .full-width {
497
- grid-column: 1 / -1;
498
- }
499
-
500
- .checkbox-group {
501
- grid-column: 1 / -1;
502
- display: flex;
503
- flex-direction: column;
504
- gap: 12px;
505
-
506
- mat-checkbox {
507
- display: flex;
508
- align-items: center;
509
-
510
- .info-icon {
511
- font-size: 18px;
512
- margin-left: 8px;
513
- color: #666;
514
- cursor: help;
515
- }
516
- }
517
- }
518
- }
519
-
520
- .stt-info-card {
521
- margin-top: 16px;
522
- padding: 16px;
523
- background-color: #e3f2fd;
524
- border-radius: 4px;
525
- display: flex;
526
- gap: 16px;
527
-
528
- mat-icon {
529
- color: #1976d2;
530
- flex-shrink: 0;
531
- }
532
-
533
- ol {
534
- margin: 8px 0;
535
- padding-left: 20px;
536
- }
537
-
538
- .cost-info {
539
- margin-top: 8px;
540
- font-size: 14px;
541
- color: #666;
542
- }
543
- }
544
- }
545
-
546
- ::ng-deep {
547
- .mat-mdc-form-field-icon-prefix {
548
- padding-right: 12px;
549
- }
550
-
551
- .mat-mdc-progress-spinner {
552
- --mdc-circular-progress-active-indicator-color: white;
553
- }
554
-
555
- .mat-expansion-panel-body {
556
- padding: 16px 0 !important;
557
- }
558
- }
559
- `]
560
- })
561
- export class EnvironmentComponent implements OnInit {
562
- private apiService = inject(ApiService);
563
- private snackBar = inject(MatSnackBar);
564
- private environmentService = inject(EnvironmentService);
565
- private fb = inject(FormBuilder);
566
-
567
- environmentForm!: FormGroup;
568
- loading = true;
569
- saving = false;
570
- sttSettingsExpanded = false;
571
-
572
- ngOnInit() {
573
- this.initializeForm();
574
- this.loadEnvironment();
575
- }
576
-
577
- initializeForm() {
578
- this.environmentForm = this.fb.group({
579
- work_mode: ['hfcloud'],
580
- cloud_token: [''],
581
- spark_endpoint: [''],
582
- internal_prompt: [''],
583
- tts_engine: ['no_tts'],
584
- tts_engine_api_key: [''],
585
- stt_engine: ['no_stt'],
586
- stt_engine_api_key: [''],
587
- stt_settings: this.fb.group({
588
- speech_timeout_ms: [2000],
589
- noise_reduction_level: [2],
590
- vad_sensitivity: [0.5],
591
- language: ['tr-TR'],
592
- model: ['latest_long'],
593
- use_enhanced: [true],
594
- enable_punctuation: [true],
595
- interim_results: [true]
596
- })
597
- });
598
-
599
- // STT engine değişikliklerini dinle
600
- this.environmentForm.get('stt_engine')?.valueChanges.subscribe(value => {
601
- this.onSTTEngineChange(value);
602
- });
603
- }
604
-
605
- loadEnvironment() {
606
- this.loading = true;
607
- this.apiService.getEnvironment().subscribe({
608
- next: (env) => {
609
- this.environmentForm.patchValue({
610
- work_mode: env.work_mode,
611
- cloud_token: env.cloud_token,
612
- spark_endpoint: env.spark_endpoint,
613
- internal_prompt: env.internal_prompt,
614
- tts_engine: env.tts_engine,
615
- tts_engine_api_key: env.tts_engine_api_key,
616
- stt_engine: env.stt_engine,
617
- stt_engine_api_key: env.stt_engine_api_key
618
- });
619
-
620
- if (env.stt_settings) {
621
- this.environmentForm.get('stt_settings')?.patchValue(env.stt_settings);
622
- }
623
-
624
- this.loading = false;
625
- },
626
- error: (err) => {
627
- this.snackBar.open('Failed to load environment configuration', 'Close', {
628
- duration: 5000,
629
- panelClass: 'error-snackbar'
630
- });
631
- this.loading = false;
632
- }
633
- });
634
- }
635
-
636
- onSTTEngineChange(value: string) {
637
- if (value === 'no_stt') {
638
- this.environmentForm.patchValue({ stt_engine_api_key: '' });
639
- this.sttSettingsExpanded = false;
640
- } else {
641
- this.sttSettingsExpanded = true;
642
- }
643
- }
644
-
645
- save() {
646
- if (this.environmentForm.invalid) {
647
- return;
648
- }
649
-
650
- this.saving = true;
651
- const formValue = this.environmentForm.value;
652
-
653
- this.apiService.updateEnvironment(formValue).subscribe({
654
- next: () => {
655
- this.environmentService.updateEnvironment(formValue);
656
- this.snackBar.open('Environment configuration saved successfully', 'Close', {
657
- duration: 3000
658
- });
659
- this.saving = false;
660
- },
661
- error: (err) => {
662
- this.snackBar.open(
663
- err.error?.detail || 'Failed to save configuration',
664
- 'Close',
665
- { duration: 5000, panelClass: 'error-snackbar' }
666
- );
667
- this.saving = false;
668
- }
669
- });
670
- }
671
-
672
- // Diğer metodlar
673
- isGPTMode(): boolean {
674
- const workMode = this.environmentForm.get('work_mode')?.value;
675
- return workMode === 'gpt4o' || workMode === 'gpt4o-mini';
676
- }
677
-
678
- formatVAD(value: number): string {
679
- return value.toFixed(1);
680
- }
681
-
682
- getSTTKeyPlaceholder(): string {
683
- const sttEngine = this.environmentForm.get('stt_engine')?.value;
684
- switch(sttEngine) {
685
- case 'google':
686
- return 'Google service account JSON content';
687
- case 'azure':
688
- return 'Azure subscription key';
689
- case 'amazon':
690
- return 'AWS access key';
691
- case 'gpt4o_realtime':
692
- return 'OpenAI API key';
693
- default:
694
- return 'Enter API key';
695
- }
696
- }
697
-
698
- onSTTCredentialsFileSelected(event: any) {
699
- const file = event.target.files[0];
700
- if (file && file.type === 'application/json') {
701
- const reader = new FileReader();
702
- reader.onload = (e: any) => {
703
- try {
704
- JSON.parse(e.target.result); // Validate JSON
705
- this.environmentForm.patchValue({ stt_engine_api_key: e.target.result });
706
- this.snackBar.open('Google credentials loaded successfully', 'Close', {
707
- duration: 3000
708
- });
709
- } catch (error) {
710
- this.snackBar.open('Invalid JSON file', 'Close', {
711
- duration: 3000,
712
- panelClass: 'error-snackbar'
713
- });
714
- }
715
- };
716
- reader.readAsText(file);
717
- }
718
- }
719
-
720
- get environment(): Environment {
721
- return {
722
- ...this.environmentForm.value,
723
- stt_settings: this.sttSettingsForm.value
724
- };
725
- }
726
-
727
- set environment(value: Environment) {
728
- // Form update için kullanılmıyor, sadece compat için
729
- }
730
-
731
- updateSTTSetting(key: string, value: any) {
732
- if (!this.environment.stt_settings) {
733
- this.environment.stt_settings = {
734
- speech_timeout_ms: 2000,
735
- noise_reduction_level: 2,
736
- vad_sensitivity: 0.5,
737
- language: 'tr-TR',
738
- model: 'latest_long',
739
- use_enhanced: true,
740
- enable_punctuation: true,
741
- interim_results: true
742
- };
743
- }
744
- (this.environment.stt_settings as any)[key] = value;
745
- }
746
-
747
- getTokenLabel(): string {
748
- switch(this.environment.work_mode) {
749
- case 'gpt4o':
750
- case 'gpt4o-mini':
751
- return 'OpenAI API Key';
752
- case 'hfcloud':
753
- case 'cloud':
754
- return 'Cloud Token';
755
- default:
756
- return 'Cloud Token';
757
- }
758
- }
759
-
760
- getTokenPlaceholder(): string {
761
- switch(this.environment.work_mode) {
762
- case 'gpt4o':
763
- case 'gpt4o-mini':
764
- return 'Enter OpenAI API key (sk-...)';
765
- case 'hfcloud':
766
- case 'cloud':
767
- return 'Enter cloud token';
768
- default:
769
- return 'Enter cloud token';
770
- }
771
- }
772
-
773
- isGPTMode(): boolean {
774
- return this.environment.work_mode === 'gpt4o' || this.environment.work_mode === 'gpt4o-mini';
775
- }
776
-
777
- onWorkModeChange() {
778
- if (this.environment.work_mode === 'on-premise') {
779
- this.environment.cloud_token = '';
780
- }
781
- }
782
-
783
- onTTSEngineChange() {
784
- if (this.environment.tts_engine === 'no_tts') {
785
- this.environment.tts_engine_api_key = '';
786
- }
787
- }
788
-
789
- onSTTEngineChange() {
790
- if (this.environment.stt_engine === 'no_stt') {
791
- this.environment.stt_engine_api_key = '';
792
- this.sttSettingsExpanded = false;
793
- } else {
794
- // STT settings'i hemen oluştur
795
- if (!this.environment.stt_settings) {
796
- this.environment.stt_settings = {
797
- speech_timeout_ms: 2000,
798
- noise_reduction_level: 2,
799
- vad_sensitivity: 0.5,
800
- language: 'tr-TR',
801
- model: 'latest_long',
802
- use_enhanced: true,
803
- enable_punctuation: true,
804
- interim_results: true
805
- };
806
- }
807
-
808
- setTimeout(() => {
809
- this.sttSettingsExpanded = true;
810
- }, 50);
811
- }
812
- }
813
-
814
- formatVAD(value: number): string {
815
- return value.toFixed(1);
816
- }
817
-
818
- getSTTKeyPlaceholder(): string {
819
- switch(this.environment.stt_engine) {
820
- case 'google':
821
- return 'Google service account JSON content';
822
- case 'azure':
823
- return 'Azure subscription key';
824
- case 'amazon':
825
- return 'AWS access key';
826
- case 'gpt4o_realtime':
827
- return 'OpenAI API key';
828
- default:
829
- return 'Enter API key';
830
- }
831
- }
832
-
833
- testConnection() {
834
- this.snackBar.open('Testing connection to Spark endpoint...', undefined, {
835
- duration: 2000
836
- });
837
-
838
- // TODO: Implement actual connection test
839
- setTimeout(() => {
840
- this.snackBar.open('Connection successful!', 'Close', {
841
- duration: 3000
842
- });
843
- }, 2000);
844
- }
845
-
846
- reloadFromSpark() {
847
- if (this.isGPTMode()) {
848
- return;
849
- }
850
-
851
- this.snackBar.open('Reloading configuration from Spark...', undefined, {
852
- duration: 2000
853
- });
854
-
855
- setTimeout(() => {
856
- this.loadEnvironment();
857
- this.snackBar.open('Configuration reloaded', 'Close', {
858
- duration: 3000
859
- });
860
- }, 1000);
861
- }
862
  }
 
1
+ import { Component, inject, OnInit } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { MatCardModule } from '@angular/material/card';
5
+ import { MatFormFieldModule } from '@angular/material/form-field';
6
+ import { MatInputModule } from '@angular/material/input';
7
+ import { MatSelectModule } from '@angular/material/select';
8
+ import { MatButtonModule } from '@angular/material/button';
9
+ import { MatIconModule } from '@angular/material/icon';
10
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
11
+ import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
12
+ import { MatExpansionModule } from '@angular/material/expansion';
13
+ import { ApiService, Environment } from '../../services/api.service';
14
+ import { EnvironmentService } from '../../services/environment.service';
15
+
16
+ @Component({
17
+ selector: 'app-environment',
18
+ standalone: true,
19
+ imports: [
20
+ CommonModule,
21
+ FormsModule,
22
+ MatCardModule,
23
+ MatFormFieldModule,
24
+ MatInputModule,
25
+ MatSelectModule,
26
+ MatButtonModule,
27
+ MatIconModule,
28
+ MatProgressSpinnerModule,
29
+ MatSnackBarModule,
30
+ MatExpansionModule
31
+ ],
32
+ template: `
33
+ <div class="environment-container">
34
+ <mat-card>
35
+ <mat-card-header>
36
+ <mat-card-title>
37
+ <mat-icon>settings</mat-icon>
38
+ Environment Configuration
39
+ </mat-card-title>
40
+ </mat-card-header>
41
+
42
+ <mat-card-content>
43
+ @if (isGPTMode()) {
44
+ <mat-card class="info-card">
45
+ <mat-card-content>
46
+ <mat-icon>info</mat-icon>
47
+ <div>
48
+ <strong>{{ environment.work_mode === 'gpt4o' ? 'GPT-4o' : 'GPT-4o Mini' }} Mode</strong>
49
+ <p>This mode uses OpenAI's API which has usage-based pricing.</p>
50
+ <p>Approximate cost: {{ environment.work_mode === 'gpt4o' ? '$0.01-0.03' : '$0.001-0.003' }} per conversation turn.</p>
51
+ </div>
52
+ </mat-card-content>
53
+ </mat-card>
54
+ }
55
+
56
+ <form (ngSubmit)="save()" #envForm="ngForm">
57
+ <mat-form-field appearance="outline" class="full-width">
58
+ <mat-label>Work Mode</mat-label>
59
+ <mat-select
60
+ name="workMode"
61
+ [(ngModel)]="environment.work_mode"
62
+ (selectionChange)="onWorkModeChange()"
63
+ required
64
+ [disabled]="loading"
65
+ >
66
+ <mat-option value="hfcloud">HF Cloud</mat-option>
67
+ <mat-option value="cloud">Cloud</mat-option>
68
+ <mat-option value="on-premise">On-Premise</mat-option>
69
+ <mat-option value="gpt4o">GPT-4o</mat-option>
70
+ <mat-option value="gpt4o-mini">GPT-4o Mini</mat-option>
71
+ </mat-select>
72
+ <mat-icon matPrefix>cloud</mat-icon>
73
+ </mat-form-field>
74
+
75
+ <mat-form-field appearance="outline" class="full-width">
76
+ <mat-label>{{ getTokenLabel() }}</mat-label>
77
+ <input
78
+ matInput
79
+ type="password"
80
+ name="cloudToken"
81
+ [(ngModel)]="environment.cloud_token"
82
+ [disabled]="loading || environment.work_mode === 'on-premise'"
83
+ [placeholder]="getTokenPlaceholder()"
84
+ >
85
+ <mat-icon matPrefix>vpn_key</mat-icon>
86
+ <mat-hint>{{ isGPTMode() ? 'Required for OpenAI API access' : 'Required for HF Cloud and Cloud modes' }}</mat-hint>
87
+ </mat-form-field>
88
+
89
+ <mat-form-field appearance="outline" class="full-width">
90
+ <mat-label>Spark Endpoint</mat-label>
91
+ <input
92
+ matInput
93
+ type="url"
94
+ name="sparkEndpoint"
95
+ [(ngModel)]="environment.spark_endpoint"
96
+ [required]="!isGPTMode()"
97
+ [disabled]="loading || isGPTMode()"
98
+ placeholder="https://spark-service.example.com"
99
+ >
100
+ <mat-icon matPrefix>link</mat-icon>
101
+ <button
102
+ mat-icon-button
103
+ matSuffix
104
+ type="button"
105
+ (click)="testConnection()"
106
+ [disabled]="loading || !environment.spark_endpoint || isGPTMode()"
107
+ matTooltip="Test Connection"
108
+ >
109
+ <mat-icon>wifi_tethering</mat-icon>
110
+ </button>
111
+ @if (isGPTMode()) {
112
+ <mat-hint>Not required for GPT mode</mat-hint>
113
+ }
114
+ </mat-form-field>
115
+
116
+ <!-- TTS Configuration -->
117
+ <div class="engine-row">
118
+ <mat-form-field appearance="outline" class="engine-field">
119
+ <mat-label>TTS Engine</mat-label>
120
+ <mat-select
121
+ name="ttsEngine"
122
+ [(ngModel)]="environment.tts_engine"
123
+ (selectionChange)="onTTSEngineChange()"
124
+ [disabled]="loading"
125
+ >
126
+ <mat-option value="no_tts">No TTS</mat-option>
127
+ <mat-option value="elevenlabs">ElevenLabs</mat-option>
128
+ <mat-option value="blaze">Blaze (Coming Soon)</mat-option>
129
+ </mat-select>
130
+ <mat-icon matPrefix>record_voice_over</mat-icon>
131
+ </mat-form-field>
132
+
133
+ <mat-form-field appearance="outline" class="api-key-field">
134
+ <mat-label>TTS API Key</mat-label>
135
+ <input
136
+ matInput
137
+ type="password"
138
+ name="ttsApiKey"
139
+ [(ngModel)]="environment.tts_engine_api_key"
140
+ [disabled]="loading || environment.tts_engine === 'no_tts'"
141
+ [required]="environment.tts_engine !== 'no_tts'"
142
+ placeholder="Enter TTS API key"
143
+ >
144
+ <mat-icon matPrefix>key</mat-icon>
145
+ </mat-form-field>
146
+ </div>
147
+
148
+ <!-- STT Configuration -->
149
+ <div class="engine-row">
150
+ <mat-form-field appearance="outline" class="engine-field">
151
+ <mat-label>STT Engine</mat-label>
152
+ <mat-select
153
+ name="sttEngine"
154
+ [(ngModel)]="environment.stt_engine"
155
+ (selectionChange)="onSTTEngineChange()"
156
+ [disabled]="loading"
157
+ >
158
+ <mat-option value="no_stt">No STT</mat-option>
159
+ <mat-option value="elevenlabs">ElevenLabs</mat-option>
160
+ <mat-option value="flicker">Flicker (Coming Soon)</mat-option>
161
+ </mat-select>
162
+ <mat-icon matPrefix>mic</mat-icon>
163
+ </mat-form-field>
164
+
165
+ <mat-form-field appearance="outline" class="api-key-field">
166
+ <mat-label>STT API Key</mat-label>
167
+ <input
168
+ matInput
169
+ type="password"
170
+ name="sttApiKey"
171
+ [(ngModel)]="environment.stt_engine_api_key"
172
+ [disabled]="loading || environment.stt_engine === 'no_stt'"
173
+ [required]="environment.stt_engine !== 'no_stt'"
174
+ placeholder="Enter STT API key"
175
+ >
176
+ <mat-icon matPrefix>key</mat-icon>
177
+ </mat-form-field>
178
+ </div>
179
+
180
+ <mat-expansion-panel class="prompt-panel">
181
+ <mat-expansion-panel-header>
182
+ <mat-panel-title>
183
+ <mat-icon>psychology</mat-icon>
184
+ Internal System Prompt
185
+ </mat-panel-title>
186
+ <mat-panel-description>
187
+ Advanced configuration for LLM
188
+ </mat-panel-description>
189
+ </mat-expansion-panel-header>
190
+
191
+ <mat-form-field appearance="outline" class="full-width">
192
+ <mat-label>Internal Prompt</mat-label>
193
+ <textarea
194
+ matInput
195
+ name="internalPrompt"
196
+ [(ngModel)]="environment.internal_prompt"
197
+ [disabled]="loading"
198
+ rows="10"
199
+ placeholder="Enter internal system prompt..."
200
+ ></textarea>
201
+ <mat-hint>This prompt will be prepended to all project prompts</mat-hint>
202
+ </mat-form-field>
203
+ </mat-expansion-panel>
204
+
205
+ <div class="form-actions">
206
+ <button
207
+ mat-raised-button
208
+ color="primary"
209
+ type="submit"
210
+ [disabled]="loading || !envForm.valid || saving"
211
+ >
212
+ @if (saving) {
213
+ <mat-spinner diameter="20"></mat-spinner>
214
+ Saving...
215
+ } @else {
216
+ <mat-icon>save</mat-icon>
217
+ Save
218
+ }
219
+ </button>
220
+
221
+ <button
222
+ mat-raised-button
223
+ type="button"
224
+ (click)="reloadFromSpark()"
225
+ [disabled]="loading || isGPTMode()"
226
+ >
227
+ <mat-icon>refresh</mat-icon>
228
+ Reload from Spark
229
+ </button>
230
+ </div>
231
+ </form>
232
+ </mat-card-content>
233
+ </mat-card>
234
+ </div>
235
+ `,
236
+ styles: [`
237
+ .environment-container {
238
+ max-width: 800px;
239
+ margin: 0 auto;
240
+ }
241
+
242
+ mat-card-header {
243
+ margin-bottom: 24px;
244
+
245
+ mat-card-title {
246
+ display: flex;
247
+ align-items: center;
248
+ gap: 8px;
249
+ font-size: 24px;
250
+
251
+ mat-icon {
252
+ font-size: 28px;
253
+ width: 28px;
254
+ height: 28px;
255
+ }
256
+ }
257
+ }
258
+
259
+ .info-card {
260
+ margin-bottom: 20px;
261
+ background-color: #e3f2fd;
262
+
263
+ mat-card-content {
264
+ display: flex;
265
+ gap: 16px;
266
+ align-items: flex-start;
267
+
268
+ mat-icon {
269
+ color: #1976d2;
270
+ margin-top: 4px;
271
+ }
272
+
273
+ p {
274
+ margin: 4px 0;
275
+ font-size: 14px;
276
+ }
277
+ }
278
+ }
279
+
280
+ .full-width {
281
+ width: 100%;
282
+ margin-bottom: 20px;
283
+ }
284
+
285
+ .engine-row {
286
+ display: flex;
287
+ gap: 16px;
288
+ align-items: flex-start;
289
+ margin-bottom: 20px;
290
+
291
+ .engine-field {
292
+ flex: 1;
293
+ }
294
+
295
+ .api-key-field {
296
+ flex: 1.5;
297
+ }
298
+ }
299
+
300
+ .prompt-panel {
301
+ margin-bottom: 20px;
302
+
303
+ mat-expansion-panel-header {
304
+ mat-panel-title {
305
+ display: flex;
306
+ align-items: center;
307
+ gap: 8px;
308
+
309
+ mat-icon {
310
+ color: #666;
311
+ }
312
+ }
313
+ }
314
+ }
315
+
316
+ .form-actions {
317
+ display: flex;
318
+ gap: 16px;
319
+ margin-top: 24px;
320
+ padding-top: 24px;
321
+ border-top: 1px solid #e0e0e0;
322
+
323
+ button {
324
+
325
+ mat-spinner {
326
+ margin-right: 8px;
327
+ vertical-align: middle; // Spinner'ı hizalamak için ekleyin
328
+ }
329
+
330
+ mat-icon {
331
+ vertical-align: middle; // Icon'u hizalamak için ekleyin
332
+ margin-right: 4px; // Icon ile text arasına boşluk
333
+ }
334
+ }
335
+ }
336
+
337
+ ::ng-deep {
338
+ .mat-mdc-form-field-icon-prefix {
339
+ padding-right: 12px;
340
+ }
341
+
342
+ .mat-mdc-progress-spinner {
343
+ --mdc-circular-progress-active-indicator-color: white;
344
+ }
345
+
346
+ .mat-expansion-panel-body {
347
+ padding: 16px 0 !important;
348
+ }
349
+ }
350
+ `]
351
+ })
352
+ export class EnvironmentComponent implements OnInit {
353
+ private apiService = inject(ApiService);
354
+ private snackBar = inject(MatSnackBar);
355
+ private environmentService = inject(EnvironmentService);
356
+
357
+ environment: Environment = {
358
+ work_mode: 'hfcloud',
359
+ cloud_token: '',
360
+ spark_endpoint: '',
361
+ internal_prompt: '',
362
+ tts_engine: 'no_tts',
363
+ tts_engine_api_key: '',
364
+ stt_engine: 'no_stt',
365
+ stt_engine_api_key: ''
366
+ };
367
+
368
+ loading = true;
369
+ saving = false;
370
+
371
+ ngOnInit() {
372
+ this.loadEnvironment();
373
+ }
374
+
375
+ loadEnvironment() {
376
+ this.loading = true;
377
+ this.apiService.getEnvironment().subscribe({
378
+ next: (env) => {
379
+ this.environment = env;
380
+ this.loading = false;
381
+ },
382
+ error: (err) => {
383
+ this.snackBar.open('Failed to load environment configuration', 'Close', {
384
+ duration: 5000,
385
+ panelClass: 'error-snackbar'
386
+ });
387
+ this.loading = false;
388
+ }
389
+ });
390
+ }
391
+
392
+ getTokenLabel(): string {
393
+ switch(this.environment.work_mode) {
394
+ case 'gpt4o':
395
+ case 'gpt4o-mini':
396
+ return 'OpenAI API Key';
397
+ case 'hfcloud':
398
+ case 'cloud':
399
+ return 'Cloud Token';
400
+ default:
401
+ return 'Cloud Token';
402
+ }
403
+ }
404
+
405
+ getTokenPlaceholder(): string {
406
+ switch(this.environment.work_mode) {
407
+ case 'gpt4o':
408
+ case 'gpt4o-mini':
409
+ return 'Enter OpenAI API key (sk-...)';
410
+ case 'hfcloud':
411
+ case 'cloud':
412
+ return 'Enter cloud token';
413
+ default:
414
+ return 'Enter cloud token';
415
+ }
416
+ }
417
+
418
+ isGPTMode(): boolean {
419
+ return this.environment.work_mode === 'gpt4o' || this.environment.work_mode === 'gpt4o-mini';
420
+ }
421
+
422
+ onWorkModeChange() {
423
+ if (this.environment.work_mode === 'on-premise') {
424
+ this.environment.cloud_token = '';
425
+ }
426
+ }
427
+
428
+ onTTSEngineChange() {
429
+ if (this.environment.tts_engine === 'no_tts') {
430
+ this.environment.tts_engine_api_key = '';
431
+ }
432
+ }
433
+
434
+ onSTTEngineChange() {
435
+ if (this.environment.stt_engine === 'no_stt') {
436
+ this.environment.stt_engine_api_key = '';
437
+ }
438
+ }
439
+
440
+ save() {
441
+ this.saving = true;
442
+
443
+ this.apiService.updateEnvironment(this.environment).subscribe({
444
+ next: () => {
445
+ // Environment service'i güncelle
446
+ this.environmentService.updateEnvironment(this.environment);
447
+
448
+ this.snackBar.open('Environment configuration saved successfully', 'Close', {
449
+ duration: 3000
450
+ });
451
+ this.saving = false;
452
+ },
453
+ error: (err) => {
454
+ this.snackBar.open(
455
+ err.error?.detail || 'Failed to save configuration',
456
+ 'Close',
457
+ { duration: 5000, panelClass: 'error-snackbar' }
458
+ );
459
+ this.saving = false;
460
+ }
461
+ });
462
+ }
463
+
464
+ testConnection() {
465
+ this.snackBar.open('Testing connection to Spark endpoint...', undefined, {
466
+ duration: 2000
467
+ });
468
+
469
+ // TODO: Implement actual connection test
470
+ setTimeout(() => {
471
+ this.snackBar.open('Connection successful!', 'Close', {
472
+ duration: 3000
473
+ });
474
+ }, 2000);
475
+ }
476
+
477
+ reloadFromSpark() {
478
+ if (this.isGPTMode()) {
479
+ return;
480
+ }
481
+
482
+ this.snackBar.open('Reloading configuration from Spark...', undefined, {
483
+ duration: 2000
484
+ });
485
+
486
+ setTimeout(() => {
487
+ this.loadEnvironment();
488
+ this.snackBar.open('Configuration reloaded', 'Close', {
489
+ duration: 3000
490
+ });
491
+ }, 1000);
492
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  }