refactor: Architecture cleanup

This commit is contained in:
Aleksander Grygier 2025-11-27 14:03:25 +01:00
parent 78ead49830
commit d73353732f
3 changed files with 39 additions and 36 deletions

View File

@ -1,7 +1,4 @@
import { config } from '$lib/stores/settings.svelte';
import { getJsonHeaders } from '$lib/utils/api-headers'; import { getJsonHeaders } from '$lib/utils/api-headers';
import { selectedModelName } from '$lib/stores/models.svelte';
import { isRouterMode, serverStore } from '$lib/stores/server.svelte';
import type { import type {
ApiChatCompletionRequest, ApiChatCompletionRequest,
ApiChatCompletionResponse, ApiChatCompletionResponse,
@ -106,11 +103,12 @@ export class ChatService {
// Other parameters // Other parameters
samplers, samplers,
custom, custom,
timings_per_token timings_per_token,
// Config options
systemMessage,
disableReasoningFormat
} = options; } = options;
const currentConfig = config();
const normalizedMessages: ApiChatMessageData[] = messages const normalizedMessages: ApiChatMessageData[] = messages
.map((msg) => { .map((msg) => {
if ('id' in msg && 'convId' in msg && 'timestamp' in msg) { if ('id' in msg && 'convId' in msg && 'timestamp' in msg) {
@ -130,7 +128,7 @@ export class ChatService {
return true; return true;
}); });
const processedMessages = ChatService.injectSystemMessage(normalizedMessages); const processedMessages = ChatService.injectSystemMessage(normalizedMessages, systemMessage);
const requestBody: ApiChatCompletionRequest = { const requestBody: ApiChatCompletionRequest = {
messages: processedMessages.map((msg: ApiChatMessageData) => ({ messages: processedMessages.map((msg: ApiChatMessageData) => ({
@ -140,14 +138,12 @@ export class ChatService {
stream stream
}; };
const isRouter = isRouterMode(); // Include model in request if provided (required in ROUTER mode)
const activeModel = isRouter ? options.model || selectedModelName() : null; if (options.model) {
requestBody.model = options.model;
if (isRouter && activeModel) {
requestBody.model = activeModel;
} }
requestBody.reasoning_format = currentConfig.disableReasoningFormat ? 'none' : 'auto'; requestBody.reasoning_format = disableReasoningFormat ? 'none' : 'auto';
if (temperature !== undefined) requestBody.temperature = temperature; if (temperature !== undefined) requestBody.temperature = temperature;
if (max_tokens !== undefined) { if (max_tokens !== undefined) {
@ -728,28 +724,30 @@ export class ChatService {
} }
/** /**
* Injects a system message at the beginning of the conversation if configured in settings. * Injects a system message at the beginning of the conversation if provided.
* Checks for existing system messages to avoid duplication and retrieves the system message * Checks for existing system messages to avoid duplication.
* from the current configuration settings.
* *
* @param messages - Array of chat messages to process * @param messages - Array of chat messages to process
* @returns Array of messages with system message injected at the beginning if configured * @param systemMessage - Optional system message to inject
* @returns Array of messages with system message injected at the beginning if provided
* @private * @private
*/ */
private static injectSystemMessage(messages: ApiChatMessageData[]): ApiChatMessageData[] { private static injectSystemMessage(
const currentConfig = config(); messages: ApiChatMessageData[],
const systemMessage = currentConfig.systemMessage?.toString().trim(); systemMessage?: string
): ApiChatMessageData[] {
const trimmedSystemMessage = systemMessage?.trim();
if (!systemMessage) { if (!trimmedSystemMessage) {
return messages; return messages;
} }
if (messages.length > 0 && messages[0].role === 'system') { if (messages.length > 0 && messages[0].role === 'system') {
if (messages[0].content !== systemMessage) { if (messages[0].content !== trimmedSystemMessage) {
const updatedMessages = [...messages]; const updatedMessages = [...messages];
updatedMessages[0] = { updatedMessages[0] = {
role: 'system', role: 'system',
content: systemMessage content: trimmedSystemMessage
}; };
return updatedMessages; return updatedMessages;
} }
@ -759,7 +757,7 @@ export class ChatService {
const systemMsg: ApiChatMessageData = { const systemMsg: ApiChatMessageData = {
role: 'system', role: 'system',
content: systemMessage content: trimmedSystemMessage
}; };
return [systemMsg, ...messages]; return [systemMsg, ...messages];
@ -799,16 +797,6 @@ export class ChatService {
* @private * @private
*/ */
private static extractModelName(data: unknown): string | undefined { private static extractModelName(data: unknown): string | undefined {
// WORKAROUND: In single model mode, use model name from props instead of API response
// because llama-server returns `gpt-3.5-turbo` value in the `model` field
const isRouter = isRouterMode();
if (!isRouter) {
const propsModelName = serverStore.modelName;
if (propsModelName) {
return propsModelName;
}
}
const asRecord = (value: unknown): Record<string, unknown> | undefined => { const asRecord = (value: unknown): Record<string, unknown> | undefined => {
return typeof value === 'object' && value !== null return typeof value === 'object' && value !== null
? (value as Record<string, unknown>) ? (value as Record<string, unknown>)

View File

@ -1,7 +1,8 @@
import { DatabaseService, ChatService } from '$lib/services'; import { DatabaseService, ChatService } from '$lib/services';
import { conversationsStore } from '$lib/stores/conversations.svelte'; import { conversationsStore } from '$lib/stores/conversations.svelte';
import { config } from '$lib/stores/settings.svelte'; import { config } from '$lib/stores/settings.svelte';
import { contextSize } from '$lib/stores/server.svelte'; import { contextSize, isRouterMode } from '$lib/stores/server.svelte';
import { selectedModelName } from '$lib/stores/models.svelte';
import { normalizeModelName } from '$lib/utils/model-names'; import { normalizeModelName } from '$lib/utils/model-names';
import { filterByLeafNodeId, findDescendantMessages, findLeafNode } from '$lib/utils/branching'; import { filterByLeafNodeId, findDescendantMessages, findLeafNode } from '$lib/utils/branching';
import { SvelteMap } from 'svelte/reactivity'; import { SvelteMap } from 'svelte/reactivity';
@ -78,6 +79,16 @@ class ChatStore {
const apiOptions: Record<string, unknown> = { stream: true, timings_per_token: true }; const apiOptions: Record<string, unknown> = { stream: true, timings_per_token: true };
// Model selection (required in ROUTER mode)
if (isRouterMode()) {
const modelName = selectedModelName();
if (modelName) apiOptions.model = modelName;
}
// Config options needed by ChatService
if (currentConfig.systemMessage) apiOptions.systemMessage = currentConfig.systemMessage;
if (currentConfig.disableReasoningFormat) apiOptions.disableReasoningFormat = true;
if (hasValue(currentConfig.temperature)) if (hasValue(currentConfig.temperature))
apiOptions.temperature = Number(currentConfig.temperature); apiOptions.temperature = Number(currentConfig.temperature);
if (hasValue(currentConfig.max_tokens)) if (hasValue(currentConfig.max_tokens))

View File

@ -14,8 +14,12 @@ export interface SettingsFieldConfig {
export interface SettingsChatServiceOptions { export interface SettingsChatServiceOptions {
stream?: boolean; stream?: boolean;
// Model override (for regenerate with specific model) // Model (required in ROUTER mode, optional in MODEL mode)
model?: string; model?: string;
// System message to inject
systemMessage?: string;
// Disable reasoning format (use 'none' instead of 'auto')
disableReasoningFormat?: boolean;
// Generation parameters // Generation parameters
temperature?: number; temperature?: number;
max_tokens?: number; max_tokens?: number;