flare / flare-ui /src /app /services /api.service.ts
ciyidogan's picture
Upload 118 files
9f79da5 verified
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;
}
@Injectable({
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);
}
}