From a9a15a7f3f03c64f2709613c944a0ad5415a5165 Mon Sep 17 00:00:00 2001 From: Dorian Date: Tue, 3 Mar 2026 23:40:32 +0000 Subject: [PATCH] feat(chat): add temperature/params sliders & stop sequences (M9.9, M9.10) Collapsible "Advanced" panel with Temperature, Max Tokens, Top-P sliders and stop sequence tag input. Params persisted per conversation and passed to Claude API. Reset to defaults button. Co-Authored-By: Claude Opus 4.6 --- .../src/components/chat/AdvancedSettings.vue | 179 ++++++++++++++++++ .../app/src/components/chat/ChatWindow.vue | 2 + packages/app/src/composables/useAI.ts | 47 ++++- packages/core/src/types/message.ts | 6 + 4 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 packages/app/src/components/chat/AdvancedSettings.vue diff --git a/packages/app/src/components/chat/AdvancedSettings.vue b/packages/app/src/components/chat/AdvancedSettings.vue new file mode 100644 index 00000000..aaaadcf5 --- /dev/null +++ b/packages/app/src/components/chat/AdvancedSettings.vue @@ -0,0 +1,179 @@ + + + diff --git a/packages/app/src/components/chat/ChatWindow.vue b/packages/app/src/components/chat/ChatWindow.vue index 9ec31f00..901912ac 100644 --- a/packages/app/src/components/chat/ChatWindow.vue +++ b/packages/app/src/components/chat/ChatWindow.vue @@ -22,6 +22,7 @@ + void, @@ -156,6 +163,7 @@ async function streamClaude( systemPrompt: string, webSearch: boolean, signal?: AbortSignal, + params?: GenerationParams, ): Promise { const headers: Record = { 'Content-Type': 'application/json' } @@ -171,16 +179,22 @@ async function streamClaude( content: buildClaudeContent(m), })) + const body: Record = { + model: activeModel.value, + system: systemPrompt, + messages: apiMessages, + stream: true, + webSearch, + } + if (params?.temperature !== undefined) body.temperature = params.temperature + if (params?.maxTokens !== undefined) body.max_tokens = params.maxTokens + if (params?.topP !== undefined) body.top_p = params.topP + if (params?.stopSequences && params.stopSequences.length > 0) body.stop_sequences = params.stopSequences + const res = await fetch(CLAUDE_PATH, { method: 'POST', headers, - body: JSON.stringify({ - model: activeModel.value, - system: systemPrompt, - messages: apiMessages, - stream: true, - webSearch, - }), + body: JSON.stringify(body), signal, }) @@ -332,6 +346,17 @@ function buildSystemPrompt(chatStore: ReturnType): string { return prompt } +function getConversationParams(chatStore: ReturnType): GenerationParams { + const conv = chatStore.activeConversation + if (!conv) return {} + return { + temperature: conv.temperature, + maxTokens: conv.maxTokens, + topP: conv.topP, + stopSequences: conv.stopSequences, + } +} + let currentAbort: AbortController | null = null /** Background title generation after first exchange */ @@ -459,9 +484,11 @@ export function useAI() { chatStore.appendToLastMessage(cid, `⚠ ${err}`) } + const genParams = getConversationParams(chatStore) + try { if (provider === 'claude') { - await streamClaude(history, onToken, onError, systemPrompt, chatStore.webSearchEnabled, signal) + await streamClaude(history, onToken, onError, systemPrompt, chatStore.webSearchEnabled, signal, genParams) } else if (provider === 'openrouter') { await streamOpenRouter(history, onToken, onError, systemPrompt, signal) } else { @@ -565,9 +592,11 @@ export function useAI() { chatStore.appendToLastMessage(cid, `⚠ ${err}`) } + const genParams = getConversationParams(chatStore) + try { if (provider === 'claude') { - await streamClaude(history, onToken, onError, systemPrompt, chatStore.webSearchEnabled, signal) + await streamClaude(history, onToken, onError, systemPrompt, chatStore.webSearchEnabled, signal, genParams) } else if (provider === 'openrouter') { await streamOpenRouter(history, onToken, onError, systemPrompt, signal) } else { diff --git a/packages/core/src/types/message.ts b/packages/core/src/types/message.ts index 717b2a82..d7e273a7 100644 --- a/packages/core/src/types/message.ts +++ b/packages/core/src/types/message.ts @@ -58,4 +58,10 @@ export interface Conversation { childBranchIds?: string[] /** ID of the persona applied to this conversation */ personaId?: string + /** Generation params (persisted per conversation) */ + temperature?: number + maxTokens?: number + topP?: number + /** Comma-separated stop sequences */ + stopSequences?: string[] }