Add generated API client files to git for CI builds

This commit is contained in:
Jamie Pine
2026-01-25 04:57:06 -08:00
parent 5d8672bb9c
commit db5ca1f2ee
4 changed files with 309 additions and 1 deletions
-1
View File
@@ -50,7 +50,6 @@ logs/
.env.local
# Generated files
app/src/lib/api/
app/openapi.json
tauri/src-tauri/binaries/*
+1
View File
@@ -0,0 +1 @@
# Generated OpenAPI client will be placed here
+201
View File
@@ -0,0 +1,201 @@
import { useServerStore } from '@/stores/serverStore';
import type {
VoiceProfileCreate,
VoiceProfileResponse,
ProfileSampleResponse,
GenerationRequest,
GenerationResponse,
HistoryQuery,
HistoryListResponse,
HistoryResponse,
TranscriptionResponse,
HealthResponse,
ModelStatusListResponse,
ModelDownloadRequest,
} from './types';
class ApiClient {
private getBaseUrl(): string {
const serverUrl = useServerStore.getState().serverUrl;
return serverUrl;
}
private async request<T>(
endpoint: string,
options?: RequestInit,
): Promise<T> {
const url = `${this.getBaseUrl()}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({
detail: response.statusText,
}));
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
}
return response.json();
}
// Health
async getHealth(): Promise<HealthResponse> {
return this.request<HealthResponse>('/health');
}
// Profiles
async createProfile(data: VoiceProfileCreate): Promise<VoiceProfileResponse> {
return this.request<VoiceProfileResponse>('/profiles', {
method: 'POST',
body: JSON.stringify(data),
});
}
async listProfiles(): Promise<VoiceProfileResponse[]> {
return this.request<VoiceProfileResponse[]>('/profiles');
}
async getProfile(profileId: string): Promise<VoiceProfileResponse> {
return this.request<VoiceProfileResponse>(`/profiles/${profileId}`);
}
async updateProfile(
profileId: string,
data: VoiceProfileCreate,
): Promise<VoiceProfileResponse> {
return this.request<VoiceProfileResponse>(`/profiles/${profileId}`, {
method: 'PUT',
body: JSON.stringify(data),
});
}
async deleteProfile(profileId: string): Promise<void> {
await this.request<void>(`/profiles/${profileId}`, {
method: 'DELETE',
});
}
async addProfileSample(
profileId: string,
file: File,
referenceText: string,
): Promise<ProfileSampleResponse> {
const url = `${this.getBaseUrl()}/profiles/${profileId}/samples`;
const formData = new FormData();
formData.append('file', file);
formData.append('reference_text', referenceText);
const response = await fetch(url, {
method: 'POST',
body: formData,
});
if (!response.ok) {
const error = await response.json().catch(() => ({
detail: response.statusText,
}));
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
}
return response.json();
}
async listProfileSamples(
profileId: string,
): Promise<ProfileSampleResponse[]> {
return this.request<ProfileSampleResponse[]>(
`/profiles/${profileId}/samples`,
);
}
async deleteProfileSample(sampleId: string): Promise<void> {
await this.request<void>(`/profiles/samples/${sampleId}`, {
method: 'DELETE',
});
}
// Generation
async generateSpeech(
data: GenerationRequest,
): Promise<GenerationResponse> {
return this.request<GenerationResponse>('/generate', {
method: 'POST',
body: JSON.stringify(data),
});
}
// History
async listHistory(query?: HistoryQuery): Promise<HistoryListResponse> {
const params = new URLSearchParams();
if (query?.profile_id) params.append('profile_id', query.profile_id);
if (query?.search) params.append('search', query.search);
if (query?.limit) params.append('limit', query.limit.toString());
if (query?.offset) params.append('offset', query.offset.toString());
const queryString = params.toString();
const endpoint = queryString ? `/history?${queryString}` : '/history';
return this.request<HistoryListResponse>(endpoint);
}
async getGeneration(generationId: string): Promise<HistoryResponse> {
return this.request<HistoryResponse>(`/history/${generationId}`);
}
async deleteGeneration(generationId: string): Promise<void> {
await this.request<void>(`/history/${generationId}`, {
method: 'DELETE',
});
}
// Audio
getAudioUrl(audioId: string): string {
return `${this.getBaseUrl()}/audio/${audioId}`;
}
// Transcription
async transcribeAudio(
file: File,
language?: 'en' | 'zh',
): Promise<TranscriptionResponse> {
const formData = new FormData();
formData.append('file', file);
if (language) {
formData.append('language', language);
}
const url = `${this.getBaseUrl()}/transcribe`;
const response = await fetch(url, {
method: 'POST',
body: formData,
});
if (!response.ok) {
const error = await response.json().catch(() => ({
detail: response.statusText,
}));
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
}
return response.json();
}
// Model Management
async getModelStatus(): Promise<ModelStatusListResponse> {
return this.request<ModelStatusListResponse>('/models/status');
}
async triggerModelDownload(modelName: string): Promise<{ message: string }> {
return this.request<{ message: string }>('/models/download', {
method: 'POST',
body: JSON.stringify({ model_name: modelName } as ModelDownloadRequest),
});
}
}
export const apiClient = new ApiClient();
+107
View File
@@ -0,0 +1,107 @@
// API Types matching backend Pydantic models
export interface VoiceProfileCreate {
name: string;
description?: string;
language: 'en' | 'zh';
}
export interface VoiceProfileResponse {
id: string;
name: string;
description?: string;
language: string;
created_at: string;
updated_at: string;
}
export interface ProfileSampleCreate {
reference_text: string;
}
export interface ProfileSampleResponse {
id: string;
profile_id: string;
audio_path: string;
reference_text: string;
}
export interface GenerationRequest {
profile_id: string;
text: string;
language: 'en' | 'zh';
seed?: number;
model_size?: '1.7B' | '0.6B';
}
export interface GenerationResponse {
id: string;
profile_id: string;
text: string;
language: string;
audio_path: string;
duration: number;
seed?: number;
created_at: string;
}
export interface HistoryQuery {
profile_id?: string;
search?: string;
limit?: number;
offset?: number;
}
export interface HistoryResponse extends GenerationResponse {
profile_name: string;
}
export interface HistoryListResponse {
items: HistoryResponse[];
total: number;
}
export interface TranscriptionRequest {
language?: 'en' | 'zh';
}
export interface TranscriptionResponse {
text: string;
duration: number;
}
export interface HealthResponse {
status: string;
model_loaded: boolean;
model_downloaded?: boolean;
model_size?: string;
gpu_available: boolean;
vram_used_mb?: number;
}
export interface ModelProgress {
model_name: string;
current: number;
total: number;
progress: number;
filename?: string;
status: 'downloading' | 'extracting' | 'complete' | 'error';
timestamp: string;
error?: string;
}
export interface ModelStatus {
model_name: string;
display_name: string;
downloaded: boolean;
size_mb?: number;
loaded: boolean;
}
export interface ModelStatusListResponse {
models: ModelStatus[];
}
export interface ModelDownloadRequest {
model_name: string;
}