enhance context retention between tool calls, fix lints

This commit is contained in:
Josh Leverette 2025-12-15 09:29:10 -06:00
parent 0a428ff112
commit 1bbf328caf
6 changed files with 27 additions and 8 deletions

View File

@ -5,19 +5,22 @@
import { buttonVariants } from '$lib/components/ui/button/index.js'; import { buttonVariants } from '$lib/components/ui/button/index.js';
import { Card } from '$lib/components/ui/card'; import { Card } from '$lib/components/ui/card';
import { config } from '$lib/stores/settings.svelte'; import { config } from '$lib/stores/settings.svelte';
import type { Snippet } from 'svelte';
interface Props { interface Props {
class?: string; class?: string;
hasRegularContent?: boolean; hasRegularContent?: boolean;
isStreaming?: boolean; isStreaming?: boolean;
reasoningContent: string | null; reasoningContent: string | null;
children?: Snippet;
} }
let { let {
class: className = '', class: className = '',
hasRegularContent = false, hasRegularContent = false,
isStreaming = false, isStreaming = false,
reasoningContent reasoningContent,
children
}: Props = $props(); }: Props = $props();
const currentConfig = config(); const currentConfig = config();
@ -72,9 +75,11 @@
<div class="border-t border-muted px-3 pb-3"> <div class="border-t border-muted px-3 pb-3">
<div class="pt-3"> <div class="pt-3">
<div class="text-xs leading-relaxed break-words whitespace-pre-wrap"> <div class="text-xs leading-relaxed break-words whitespace-pre-wrap">
<slot> {#if children}
{@render children()}
{:else}
{reasoningContent ?? ''} {reasoningContent ?? ''}
</slot> {/if}
</div> </div>
</div> </div>
</div> </div>

View File

@ -119,6 +119,7 @@ export class ChatService {
messages: normalizedMessages.map((msg: ApiChatMessageData) => ({ messages: normalizedMessages.map((msg: ApiChatMessageData) => ({
role: msg.role, role: msg.role,
content: msg.content, content: msg.content,
...(msg.reasoning_content ? { reasoning_content: msg.reasoning_content } : {}),
...((msg as ApiChatCompletionRequestMessage).tool_call_id ...((msg as ApiChatCompletionRequestMessage).tool_call_id
? { tool_call_id: (msg as ApiChatCompletionRequestMessage).tool_call_id } ? { tool_call_id: (msg as ApiChatCompletionRequestMessage).tool_call_id }
: {}), : {}),
@ -602,6 +603,9 @@ export class ChatService {
return { return {
role: message.role as ChatRole, role: message.role as ChatRole,
content: message.content, content: message.content,
...(message.role === 'assistant' && message.thinking
? { reasoning_content: message.thinking }
: {}),
// tool_call_id is only relevant for tool role messages // tool_call_id is only relevant for tool role messages
...(message.toolCallId ? { tool_call_id: message.toolCallId } : {}), ...(message.toolCallId ? { tool_call_id: message.toolCallId } : {}),
...(toolCalls ? { tool_calls: toolCalls } : {}) ...(toolCalls ? { tool_calls: toolCalls } : {})
@ -693,6 +697,9 @@ export class ChatService {
return { return {
role: message.role as ChatRole, role: message.role as ChatRole,
content: contentParts, content: contentParts,
...(message.role === 'assistant' && message.thinking
? { reasoning_content: message.thinking }
: {}),
...(message.toolCallId ? { tool_call_id: message.toolCallId } : {}), ...(message.toolCallId ? { tool_call_id: message.toolCallId } : {}),
...(toolCalls ? { tool_calls: toolCalls } : {}) ...(toolCalls ? { tool_calls: toolCalls } : {})
}; };

View File

@ -35,6 +35,13 @@ export interface ApiChatMessageData {
role: ChatRole; role: ChatRole;
content: string | ApiChatMessageContentPart[]; content: string | ApiChatMessageContentPart[];
timestamp?: number; timestamp?: number;
/**
* Optional reasoning/thinking content to be sent back to the server.
*
* llama-server accepts this non-OpenAI field and uses it to preserve the model's
* internal "thinking" blocks across tool-call resumptions (notably for gpt-oss).
*/
reasoning_content?: string;
tool_call_id?: string; tool_call_id?: string;
tool_calls?: ApiChatCompletionToolCallDelta[]; tool_calls?: ApiChatCompletionToolCallDelta[];
} }
@ -183,6 +190,7 @@ export interface ApiLlamaCppServerProps {
export interface ApiChatCompletionRequestMessage { export interface ApiChatCompletionRequestMessage {
role: ChatRole; role: ChatRole;
content: string | ApiChatMessageContentPart[]; content: string | ApiChatMessageContentPart[];
reasoning_content?: string;
tool_call_id?: string; tool_call_id?: string;
tool_calls?: ApiChatCompletionToolCallDelta[]; tool_calls?: ApiChatCompletionToolCallDelta[];
} }

View File

@ -75,9 +75,8 @@ describe('ChatMessage delete for merged assistant messages', () => {
conversationsStore.activeMessages = allMessages; conversationsStore.activeMessages = allMessages;
// Avoid touching IndexedDB by stubbing the store call used by getDeletionInfo. // Avoid touching IndexedDB by stubbing the store call used by getDeletionInfo.
const originalGetConversationMessages = conversationsStore.getConversationMessages.bind( const originalGetConversationMessages =
conversationsStore conversationsStore.getConversationMessages.bind(conversationsStore);
);
conversationsStore.getConversationMessages = async () => allMessages; conversationsStore.getConversationMessages = async () => allMessages;
const onDelete = vi.fn(); const onDelete = vi.fn();
@ -111,4 +110,3 @@ describe('ChatMessage delete for merged assistant messages', () => {
} }
}); });
}); });

View File

@ -14,4 +14,3 @@
<ChatMessage message={props.message} onDelete={props.onDelete} /> <ChatMessage message={props.message} onDelete={props.onDelete} />
</Sidebar.Provider> </Sidebar.Provider>
</Tooltip.Provider> </Tooltip.Provider>

View File

@ -137,4 +137,6 @@ test('tool output does not echo tool arguments back to the model', async ({ page
); );
expect(assistantWithToolCall).toBeTruthy(); expect(assistantWithToolCall).toBeTruthy();
expect(JSON.stringify(assistantWithToolCall?.tool_calls ?? null)).toContain('LARGE_CODE_BEGIN'); expect(JSON.stringify(assistantWithToolCall?.tool_calls ?? null)).toContain('LARGE_CODE_BEGIN');
// Preserve the model's reasoning across tool-call resumptions (required for gpt-oss).
expect(String(assistantWithToolCall?.reasoning_content ?? '')).toContain('reasoning-step-1');
}); });