api.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // API client for stable-diffusion REST API
  2. // Type for server config injected by the server
  3. declare global {
  4. interface Window {
  5. __SERVER_CONFIG__?: {
  6. apiUrl: string;
  7. apiBasePath: string;
  8. host: string;
  9. port: number;
  10. };
  11. }
  12. }
  13. // Get configuration from server-injected config or fallback to environment/defaults
  14. // This function is called at runtime to ensure __SERVER_CONFIG__ is available
  15. function getApiConfig() {
  16. if (typeof window !== 'undefined' && window.__SERVER_CONFIG__) {
  17. return {
  18. apiUrl: window.__SERVER_CONFIG__.apiUrl,
  19. apiBase: window.__SERVER_CONFIG__.apiBasePath,
  20. };
  21. }
  22. // Fallback for development mode
  23. return {
  24. apiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080',
  25. apiBase: process.env.NEXT_PUBLIC_API_BASE_PATH || '/api',
  26. };
  27. }
  28. export interface GenerationRequest {
  29. model?: string;
  30. prompt: string;
  31. negative_prompt?: string;
  32. width?: number;
  33. height?: number;
  34. steps?: number;
  35. cfg_scale?: number;
  36. seed?: string;
  37. sampling_method?: string;
  38. scheduler?: string;
  39. batch_count?: number;
  40. clip_skip?: number;
  41. strength?: number;
  42. control_strength?: number;
  43. }
  44. export interface JobInfo {
  45. id?: string;
  46. request_id?: string;
  47. status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled' | 'queued';
  48. progress?: number;
  49. result?: {
  50. images: string[];
  51. };
  52. error?: string;
  53. created_at?: string;
  54. updated_at?: string;
  55. message?: string;
  56. queue_position?: number;
  57. }
  58. export interface ModelInfo {
  59. id?: string;
  60. name: string;
  61. path?: string;
  62. type: string;
  63. size?: number;
  64. file_size?: number;
  65. file_size_mb?: number;
  66. sha256?: string | null;
  67. sha256_short?: string | null;
  68. loaded?: boolean;
  69. }
  70. export interface QueueStatus {
  71. active_generations: number;
  72. jobs: JobInfo[];
  73. running: boolean;
  74. size: number;
  75. }
  76. class ApiClient {
  77. // Get base URL dynamically at runtime to ensure server config is loaded
  78. private getBaseUrl(): string {
  79. const { apiUrl, apiBase } = getApiConfig();
  80. return `${apiUrl}${apiBase}`;
  81. }
  82. private async request<T>(
  83. endpoint: string,
  84. options: RequestInit = {}
  85. ): Promise<T> {
  86. const url = `${this.getBaseUrl()}${endpoint}`;
  87. const response = await fetch(url, {
  88. ...options,
  89. headers: {
  90. 'Content-Type': 'application/json',
  91. ...options.headers,
  92. },
  93. });
  94. if (!response.ok) {
  95. const errorData = await response.json().catch(() => ({
  96. error: { message: response.statusText },
  97. }));
  98. // Handle nested error structure: { error: { message: "..." } }
  99. const errorMessage =
  100. errorData.error?.message ||
  101. errorData.message ||
  102. errorData.error ||
  103. 'API request failed';
  104. throw new Error(errorMessage);
  105. }
  106. return response.json();
  107. }
  108. // Generation endpoints
  109. async generateImage(params: GenerationRequest): Promise<JobInfo> {
  110. return this.request<JobInfo>('/generate/text2img', {
  111. method: 'POST',
  112. body: JSON.stringify(params),
  113. });
  114. }
  115. async text2img(params: GenerationRequest): Promise<JobInfo> {
  116. return this.request<JobInfo>('/generate/text2img', {
  117. method: 'POST',
  118. body: JSON.stringify(params),
  119. });
  120. }
  121. async img2img(params: GenerationRequest & { image: string }): Promise<JobInfo> {
  122. return this.request<JobInfo>('/generate/img2img', {
  123. method: 'POST',
  124. body: JSON.stringify(params),
  125. });
  126. }
  127. // Job management
  128. async getJobStatus(jobId: string): Promise<JobInfo> {
  129. return this.request<JobInfo>(`/queue/job/${jobId}`);
  130. }
  131. async cancelJob(jobId: string): Promise<void> {
  132. return this.request<void>('/queue/cancel', {
  133. method: 'POST',
  134. body: JSON.stringify({ job_id: jobId }),
  135. });
  136. }
  137. async getQueueStatus(): Promise<QueueStatus> {
  138. const response = await this.request<{ queue: QueueStatus }>('/queue/status');
  139. return response.queue;
  140. }
  141. async clearQueue(): Promise<void> {
  142. return this.request<void>('/queue/clear', {
  143. method: 'POST',
  144. });
  145. }
  146. // Model management
  147. async getModels(type?: string, loaded?: boolean): Promise<ModelInfo[]> {
  148. let endpoint = '/models';
  149. const params = [];
  150. if (type && type !== 'loaded') params.push(`type=${type}`);
  151. if (type === 'loaded' || loaded) params.push('loaded=true');
  152. // Request a high limit to get all models (default is 50)
  153. params.push('limit=1000');
  154. if (params.length > 0) endpoint += '?' + params.join('&');
  155. const response = await this.request<{ models: ModelInfo[] }>(endpoint);
  156. // Add id field based on sha256_short or name, and normalize size field
  157. return response.models.map(model => ({
  158. ...model,
  159. id: model.sha256_short || model.name,
  160. size: model.file_size || model.size,
  161. path: model.path || model.name,
  162. }));
  163. }
  164. async getModelInfo(modelId: string): Promise<ModelInfo> {
  165. return this.request<ModelInfo>(`/models/${modelId}`);
  166. }
  167. async loadModel(modelId: string): Promise<void> {
  168. return this.request<void>(`/models/${modelId}/load`, {
  169. method: 'POST',
  170. });
  171. }
  172. async unloadModel(modelId: string): Promise<void> {
  173. return this.request<void>(`/models/${modelId}/unload`, {
  174. method: 'POST',
  175. });
  176. }
  177. async scanModels(): Promise<void> {
  178. return this.request<void>('/models/refresh', {
  179. method: 'POST',
  180. });
  181. }
  182. async convertModel(modelName: string, quantizationType: string, outputPath?: string): Promise<{ request_id: string; message: string }> {
  183. return this.request<{ request_id: string; message: string }>('/models/convert', {
  184. method: 'POST',
  185. body: JSON.stringify({
  186. model_name: modelName,
  187. quantization_type: quantizationType,
  188. output_path: outputPath,
  189. }),
  190. });
  191. }
  192. // System endpoints
  193. async getHealth(): Promise<{ status: string }> {
  194. return this.request<{ status: string }>('/health');
  195. }
  196. async getStatus(): Promise<any> {
  197. return this.request<any>('/status');
  198. }
  199. async getSystemInfo(): Promise<any> {
  200. return this.request<any>('/system');
  201. }
  202. async restartServer(): Promise<{ message: string }> {
  203. return this.request<{ message: string }>('/system/restart', {
  204. method: 'POST',
  205. body: JSON.stringify({}),
  206. });
  207. }
  208. // Configuration endpoints
  209. async getSamplers(): Promise<Array<{ name: string; description: string; recommended_steps: number }>> {
  210. const response = await this.request<{ samplers: Array<{ name: string; description: string; recommended_steps: number }> }>('/samplers');
  211. return response.samplers;
  212. }
  213. async getSchedulers(): Promise<Array<{ name: string; description: string }>> {
  214. const response = await this.request<{ schedulers: Array<{ name: string; description: string }> }>('/schedulers');
  215. return response.schedulers;
  216. }
  217. }
  218. export const apiClient = new ApiClient();