Spaces:
Building
Building
import { Injectable } from '@angular/core'; | |
import { HttpClient, HttpHeaders } from '@angular/common/http'; | |
import { Observable, throwError } from 'rxjs'; | |
import { catchError, tap } from 'rxjs/operators'; | |
import { Router } from '@angular/router'; | |
import { AuthService } from './auth.service'; | |
// Interfaces | |
export interface API { | |
name: string; | |
url: string; | |
method: string; | |
headers?: any; | |
body_template?: any; | |
timeout_seconds: number; | |
retry?: { | |
retry_count: number; | |
backoff_seconds: number; | |
strategy: string; | |
}; | |
auth?: { | |
enabled: boolean; | |
token_endpoint?: string; | |
response_token_path?: string; | |
token_request_body?: any; | |
token_refresh_endpoint?: string; | |
token_refresh_body?: any; | |
}; | |
response_prompt?: string; | |
response_mappings?: ResponseMapping[]; // Yeni alan | |
deleted?: boolean; | |
last_update_date?: string; | |
last_update_user?: string; | |
} | |
export interface LocalizedCaption { | |
locale_code: string; | |
caption: string; | |
} | |
export interface LocalizedExample { | |
locale_code: string; | |
example: string; | |
} | |
export interface ResponseMapping { | |
variable_name: string; | |
type: 'str' | 'int' | 'float' | 'bool' | 'date'; | |
json_path: string; | |
caption: LocalizedCaption[]; | |
} | |
export interface IntentParameter { | |
name: string; | |
caption: LocalizedCaption[]; | |
type: 'str' | 'int' | 'float' | 'bool' | 'date'; | |
required: boolean; | |
variable_name: string; | |
extraction_prompt?: string; | |
validation_regex?: string; | |
invalid_prompt?: string; | |
type_error_prompt?: string; | |
} | |
export interface Intent { | |
name: string; | |
caption: string; | |
detection_prompt: string; | |
examples: LocalizedExample[]; | |
parameters: IntentParameter[]; | |
action: string; | |
fallback_timeout_prompt?: string; | |
fallback_error_prompt?: string; | |
} | |
export interface GenerationConfig { | |
max_new_tokens: number; | |
temperature: number; | |
top_p: number; | |
top_k?: number; | |
repetition_penalty?: number; | |
do_sample?: boolean; | |
num_beams?: number; | |
length_penalty?: number; | |
early_stopping?: boolean; | |
} | |
export interface LLMConfig { | |
repo_id: string; | |
generation_config: GenerationConfig; // any yerine GenerationConfig | |
use_fine_tune: boolean; | |
fine_tune_zip: string; | |
} | |
export interface Version { | |
no: number; | |
caption?: string; | |
description?: string; | |
published: boolean; | |
general_prompt?: string; | |
llm: LLMConfig; // inline yerine LLMConfig | |
intents: Intent[]; | |
parameters: any[]; | |
last_update_date?: string; | |
} | |
export interface Project { | |
id: number; | |
name: string; | |
caption: string; | |
enabled: boolean; | |
icon?: string; | |
description?: string; | |
default_locale?: string; | |
supported_locales?: string[]; | |
timezone?: string; | |
region?: string; | |
versions: Version[]; | |
last_update_date?: string; | |
deleted?: boolean; | |
created_date?: string; | |
created_by?: string; | |
last_update_user?: string; | |
} | |
export interface ParameterCollectionConfig { | |
max_params_per_question: number; | |
smart_grouping: boolean; | |
retry_unanswered: boolean; | |
collection_prompt: string; | |
} | |
export interface ProviderConfig { | |
type: 'llm' | 'tts' | 'stt'; | |
name: string; | |
display_name: string; | |
requires_endpoint: boolean; | |
requires_api_key: boolean; | |
requires_repo_info?: boolean; | |
description: string; | |
} | |
export interface ProviderSettings { | |
name: string; | |
api_key?: string; | |
endpoint?: string | null; | |
settings?: any; | |
} | |
export interface Environment { | |
llm_provider: ProviderSettings; | |
tts_provider: ProviderSettings; | |
stt_provider: ProviderSettings; | |
providers: ProviderConfig[]; | |
parameter_collection_config?: ParameterCollectionConfig; | |
} | |
export interface STTSettings { | |
speech_timeout_ms: number; | |
noise_reduction_level: number; | |
vad_sensitivity: number; | |
language: string; | |
model: string; | |
use_enhanced: boolean; | |
enable_punctuation: boolean; | |
interim_results: boolean; | |
} | |
export interface TTSSettings { | |
use_ssml: boolean; | |
} | |
({ | |
providedIn: 'root' | |
}) | |
export class ApiService { | |
private apiUrl = '/api'; | |
private adminUrl = `${this.apiUrl}/admin`; | |
constructor( | |
private http: HttpClient, | |
private router: Router, | |
private authService: AuthService | |
) {} | |
// ===================== Utils ===================== | |
private normalizeTimestamp(timestamp: string | null | undefined): string { | |
if (!timestamp) return ''; | |
// Remove milliseconds for comparison | |
if (timestamp.includes('.') && timestamp.endsWith('Z')) { | |
return timestamp.split('.')[0] + 'Z'; | |
} | |
return timestamp; | |
} | |
// ===================== Auth ===================== | |
login(username: string, password: string): Observable<any> { | |
return this.http.post(`${this.adminUrl}/login`, { username, password }).pipe( | |
tap((response: any) => { | |
this.authService.setToken(response.token); | |
this.authService.setUsername(response.username); | |
}) | |
); | |
} | |
logout(): void { | |
this.authService.logout(); | |
} | |
private getAuthHeaders(): HttpHeaders { | |
try { | |
const token = this.authService.getToken(); | |
if (!token) { | |
console.warn('No auth token available'); | |
// Token yoksa boş header dön veya login'e yönlendir | |
return new HttpHeaders({ | |
'Content-Type': 'application/json' | |
}); | |
} | |
return new HttpHeaders({ | |
'Authorization': `Bearer ${token}`, | |
'Content-Type': 'application/json' | |
}); | |
} catch (error) { | |
console.error('Error getting auth headers:', error); | |
return new HttpHeaders({ | |
'Content-Type': 'application/json' | |
}); | |
} | |
} | |
// ===================== User ===================== | |
changePassword(currentPassword: string, newPassword: string): Observable<any> { | |
return this.http.post( | |
`${this.adminUrl}/change-password`, | |
{ current_password: currentPassword, new_password: newPassword }, | |
{ headers: this.getAuthHeaders() } | |
).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Environment ===================== | |
getEnvironment(): Observable<any> { | |
return this.http.get(`${this.adminUrl}/environment`, { | |
headers: this.getAuthHeaders() | |
}); | |
} | |
updateEnvironment(data: Environment): Observable<any> { | |
return this.http.put(`${this.adminUrl}/environment`, data, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Projects ===================== | |
getProjects(includeDeleted = false): Observable<Project[]> { | |
return this.http.get<Project[]>(`${this.adminUrl}/projects`, { | |
headers: this.getAuthHeaders(), | |
params: { include_deleted: includeDeleted.toString() } | |
}).pipe( | |
catchError(error => { | |
// Race condition check | |
if (error.status === 409) { | |
console.warn('Race condition detected in getProjects:', error.error); | |
// Component'ler bu hatayı handle edecek | |
} | |
return throwError(() => error); | |
}) | |
); | |
} | |
getProject(id: number): Observable<Project> { | |
return this.http.get<Project>(`${this.adminUrl}/projects/${id}`); | |
} | |
createProject(data: any): Observable<any> { | |
console.log('createProject called with data:', data); | |
let headers; | |
try { | |
headers = this.getAuthHeaders(); | |
console.log('Headers obtained successfully'); | |
} catch (error) { | |
console.error('Error getting headers:', error); | |
return throwError(() => ({ message: 'Authentication error' })); | |
} | |
console.log('Making POST request to:', `${this.adminUrl}/projects`); | |
return this.http.post(`${this.adminUrl}/projects`, data, { headers }).pipe( | |
tap(response => { | |
console.log('Project creation successful:', response); | |
}), | |
catchError(error => { | |
console.error('Project creation failed:', error); | |
return this.handleError(error); | |
}) | |
); | |
} | |
updateProject(id: number, data: any): Observable<any> { | |
// Normalize the timestamp before sending | |
if (data.last_update_date) { | |
data.last_update_date = this.normalizeTimestamp(data.last_update_date); | |
} | |
return this.http.put(`${this.adminUrl}/projects/${id}`, data, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(error => { | |
// Race condition özel handling | |
if (error.status === 409) { | |
const details = error.error?.details || {}; | |
return throwError(() => ({ | |
...error, | |
raceCondition: true, | |
lastUpdateUser: details.last_update_user, | |
lastUpdateDate: details.last_update_date | |
})); | |
} | |
return throwError(() => error); | |
}) | |
); | |
} | |
deleteProject(id: number): Observable<any> { | |
return this.http.delete(`${this.adminUrl}/projects/${id}`, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
toggleProject(id: number): Observable<any> { | |
return this.http.patch(`${this.adminUrl}/projects/${id}/toggle`, {}, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
exportProject(id: number): Observable<any> { | |
return this.http.get(`${this.adminUrl}/projects/${id}/export`, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
importProject(data: any): Observable<any> { | |
return this.http.post(`${this.adminUrl}/projects/import`, data, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Versions ===================== | |
createVersion(projectId: number, data: any): Observable<any> { | |
return this.http.post(`${this.adminUrl}/projects/${projectId}/versions`, data, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(error => { | |
if (error.status === 409) { | |
console.warn('Race condition in createVersion:', error.error); | |
} | |
return throwError(() => error); | |
}) | |
); | |
} | |
updateVersion(projectId: number, versionNo: number, data: any, force: boolean = false): Observable<any> { | |
// Normalize the timestamp before sending | |
if (data.last_update_date) { | |
data.last_update_date = this.normalizeTimestamp(data.last_update_date); | |
} | |
return this.http.put( | |
`${this.adminUrl}/projects/${projectId}/versions/${versionNo}${force ? '?force=true' : ''}`, | |
data, | |
{ headers: this.getAuthHeaders() } | |
).pipe( | |
catchError(this.handleError) | |
); | |
} | |
deleteVersion(projectId: number, versionNo: number): Observable<any> { | |
return this.http.delete(`${this.adminUrl}/projects/${projectId}/versions/${versionNo}`, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
publishVersion(projectId: number, versionNo: number): Observable<any> { | |
return this.http.post(`${this.adminUrl}/projects/${projectId}/versions/${versionNo}/publish`, {}, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== APIs ===================== | |
getAPIs(includeDeleted = false): Observable<API[]> { | |
return this.http.get<API[]>(`${this.adminUrl}/apis`, { | |
headers: this.getAuthHeaders(), | |
params: { include_deleted: includeDeleted.toString() } | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
createAPI(data: any): Observable<any> { | |
return this.http.post(`${this.adminUrl}/apis`, data, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
updateAPI(name: string, data: any): Observable<any> { | |
// Normalize the timestamp before sending | |
if (data.last_update_date) { | |
data.last_update_date = this.normalizeTimestamp(data.last_update_date); | |
} | |
return this.http.put(`${this.adminUrl}/apis/${name}`, data, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(error => { | |
if (error.status === 409) { | |
const details = error.error?.details || {}; | |
return throwError(() => ({ | |
...error, | |
raceCondition: true, | |
lastUpdateUser: details.last_update_user, | |
lastUpdateDate: details.last_update_date | |
})); | |
} | |
return throwError(() => error); | |
}) | |
); | |
} | |
deleteAPI(name: string): Observable<any> { | |
return this.http.delete(`${this.adminUrl}/apis/${name}`, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
testAPI(data: any): Observable<any> { | |
return this.http.post(`${this.adminUrl}/apis/test`, data, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Spark Integration ===================== | |
sparkStartup(projectName: string): Observable<any> { | |
return this.http.post(`${this.adminUrl}/spark/startup`, | |
{ project_name: projectName }, | |
{ headers: this.getAuthHeaders() } | |
).pipe( | |
catchError(this.handleError) | |
); | |
} | |
sparkGetProjects(): Observable<any> { | |
return this.http.get(`${this.adminUrl}/spark/projects`, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
sparkEnableProject(projectName: string): Observable<any> { | |
return this.http.post(`${this.adminUrl}/spark/project/enable`, | |
{ project_name: projectName }, | |
{ headers: this.getAuthHeaders() } | |
).pipe( | |
catchError(this.handleError) | |
); | |
} | |
sparkDisableProject(projectName: string): Observable<any> { | |
return this.http.post(`${this.adminUrl}/spark/project/disable`, | |
{ project_name: projectName }, | |
{ headers: this.getAuthHeaders() } | |
).pipe( | |
catchError(this.handleError) | |
); | |
} | |
sparkDeleteProject(projectName: string): Observable<any> { | |
return this.http.delete(`${this.adminUrl}/spark/project/${projectName}`, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Tests ===================== | |
runTests(testType: string): Observable<any> { | |
return this.http.post(`${this.adminUrl}/test/run-all`, { test_type: testType }, { | |
headers: this.getAuthHeaders() | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Activity Log ===================== | |
getActivityLog(limit = 50): Observable<any[]> { | |
return this.http.get<any[]>(`${this.adminUrl}/activity-log`, { | |
headers: this.getAuthHeaders(), | |
params: { limit: limit.toString() } | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Validation ===================== | |
validateRegex(pattern: string, testValue: string): Observable<any> { | |
return this.http.post(`${this.adminUrl}/validate/regex`, | |
{ pattern, test_value: testValue }, | |
{ headers: this.getAuthHeaders() } | |
).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Chat ===================== | |
/* 1️⃣ Proje isimleri (combo’yu doldurmak için) */ | |
getChatProjects() { | |
return this.http.get<string[]>(`${this.adminUrl}/projects/names`); | |
} | |
/* 2️⃣ Oturum başlat */ | |
startChat(projectName: string, isRealtime: boolean, locale?: string) { | |
return this.http.post<{ | |
session_id: string; | |
answer: string; | |
}>(`${this.apiUrl}/start_session`, { | |
project_name: projectName, | |
is_realtime: isRealtime, | |
locale: locale || 'tr' | |
}); | |
} | |
/* 3️⃣ Mesaj gönder/al */ | |
chat(sessionId: string, text: string) { | |
const headers = new HttpHeaders().set('X-Session-ID', sessionId); | |
return this.http.post<{ | |
response: string; | |
intent?: string; | |
state: string; | |
}>( | |
`${this.apiUrl}/chat`, | |
{ message: text }, | |
{ headers } | |
); | |
} | |
endSession(sessionId: string): Observable<any> { | |
return this.http.post(`${this.apiUrl}/end-session`, | |
{ session_id: sessionId }, | |
{ headers: this.getAuthHeaders() } | |
); | |
} | |
// ===================== TTS ===================== | |
generateTTS(text: string, voiceId?: string, modelId?: string, outputFormat: string = 'mp3_44100_128'): Observable<Blob> { | |
const body = { | |
text, | |
voice_id: voiceId, | |
model_id: modelId, | |
output_format: outputFormat | |
}; | |
return this.http.post(`${this.apiUrl}/tts/generate`, body, { | |
headers: this.getAuthHeaders(), | |
responseType: 'blob' | |
}).pipe( | |
catchError(this.handleError) | |
); | |
} | |
// ===================== Locale ===================== | |
getAvailableLocales(): Observable<any> { | |
return this.http.get(`${this.adminUrl}/locales`, { headers: this.getAuthHeaders() }); | |
} | |
getLocaleDetails(code: string): Observable<any> { | |
return this.http.get(`${this.adminUrl}/locales/${code}`, { headers: this.getAuthHeaders() }); | |
} | |
// ===================== Error Handler ===================== | |
private handleError(error: any) { | |
console.error('API Error:', error); | |
if (error.status === 401) { | |
// Token expired or invalid | |
this.authService.logout(); | |
} else if (error.status === 409) { | |
// Race condition error | |
const message = error.error?.detail || 'Resource was modified by another user'; | |
return throwError(() => ({ | |
...error, | |
userMessage: message, | |
requiresReload: true | |
})); | |
} | |
// Ensure error object has proper structure | |
const errorResponse = { | |
status: error.status, | |
error: error.error || { detail: error.message || 'Unknown error' }, | |
message: error.error?.detail || error.error?.message || error.message || 'Unknown error' | |
}; | |
return throwError(() => errorResponse); | |
} | |
} |