Keep Assistant persisted tools before answers

This commit is contained in:
rcourtman
2026-06-07 12:36:34 +01:00
parent bc864074d6
commit 063fdbadee
4 changed files with 37 additions and 9 deletions
@@ -153,6 +153,34 @@ describe('Assistant transcript export', () => {
expect(transcript).toContain('No active alerts');
});
it('exports persisted tool calls before reconstructed assistant answer text', () => {
const transcript = formatAssistantTranscript({
messages: [
{
id: 'assistant-1',
role: 'assistant',
content: 'I checked the resource.',
timestamp,
toolCalls: [
{
name: 'pulse_read',
input: JSON.stringify({ target: 'vm-100' }),
output: 'Resource is running',
success: true,
},
],
},
],
generatedAt: timestamp,
});
const toolIndex = transcript.indexOf('[tool:read]');
const answerIndex = transcript.indexOf('I checked the resource.');
expect(toolIndex).toBeGreaterThan(-1);
expect(answerIndex).toBeGreaterThan(-1);
expect(toolIndex).toBeLessThan(answerIndex);
});
it('formats initially selected model route events distinctly from fallback switches', () => {
const transcript = formatAssistantTranscript({
messages: [
@@ -2753,11 +2753,11 @@ describe('useChat', () => {
expect(msgs[0].content).toBe('hello');
expect(msgs[1].toolCalls).toHaveLength(1);
expect(msgs[1].streamEvents).toEqual([
{ type: 'content', content: 'hi there' },
{
type: 'tool',
tool: { name: 'test', input: '{}', output: 'ok', success: true },
},
{ type: 'content', content: 'hi there' },
]);
expect(msgs[1].timestamp).toBeInstanceOf(Date);
dispose();
@@ -2873,7 +2873,6 @@ describe('useChat', () => {
expect(assistant.model).toBe('openai:gpt-4');
expect(assistant.content).toBe('I checked the resource.');
expect(assistant.streamEvents).toEqual([
{ type: 'content', content: 'I checked the resource.' },
{
type: 'tool',
tool: {
@@ -2883,6 +2882,7 @@ describe('useChat', () => {
success: true,
},
},
{ type: 'content', content: 'I checked the resource.' },
]);
dispose();
});
@@ -1135,15 +1135,15 @@ export function useChat(options: UseChatOptions = {}) {
toolCalls?: PersistedToolCall[],
): StreamDisplayEvent[] | undefined => {
const events: StreamDisplayEvent[] = [];
if (content.trim()) {
events.push({ type: 'content', content });
}
for (const tool of toolCalls || []) {
events.push({
type: 'tool',
tool: normalizePersistedToolExecution(tool),
});
}
if (content.trim()) {
events.push({ type: 'content', content });
}
return events.length > 0 ? events : undefined;
};
@@ -287,16 +287,16 @@ const appendAssistantStreamEvents = (
}
}
if (!contentAppended) {
appendBlock(lines, visibleAssistantContent(message));
}
if (!toolAppended) {
for (const tool of message.toolCalls || []) {
appendBlock(lines, formatCompletedTool(tool, options.includeToolOutput === true));
}
}
if (!contentAppended) {
appendBlock(lines, visibleAssistantContent(message));
}
if (!pendingToolAppended) {
for (const tool of message.pendingTools || []) {
appendBlock(lines, formatPendingTool(tool));