server.svelte.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { ChatService } from '$lib/services/chat';
  2. import { config } from '$lib/stores/settings.svelte';
  3. /**
  4. * ServerStore - Server state management and capability detection
  5. *
  6. * This store manages communication with the llama.cpp server to retrieve and maintain
  7. * server properties, model information, and capability detection. It provides reactive
  8. * state for server connectivity, model capabilities, and endpoint availability.
  9. *
  10. * **Architecture & Relationships:**
  11. * - **ServerStore** (this class): Server state and capability management
  12. * - Fetches and caches server properties from `/props` endpoint
  13. * - Detects model capabilities (vision, audio support)
  14. * - Tests endpoint availability (slots endpoint)
  15. * - Provides reactive server state for UI components
  16. *
  17. * - **ChatService**: Uses server properties for request validation
  18. * - **SlotsService**: Depends on slots endpoint availability detection
  19. * - **UI Components**: Subscribe to server state for capability-based rendering
  20. *
  21. * **Key Features:**
  22. * - **Server Properties**: Model path, context size, build information
  23. * - **Capability Detection**: Vision and audio modality support
  24. * - **Endpoint Testing**: Slots endpoint availability checking
  25. * - **Error Handling**: User-friendly error messages for connection issues
  26. * - **Reactive State**: Svelte 5 runes for automatic UI updates
  27. * - **State Management**: Loading states and error recovery
  28. *
  29. * **Server Capabilities Detected:**
  30. * - Model name extraction from file path
  31. * - Vision support (multimodal image processing)
  32. * - Audio support (speech processing)
  33. * - Slots endpoint availability (for processing state monitoring)
  34. * - Context window size and token limits
  35. */
  36. class ServerStore {
  37. private _serverProps = $state<ApiLlamaCppServerProps | null>(null);
  38. private _loading = $state(false);
  39. private _error = $state<string | null>(null);
  40. private _slotsEndpointAvailable = $state<boolean | null>(null);
  41. get serverProps(): ApiLlamaCppServerProps | null {
  42. return this._serverProps;
  43. }
  44. get loading(): boolean {
  45. return this._loading;
  46. }
  47. get error(): string | null {
  48. return this._error;
  49. }
  50. get modelName(): string | null {
  51. if (!this._serverProps?.model_path) return null;
  52. return this._serverProps.model_path.split(/(\\|\/)/).pop() || null;
  53. }
  54. get supportedModalities(): string[] {
  55. const modalities: string[] = [];
  56. if (this._serverProps?.modalities?.audio) {
  57. modalities.push('audio');
  58. }
  59. if (this._serverProps?.modalities?.vision) {
  60. modalities.push('vision');
  61. }
  62. return modalities;
  63. }
  64. get supportsVision(): boolean {
  65. return this._serverProps?.modalities?.vision ?? false;
  66. }
  67. get supportsAudio(): boolean {
  68. return this._serverProps?.modalities?.audio ?? false;
  69. }
  70. get slotsEndpointAvailable(): boolean | null {
  71. return this._slotsEndpointAvailable;
  72. }
  73. /**
  74. * Check if slots endpoint is available based on server properties and endpoint support
  75. */
  76. private async checkSlotsEndpointAvailability(): Promise<void> {
  77. if (!this._serverProps) {
  78. this._slotsEndpointAvailable = false;
  79. return;
  80. }
  81. if (this._serverProps.total_slots <= 0) {
  82. this._slotsEndpointAvailable = false;
  83. return;
  84. }
  85. try {
  86. const currentConfig = config();
  87. const apiKey = currentConfig.apiKey?.toString().trim();
  88. const response = await fetch(`./slots`, {
  89. headers: {
  90. ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {})
  91. }
  92. });
  93. if (response.status === 501) {
  94. console.info('Slots endpoint not implemented - server started without --slots flag');
  95. this._slotsEndpointAvailable = false;
  96. return;
  97. }
  98. this._slotsEndpointAvailable = true;
  99. } catch (error) {
  100. console.warn('Unable to test slots endpoint availability:', error);
  101. this._slotsEndpointAvailable = false;
  102. }
  103. }
  104. /**
  105. * Fetches server properties from the server
  106. */
  107. async fetchServerProps(): Promise<void> {
  108. this._loading = true;
  109. this._error = null;
  110. try {
  111. console.log('Fetching server properties...');
  112. const props = await ChatService.getServerProps();
  113. this._serverProps = props;
  114. console.log('Server properties loaded:', props);
  115. // Check slots endpoint availability after server props are loaded
  116. await this.checkSlotsEndpointAvailability();
  117. } catch (error) {
  118. let errorMessage = 'Failed to connect to server';
  119. if (error instanceof Error) {
  120. // Handle specific error types with user-friendly messages
  121. if (error.name === 'TypeError' && error.message.includes('fetch')) {
  122. errorMessage = 'Server is not running or unreachable';
  123. } else if (error.message.includes('ECONNREFUSED')) {
  124. errorMessage = 'Connection refused - server may be offline';
  125. } else if (error.message.includes('ENOTFOUND')) {
  126. errorMessage = 'Server not found - check server address';
  127. } else if (error.message.includes('ETIMEDOUT')) {
  128. errorMessage = 'Connection timeout - server may be overloaded';
  129. } else if (error.message.includes('500')) {
  130. errorMessage = 'Server error - check server logs';
  131. } else if (error.message.includes('404')) {
  132. errorMessage = 'Server endpoint not found';
  133. } else if (error.message.includes('403') || error.message.includes('401')) {
  134. errorMessage = 'Access denied';
  135. }
  136. }
  137. this._error = errorMessage;
  138. console.error('Error fetching server properties:', error);
  139. } finally {
  140. this._loading = false;
  141. }
  142. }
  143. /**
  144. * Clears the server state
  145. */
  146. clear(): void {
  147. this._serverProps = null;
  148. this._error = null;
  149. this._loading = false;
  150. this._slotsEndpointAvailable = null;
  151. }
  152. }
  153. export const serverStore = new ServerStore();
  154. export const serverProps = () => serverStore.serverProps;
  155. export const serverLoading = () => serverStore.loading;
  156. export const serverError = () => serverStore.error;
  157. export const modelName = () => serverStore.modelName;
  158. export const supportedModalities = () => serverStore.supportedModalities;
  159. export const supportsVision = () => serverStore.supportsVision;
  160. export const supportsAudio = () => serverStore.supportsAudio;
  161. export const slotsEndpointAvailable = () => serverStore.slotsEndpointAvailable;