import { DatabaseStore } from '$lib/stores/database';
import { chatService, slotsService } from '$lib/services';
import { config } from '$lib/stores/settings.svelte';
+import { serverStore } from '$lib/stores/server.svelte';
import { normalizeModelName } from '$lib/utils/model-names';
import { filterByLeafNodeId, findLeafNode, findDescendantMessages } from '$lib/utils/branching';
import { browser } from '$app/environment';
let resolvedModel: string | null = null;
let modelPersisted = false;
+ const currentConfig = config();
+ const preferServerPropsModel = !currentConfig.modelSelectorEnabled;
+ let serverPropsRefreshed = false;
+ let updateModelFromServerProps: ((persistImmediately?: boolean) => void) | null = null;
+
+ const refreshServerPropsOnce = () => {
+ if (serverPropsRefreshed) {
+ return;
+ }
+
+ serverPropsRefreshed = true;
+
+ const hasExistingProps = serverStore.serverProps !== null;
- const recordModel = (modelName: string, persistImmediately = true): void => {
- const normalizedModel = normalizeModelName(modelName);
+ serverStore
+ .fetchServerProps({ silent: hasExistingProps })
+ .then(() => {
+ updateModelFromServerProps?.(true);
+ })
+ .catch((error) => {
+ console.warn('Failed to refresh server props after streaming started:', error);
+ });
+ };
+
+ const recordModel = (modelName: string | null | undefined, persistImmediately = true): void => {
+ const serverModelName = serverStore.modelName;
+ const preferredModelSource = preferServerPropsModel
+ ? (serverModelName ?? modelName ?? null)
+ : (modelName ?? serverModelName ?? null);
+
+ if (!preferredModelSource) {
+ return;
+ }
+
+ const normalizedModel = normalizeModelName(preferredModelSource);
if (!normalizedModel || normalizedModel === resolvedModel) {
return;
}
};
+ if (preferServerPropsModel) {
+ updateModelFromServerProps = (persistImmediately = true) => {
+ const currentServerModel = serverStore.modelName;
+
+ if (!currentServerModel) {
+ return;
+ }
+
+ recordModel(currentServerModel, persistImmediately);
+ };
+
+ updateModelFromServerProps(false);
+ }
+
slotsService.startStreaming();
slotsService.setActiveConversation(assistantMessage.convId);
{
...this.getApiOptions(),
+ onFirstValidChunk: () => {
+ refreshServerPropsOnce();
+ },
onChunk: (chunk: string) => {
streamedContent += chunk;
this.setConversationStreaming(
private _error = $state<string | null>(null);
private _serverWarning = $state<string | null>(null);
private _slotsEndpointAvailable = $state<boolean | null>(null);
+ private fetchServerPropsPromise: Promise<void> | null = null;
private readCachedServerProps(): ApiLlamaCppServerProps | null {
if (!browser) return null;
/**
* Fetches server properties from the server
*/
- async fetchServerProps(): Promise<void> {
- this._loading = true;
- this._error = null;
- this._serverWarning = null;
+ async fetchServerProps(options: { silent?: boolean } = {}): Promise<void> {
+ const { silent = false } = options;
+ const isSilent = silent && this._serverProps !== null;
- try {
- console.log('Fetching server properties...');
- const props = await ChatService.getServerProps();
- this._serverProps = props;
- this.persistServerProps(props);
- console.log('Server properties loaded:', props);
-
- // Check slots endpoint availability after server props are loaded
- await this.checkSlotsEndpointAvailability();
- } catch (error) {
- const hadCachedProps = this._serverProps !== null;
- let errorMessage = 'Failed to connect to server';
- let isOfflineLikeError = false;
- let isServerSideError = false;
-
- if (error instanceof Error) {
- // Handle specific error types with user-friendly messages
- if (error.name === 'TypeError' && error.message.includes('fetch')) {
- errorMessage = 'Server is not running or unreachable';
- isOfflineLikeError = true;
- } else if (error.message.includes('ECONNREFUSED')) {
- errorMessage = 'Connection refused - server may be offline';
- isOfflineLikeError = true;
- } else if (error.message.includes('ENOTFOUND')) {
- errorMessage = 'Server not found - check server address';
- isOfflineLikeError = true;
- } else if (error.message.includes('ETIMEDOUT')) {
- errorMessage = 'Request timed out - the server took too long to respond';
- isOfflineLikeError = true;
- } else if (error.message.includes('503')) {
- errorMessage = 'Server temporarily unavailable - try again shortly';
- isServerSideError = true;
- } else if (error.message.includes('500')) {
- errorMessage = 'Server error - check server logs';
- isServerSideError = true;
- } else if (error.message.includes('404')) {
- errorMessage = 'Server endpoint not found';
- } else if (error.message.includes('403') || error.message.includes('401')) {
- errorMessage = 'Access denied';
+ if (this.fetchServerPropsPromise) {
+ return this.fetchServerPropsPromise;
+ }
+
+ if (!isSilent) {
+ this._loading = true;
+ this._error = null;
+ this._serverWarning = null;
+ }
+
+ const hadProps = this._serverProps !== null;
+
+ const fetchPromise = (async () => {
+ try {
+ const props = await ChatService.getServerProps();
+ this._serverProps = props;
+ this.persistServerProps(props);
+ this._error = null;
+ this._serverWarning = null;
+ await this.checkSlotsEndpointAvailability();
+ } catch (error) {
+ if (isSilent && hadProps) {
+ console.warn('Silent server props refresh failed, keeping cached data:', error);
+ return;
+ }
+
+ this.handleFetchServerPropsError(error, hadProps);
+ } finally {
+ if (!isSilent) {
+ this._loading = false;
}
+
+ this.fetchServerPropsPromise = null;
}
+ })();
+
+ this.fetchServerPropsPromise = fetchPromise;
+
+ await fetchPromise;
+ }
- let cachedProps: ApiLlamaCppServerProps | null = null;
+ /**
+ * Handles fetch failures by attempting to recover cached server props and
+ * updating the user-facing error or warning state appropriately.
+ */
+ private handleFetchServerPropsError(error: unknown, hadProps: boolean): void {
+ const { errorMessage, isOfflineLikeError, isServerSideError } = this.normalizeFetchError(error);
- if (!hadCachedProps) {
- cachedProps = this.readCachedServerProps();
- if (cachedProps) {
- this._serverProps = cachedProps;
- this._error = null;
+ let cachedProps: ApiLlamaCppServerProps | null = null;
- if (isOfflineLikeError || isServerSideError) {
- this._serverWarning = errorMessage;
- }
+ if (!hadProps) {
+ cachedProps = this.readCachedServerProps();
- console.warn(
- 'Failed to refresh server properties, using cached values from localStorage:',
- errorMessage
- );
- } else {
- this._error = errorMessage;
- }
- } else {
+ if (cachedProps) {
+ this._serverProps = cachedProps;
this._error = null;
if (isOfflineLikeError || isServerSideError) {
}
console.warn(
- 'Failed to refresh server properties, continuing with cached values:',
+ 'Failed to refresh server properties, using cached values from localStorage:',
errorMessage
);
+ } else {
+ this._error = errorMessage;
+ }
+ } else {
+ this._error = null;
+
+ if (isOfflineLikeError || isServerSideError) {
+ this._serverWarning = errorMessage;
}
- console.error('Error fetching server properties:', error);
- } finally {
- this._loading = false;
+
+ console.warn(
+ 'Failed to refresh server properties, continuing with cached values:',
+ errorMessage
+ );
}
+
+ console.error('Error fetching server properties:', error);
+ }
+
+ private normalizeFetchError(error: unknown): {
+ errorMessage: string;
+ isOfflineLikeError: boolean;
+ isServerSideError: boolean;
+ } {
+ let errorMessage = 'Failed to connect to server';
+ let isOfflineLikeError = false;
+ let isServerSideError = false;
+
+ if (error instanceof Error) {
+ const message = error.message || '';
+
+ if (error.name === 'TypeError' && message.includes('fetch')) {
+ errorMessage = 'Server is not running or unreachable';
+ isOfflineLikeError = true;
+ } else if (message.includes('ECONNREFUSED')) {
+ errorMessage = 'Connection refused - server may be offline';
+ isOfflineLikeError = true;
+ } else if (message.includes('ENOTFOUND')) {
+ errorMessage = 'Server not found - check server address';
+ isOfflineLikeError = true;
+ } else if (message.includes('ETIMEDOUT')) {
+ errorMessage = 'Request timed out - the server took too long to respond';
+ isOfflineLikeError = true;
+ } else if (message.includes('503')) {
+ errorMessage = 'Server temporarily unavailable - try again shortly';
+ isServerSideError = true;
+ } else if (message.includes('500')) {
+ errorMessage = 'Server error - check server logs';
+ isServerSideError = true;
+ } else if (message.includes('404')) {
+ errorMessage = 'Server endpoint not found';
+ } else if (message.includes('403') || message.includes('401')) {
+ errorMessage = 'Access denied';
+ }
+ }
+
+ return { errorMessage, isOfflineLikeError, isServerSideError };
}
/**
this._serverWarning = null;
this._loading = false;
this._slotsEndpointAvailable = null;
+ this.fetchServerPropsPromise = null;
this.persistServerProps(null);
}
}