diff --git a/backend/functions/letta_agents.py b/backend/functions/letta_agents.py index 580c12b51..d63993c7a 100644 --- a/backend/functions/letta_agents.py +++ b/backend/functions/letta_agents.py @@ -141,12 +141,17 @@ class Pipe: return "".join(content_parts) if content_parts else "No response" def _stream_response(self, client, agent_id: str, messages: list) -> Generator: - """Stream response from Letta agent with tool call support.""" + """Stream response from Letta agent with tool call and reasoning support.""" import html + import time # Track tool calls: {tool_call_id: {"name": str, "arguments": str}} tool_calls = {} + # Track reasoning: accumulate content and emit when complete + reasoning_content = "" + reasoning_start = None + with client.agents.messages.stream( agent_id=agent_id, messages=messages, @@ -155,7 +160,23 @@ class Pipe: for chunk in stream: msg_type = getattr(chunk, 'message_type', None) - if msg_type == 'tool_call_message': + # Emit accumulated reasoning when transitioning to non-reasoning message + if reasoning_content and msg_type != 'reasoning_message': + duration = int(time.time() - reasoning_start) if reasoning_start else 0 + yield f'
\n' + yield f'{html.escape(reasoning_content)}\n' + yield '
\n\n' + reasoning_content = "" + reasoning_start = None + + if msg_type == 'reasoning_message': + reasoning = getattr(chunk, 'reasoning', '') + if reasoning: + if reasoning_start is None: + reasoning_start = time.time() + reasoning_content += reasoning + + elif msg_type == 'tool_call_message': # Handle tool call chunks - accumulate but don't emit yet tool_call = getattr(chunk, 'tool_call', None) if tool_call: @@ -200,3 +221,10 @@ class Pipe: # Stream assistant content if hasattr(chunk, 'content') and chunk.content: yield chunk.content + + # Handle case where reasoning was the last message + if reasoning_content: + duration = int(time.time() - reasoning_start) if reasoning_start else 0 + yield f'
\n' + yield f'{html.escape(reasoning_content)}\n' + yield '
\n\n' diff --git a/src/lib/components/common/Collapsible.svelte b/src/lib/components/common/Collapsible.svelte index 8f788c6d8..4df06fa33 100644 --- a/src/lib/components/common/Collapsible.svelte +++ b/src/lib/components/common/Collapsible.svelte @@ -246,18 +246,18 @@ {#if attributes?.type === 'reasoning'} {#if attributes?.done === 'true' && attributes?.duration} {#if attributes.duration < 1} - {$i18n.t('Thought for less than a second')} + {$i18n.t('Reasoned for less than a second')} {:else if attributes.duration < 60} - {$i18n.t('Thought for {{DURATION}} seconds', { + {$i18n.t('Reasoned for {{DURATION}} seconds', { DURATION: attributes.duration })} {:else} - {$i18n.t('Thought for {{DURATION}}', { + {$i18n.t('Reasoned for {{DURATION}}', { DURATION: dayjs.duration(attributes.duration, 'seconds').humanize() })} {/if} {:else} - {$i18n.t('Thinking...')} + {$i18n.t('Reasoning...')} {/if} {:else if attributes?.type === 'code_interpreter'} {#if attributes?.done === 'true'}