import { Component, inject, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatSliderModule } from '@angular/material/slider';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { ApiService, Environment, STTSettings } from '../../services/api.service';
import { EnvironmentService } from '../../services/environment.service';
@Component({
selector: 'app-environment',
standalone: true,
imports: [
CommonModule,
FormsModule,
MatCardModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatButtonModule,
MatIconModule,
MatProgressSpinnerModule,
MatSnackBarModule,
MatExpansionModule,
MatSliderModule,
MatTooltipModule,
MatCheckboxModule
],
template: `
settings
Environment Configuration
@if (isGPTMode()) {
info
{{ environment.work_mode === 'gpt4o' ? 'GPT-4o' : 'GPT-4o Mini' }} Mode
This mode uses OpenAI's API which has usage-based pricing.
Approximate cost: {{ environment.work_mode === 'gpt4o' ? '$0.01-0.03' : '$0.001-0.003' }} per conversation turn.
}
`,
styles: [`
.environment-container {
max-width: 800px;
margin: 0 auto;
}
mat-card-header {
margin-bottom: 24px;
mat-card-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 24px;
mat-icon {
font-size: 28px;
width: 28px;
height: 28px;
}
}
}
.info-card {
margin-bottom: 20px;
background-color: #e3f2fd;
mat-card-content {
display: flex;
gap: 16px;
align-items: flex-start;
mat-icon {
color: #1976d2;
margin-top: 4px;
}
p {
margin: 4px 0;
font-size: 14px;
}
}
}
.full-width {
width: 100%;
margin-bottom: 20px;
}
.engine-row {
display: flex;
gap: 16px;
align-items: flex-start;
margin-bottom: 20px;
.engine-field {
flex: 1;
}
.api-key-field {
flex: 1.5;
}
}
.prompt-panel {
margin-bottom: 20px;
mat-expansion-panel-header {
mat-panel-title {
display: flex;
align-items: center;
gap: 8px;
mat-icon {
color: #666;
}
}
}
}
.form-actions {
display: flex;
gap: 16px;
margin-top: 24px;
padding-top: 24px;
border-top: 1px solid #e0e0e0;
button {
mat-spinner {
margin-right: 8px;
vertical-align: middle; // Spinner'ı hizalamak için ekleyin
}
mat-icon {
vertical-align: middle; // Icon'u hizalamak için ekleyin
margin-right: 4px; // Icon ile text arasına boşluk
}
}
}
.stt-settings-panel {
margin-bottom: 20px;
.stt-settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
.full-width {
grid-column: 1 / -1;
}
.checkbox-group {
grid-column: 1 / -1;
display: flex;
flex-direction: column;
gap: 12px;
mat-checkbox {
display: flex;
align-items: center;
.info-icon {
font-size: 18px;
margin-left: 8px;
color: #666;
cursor: help;
}
}
}
}
.stt-info-card {
margin-top: 16px;
padding: 16px;
background-color: #e3f2fd;
border-radius: 4px;
display: flex;
gap: 16px;
mat-icon {
color: #1976d2;
flex-shrink: 0;
}
ol {
margin: 8px 0;
padding-left: 20px;
}
.cost-info {
margin-top: 8px;
font-size: 14px;
color: #666;
}
}
}
::ng-deep {
.mat-mdc-form-field-icon-prefix {
padding-right: 12px;
}
.mat-mdc-progress-spinner {
--mdc-circular-progress-active-indicator-color: white;
}
.mat-expansion-panel-body {
padding: 16px 0 !important;
}
}
`]
})
export class EnvironmentComponent implements OnInit {
private apiService = inject(ApiService);
private snackBar = inject(MatSnackBar);
private environmentService = inject(EnvironmentService);
environment: Environment = {
work_mode: 'hfcloud',
cloud_token: '',
spark_endpoint: '',
internal_prompt: '',
tts_engine: 'no_tts',
tts_engine_api_key: '',
stt_engine: 'no_stt',
stt_engine_api_key: '',
stt_settings: {
speech_timeout_ms: 2000,
noise_reduction_level: 2,
vad_sensitivity: 0.5,
language: 'tr-TR',
model: 'latest_long',
use_enhanced: true,
enable_punctuation: true,
interim_results: true
}
};
sttSettingsExpanded = false;
loading = true;
saving = false;
ngOnInit() {
console.log('EnvironmentComponent ngOnInit started');
try {
this.loadEnvironment();
} catch (error) {
console.error('Error in ngOnInit:', error);
console.error('Stack trace:', error.stack);
}
}
ngAfterViewInit() {
console.log('EnvironmentComponent ngAfterViewInit');
console.log('Current environment:', this.environment);
console.log('STT settings:', this.environment.stt_settings);
}
loadEnvironment() {
console.log('loadEnvironment called');
this.loading = true;
this.apiService.getEnvironment().subscribe({
next: (env) => {
console.log('Environment loaded from API:', env);
this.environment = env;
// Debug check
console.log('Checking stt_settings:', this.environment.stt_settings);
if (!this.environment.stt_settings) {
console.log('No stt_settings found, creating defaults...');
this.environment.stt_settings = {
speech_timeout_ms: 2000,
noise_reduction_level: 2,
vad_sensitivity: 0.5,
language: 'tr-TR',
model: 'latest_long',
use_enhanced: true,
enable_punctuation: true,
interim_results: true
};
}
console.log('Final environment:', this.environment);
this.loading = false;
},
error: (err) => {
console.error('Error loading environment:', err);
this.snackBar.open('Failed to load environment configuration', 'Close', {
duration: 5000,
panelClass: 'error-snackbar'
});
this.loading = false;
}
});
}
onSTTCredentialsFileSelected(event: any) {
const file = event.target.files[0];
if (file && file.type === 'application/json') {
const reader = new FileReader();
reader.onload = (e: any) => {
try {
// Validate it's a valid JSON
const jsonContent = JSON.parse(e.target.result);
// Store the entire JSON content as the API key
this.environment.stt_engine_api_key = e.target.result;
this.snackBar.open('Google credentials loaded successfully', 'Close', {
duration: 3000
});
} catch (error) {
this.snackBar.open('Invalid JSON file', 'Close', {
duration: 3000,
panelClass: 'error-snackbar'
});
}
};
reader.readAsText(file);
}
}
updateSTTSetting(key: string, value: any) {
if (!this.environment.stt_settings) {
this.environment.stt_settings = {
speech_timeout_ms: 2000,
noise_reduction_level: 2,
vad_sensitivity: 0.5,
language: 'tr-TR',
model: 'latest_long',
use_enhanced: true,
enable_punctuation: true,
interim_results: true
};
}
(this.environment.stt_settings as any)[key] = value;
}
getTokenLabel(): string {
switch(this.environment.work_mode) {
case 'gpt4o':
case 'gpt4o-mini':
return 'OpenAI API Key';
case 'hfcloud':
case 'cloud':
return 'Cloud Token';
default:
return 'Cloud Token';
}
}
getTokenPlaceholder(): string {
switch(this.environment.work_mode) {
case 'gpt4o':
case 'gpt4o-mini':
return 'Enter OpenAI API key (sk-...)';
case 'hfcloud':
case 'cloud':
return 'Enter cloud token';
default:
return 'Enter cloud token';
}
}
isGPTMode(): boolean {
return this.environment.work_mode === 'gpt4o' || this.environment.work_mode === 'gpt4o-mini';
}
onWorkModeChange() {
if (this.environment.work_mode === 'on-premise') {
this.environment.cloud_token = '';
}
}
onSTTEngineChange() {
console.log('onSTTEngineChange called with:', this.environment.stt_engine);
console.log('Current stt_settings:', this.environment.stt_settings);
try {
if (this.environment.stt_engine === 'no_stt') {
this.environment.stt_engine_api_key = '';
this.sttSettingsExpanded = false;
} else {
// Debug log
console.log('Setting up STT settings...');
if (!this.environment.stt_settings) {
console.log('Creating new stt_settings...');
this.environment.stt_settings = {
speech_timeout_ms: 2000,
noise_reduction_level: 2,
vad_sensitivity: 0.5,
language: 'tr-TR',
model: 'latest_long',
use_enhanced: true,
enable_punctuation: true,
interim_results: true
};
}
this.sttSettingsExpanded = true;
console.log('STT settings after setup:', this.environment.stt_settings);
}
} catch (error) {
console.error('Error in onSTTEngineChange:', error);
console.error('Stack trace:', error.stack);
}
}
formatVAD(value: number): string {
return value.toFixed(1);
}
getSTTKeyPlaceholder(): string {
switch(this.environment.stt_engine) {
case 'google':
return 'Google service account JSON content';
case 'azure':
return 'Azure subscription key';
case 'amazon':
return 'AWS access key';
case 'gpt4o_realtime':
return 'OpenAI API key';
default:
return 'Enter API key';
}
}
onSTTEngineChange() {
if (this.environment.stt_engine === 'no_stt') {
this.environment.stt_engine_api_key = '';
this.sttSettingsExpanded = false;
} else {
// Ensure stt_settings exists when enabling STT
if (!this.environment.stt_settings) {
this.environment.stt_settings = {
speech_timeout_ms: 2000,
noise_reduction_level: 2,
vad_sensitivity: 0.5,
language: 'tr-TR',
model: 'latest_long',
use_enhanced: true,
enable_punctuation: true,
interim_results: true
};
}
this.sttSettingsExpanded = true;
}
}
save() {
this.saving = true;
this.apiService.updateEnvironment(this.environment).subscribe({
next: () => {
// Environment service'i güncelle
this.environmentService.updateEnvironment(this.environment);
this.snackBar.open('Environment configuration saved successfully', 'Close', {
duration: 3000
});
this.saving = false;
},
error: (err) => {
this.snackBar.open(
err.error?.detail || 'Failed to save configuration',
'Close',
{ duration: 5000, panelClass: 'error-snackbar' }
);
this.saving = false;
}
});
}
testConnection() {
this.snackBar.open('Testing connection to Spark endpoint...', undefined, {
duration: 2000
});
// TODO: Implement actual connection test
setTimeout(() => {
this.snackBar.open('Connection successful!', 'Close', {
duration: 3000
});
}, 2000);
}
reloadFromSpark() {
if (this.isGPTMode()) {
return;
}
this.snackBar.open('Reloading configuration from Spark...', undefined, {
duration: 2000
});
setTimeout(() => {
this.loadEnvironment();
this.snackBar.open('Configuration reloaded', 'Close', {
duration: 3000
});
}, 1000);
}
}