| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- // API client for stable-diffusion REST API
- // Type for server config injected by the server
- declare global {
- interface Window {
- __SERVER_CONFIG__?: {
- apiUrl: string;
- apiBasePath: string;
- host: string;
- port: number;
- };
- }
- }
- // Get configuration from server-injected config or fallback to environment/defaults
- // This function is called at runtime to ensure __SERVER_CONFIG__ is available
- function getApiConfig() {
- if (typeof window !== 'undefined' && window.__SERVER_CONFIG__) {
- return {
- apiUrl: window.__SERVER_CONFIG__.apiUrl,
- apiBase: window.__SERVER_CONFIG__.apiBasePath,
- };
- }
- // Fallback for development mode
- return {
- apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080',
- apiBase: process.env.NEXT_PUBLIC_API_BASE_PATH || '/api',
- };
- }
- export interface GenerationRequest {
- model?: string;
- prompt: string;
- negative_prompt?: string;
- width?: number;
- height?: number;
- steps?: number;
- cfg_scale?: number;
- seed?: string;
- sampling_method?: string;
- scheduler?: string;
- batch_count?: number;
- clip_skip?: number;
- strength?: number;
- control_strength?: number;
- }
- export interface JobInfo {
- id?: string;
- request_id?: string;
- status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled' | 'queued';
- progress?: number;
- result?: {
- images: string[];
- };
- error?: string;
- created_at?: string;
- updated_at?: string;
- message?: string;
- queue_position?: number;
- }
- export interface ModelInfo {
- id?: string;
- name: string;
- path?: string;
- type: string;
- size?: number;
- file_size?: number;
- file_size_mb?: number;
- sha256?: string | null;
- sha256_short?: string | null;
- loaded?: boolean;
- }
- export interface QueueStatus {
- active_generations: number;
- jobs: JobInfo[];
- running: boolean;
- size: number;
- }
- class ApiClient {
- // Get base URL dynamically at runtime to ensure server config is loaded
- private getBaseUrl(): string {
- const { apiUrl, apiBase } = getApiConfig();
- return `${apiUrl}${apiBase}`;
- }
- 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 errorData = await response.json().catch(() => ({
- error: { message: response.statusText },
- }));
- // Handle nested error structure: { error: { message: "..." } }
- const errorMessage =
- errorData.error?.message ||
- errorData.message ||
- errorData.error ||
- 'API request failed';
- throw new Error(errorMessage);
- }
- return response.json();
- }
- // Generation endpoints
- async generateImage(params: GenerationRequest): Promise<JobInfo> {
- return this.request<JobInfo>('/generate/text2img', {
- method: 'POST',
- body: JSON.stringify(params),
- });
- }
- async text2img(params: GenerationRequest): Promise<JobInfo> {
- return this.request<JobInfo>('/generate/text2img', {
- method: 'POST',
- body: JSON.stringify(params),
- });
- }
- async img2img(params: GenerationRequest & { image: string }): Promise<JobInfo> {
- return this.request<JobInfo>('/generate/img2img', {
- method: 'POST',
- body: JSON.stringify(params),
- });
- }
- // Job management
- async getJobStatus(jobId: string): Promise<JobInfo> {
- return this.request<JobInfo>(`/queue/job/${jobId}`);
- }
- async cancelJob(jobId: string): Promise<void> {
- return this.request<void>('/queue/cancel', {
- method: 'POST',
- body: JSON.stringify({ job_id: jobId }),
- });
- }
- async getQueueStatus(): Promise<QueueStatus> {
- const response = await this.request<{ queue: QueueStatus }>('/queue/status');
- return response.queue;
- }
- async clearQueue(): Promise<void> {
- return this.request<void>('/queue/clear', {
- method: 'POST',
- });
- }
- // Model management
- async getModels(type?: string, loaded?: boolean): Promise<ModelInfo[]> {
- let endpoint = '/models';
- const params = [];
- if (type && type !== 'loaded') params.push(`type=${type}`);
- if (type === 'loaded' || loaded) params.push('loaded=true');
- // Request a high limit to get all models (default is 50)
- params.push('limit=1000');
- if (params.length > 0) endpoint += '?' + params.join('&');
- const response = await this.request<{ models: ModelInfo[] }>(endpoint);
- // Add id field based on sha256_short or name, and normalize size field
- return response.models.map(model => ({
- ...model,
- id: model.sha256_short || model.name,
- size: model.file_size || model.size,
- path: model.path || model.name,
- }));
- }
- async getModelInfo(modelId: string): Promise<ModelInfo> {
- return this.request<ModelInfo>(`/models/${modelId}`);
- }
- async loadModel(modelId: string): Promise<void> {
- return this.request<void>(`/models/${modelId}/load`, {
- method: 'POST',
- });
- }
- async unloadModel(modelId: string): Promise<void> {
- return this.request<void>(`/models/${modelId}/unload`, {
- method: 'POST',
- });
- }
- async scanModels(): Promise<void> {
- return this.request<void>('/models/refresh', {
- method: 'POST',
- });
- }
- async convertModel(modelName: string, quantizationType: string, outputPath?: string): Promise<{ request_id: string; message: string }> {
- return this.request<{ request_id: string; message: string }>('/models/convert', {
- method: 'POST',
- body: JSON.stringify({
- model_name: modelName,
- quantization_type: quantizationType,
- output_path: outputPath,
- }),
- });
- }
- // System endpoints
- async getHealth(): Promise<{ status: string }> {
- return this.request<{ status: string }>('/health');
- }
- async getStatus(): Promise<any> {
- return this.request<any>('/status');
- }
- async getSystemInfo(): Promise<any> {
- return this.request<any>('/system');
- }
- async restartServer(): Promise<{ message: string }> {
- return this.request<{ message: string }>('/system/restart', {
- method: 'POST',
- body: JSON.stringify({}),
- });
- }
- // Configuration endpoints
- async getSamplers(): Promise<Array<{ name: string; description: string; recommended_steps: number }>> {
- const response = await this.request<{ samplers: Array<{ name: string; description: string; recommended_steps: number }> }>('/samplers');
- return response.samplers;
- }
- async getSchedulers(): Promise<Array<{ name: string; description: string }>> {
- const response = await this.request<{ schedulers: Array<{ name: string; description: string }> }>('/schedulers');
- return response.schedulers;
- }
- }
- export const apiClient = new ApiClient();
|