Henri Bonamy commited on
Commit
72af187
·
1 Parent(s): 35dc01a

improved tool calling, push to hub, etc.

Browse files
frontend/src/components/Chat/ApprovalFlow.tsx CHANGED
@@ -118,6 +118,10 @@ export default function ApprovalFlow({ message }: ApprovalFlowProps) {
118
 
119
  const currentTool = batch.tools[currentIndex];
120
 
 
 
 
 
121
  const getToolDescription = (toolName: string, args: any) => {
122
  if (toolName === 'hf_jobs') {
123
  return (
@@ -217,6 +221,12 @@ export default function ApprovalFlow({ message }: ApprovalFlowProps) {
217
  <OpenInNewIcon sx={{ fontSize: 16, color: 'var(--muted-text)', opacity: 0.7 }} />
218
  </Box>
219
 
 
 
 
 
 
 
220
  {showLogsButton && (
221
  <Button
222
  variant="outlined"
 
118
 
119
  const currentTool = batch.tools[currentIndex];
120
 
121
+ // Check if script contains push_to_hub or upload_file
122
+ const args = currentTool.arguments as any;
123
+ const containsPushToHub = currentTool.tool === 'hf_jobs' && args.script && (args.script.includes('push_to_hub') || args.script.includes('upload_file'));
124
+
125
  const getToolDescription = (toolName: string, args: any) => {
126
  if (toolName === 'hf_jobs') {
127
  return (
 
221
  <OpenInNewIcon sx={{ fontSize: 16, color: 'var(--muted-text)', opacity: 0.7 }} />
222
  </Box>
223
 
224
+ {containsPushToHub && (
225
+ <Typography variant="caption" sx={{ color: 'var(--accent-green)', fontSize: '0.75rem', opacity: 0.8, px: 0.5 }}>
226
+ We've detected the result will be pushed to hub.
227
+ </Typography>
228
+ )}
229
+
230
  {showLogsButton && (
231
  <Button
232
  variant="outlined"
frontend/src/components/Chat/MessageBubble.tsx CHANGED
@@ -63,34 +63,6 @@ export default function MessageBubble({ message }: MessageBubbleProps) {
63
  </Box>
64
  )}
65
 
66
- {/* Persisted Trace Logs */}
67
- {message.trace && message.trace.length > 0 && (
68
- <Box
69
- sx={{
70
- bgcolor: 'rgba(0,0,0,0.3)',
71
- borderRadius: 1,
72
- p: 1.5,
73
- border: 1,
74
- borderColor: 'rgba(255,255,255,0.05)',
75
- width: '100%',
76
- mb: 2,
77
- }}
78
- >
79
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
80
- {message.trace.map((log) => (
81
- <Typography
82
- key={log.id}
83
- variant="caption"
84
- component="div"
85
- sx={{ color: 'var(--muted-text)', fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, monospace', fontSize: '0.75rem' }}
86
- >
87
- &gt; {log.text}
88
- </Typography>
89
- ))}
90
- </Box>
91
- </Box>
92
- )}
93
-
94
  <Box
95
  sx={{
96
  '& p': { m: 0, color: isUser ? 'var(--text)' : 'var(--text)' }, // User might want different text color? Defaults to --text
@@ -147,7 +119,56 @@ export default function MessageBubble({ message }: MessageBubbleProps) {
147
  >
148
  <ReactMarkdown remarkPlugins={[remarkGfm]}>{message.content}</ReactMarkdown>
149
  </Box>
150
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  <Typography className="meta" variant="caption" sx={{ display: 'block', textAlign: 'right', mt: 1, fontSize: '11px', opacity: 0.5 }}>
152
  {new Date(message.timestamp).toLocaleTimeString()}
153
  </Typography>
 
63
  </Box>
64
  )}
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  <Box
67
  sx={{
68
  '& p': { m: 0, color: isUser ? 'var(--text)' : 'var(--text)' }, // User might want different text color? Defaults to --text
 
119
  >
120
  <ReactMarkdown remarkPlugins={[remarkGfm]}>{message.content}</ReactMarkdown>
121
  </Box>
122
+
123
+ {/* Persisted Trace Logs - Now at the bottom */}
124
+ {message.trace && message.trace.length > 0 && (
125
+ <Box
126
+ sx={{
127
+ bgcolor: 'rgba(0,0,0,0.3)',
128
+ borderRadius: 1,
129
+ p: 1.5,
130
+ border: 1,
131
+ borderColor: 'rgba(255,255,255,0.05)',
132
+ width: '100%',
133
+ mt: 2,
134
+ }}
135
+ >
136
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
137
+ {message.trace.map((log) => {
138
+ // Extract tool name from text "Agent is executing {toolName}..."
139
+ const match = log.text.match(/Agent is executing (.+)\.\.\./);
140
+ const toolName = match ? match[1] : log.tool;
141
+
142
+ return (
143
+ <Typography
144
+ key={log.id}
145
+ variant="caption"
146
+ component="div"
147
+ sx={{
148
+ color: 'var(--muted-text)',
149
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, monospace',
150
+ fontSize: '0.75rem',
151
+ display: 'flex',
152
+ alignItems: 'center',
153
+ gap: 0.5,
154
+ }}
155
+ >
156
+ <span style={{ color: log.completed ? '#FDB022' : 'inherit' }}>*</span>
157
+ <span>Agent is executing </span>
158
+ <span style={{
159
+ fontWeight: 600,
160
+ color: 'rgba(255, 255, 255, 0.9)',
161
+ }}>
162
+ {toolName}
163
+ </span>
164
+ <span>...</span>
165
+ </Typography>
166
+ );
167
+ })}
168
+ </Box>
169
+ </Box>
170
+ )}
171
+
172
  <Typography className="meta" variant="caption" sx={{ display: 'block', textAlign: 'right', mt: 1, fontSize: '11px', opacity: 0.5 }}>
173
  {new Date(message.timestamp).toLocaleTimeString()}
174
  </Typography>
frontend/src/components/Chat/MessageList.tsx CHANGED
@@ -1,6 +1,5 @@
1
  import { useEffect, useRef } from 'react';
2
  import { Box, Typography } from '@mui/material';
3
- import { useAgentStore } from '@/store/agentStore';
4
  import { useSessionStore } from '@/store/sessionStore';
5
  import MessageBubble from './MessageBubble';
6
  import type { Message } from '@/types/agent';
@@ -40,21 +39,12 @@ const TechnicalIndicator = () => (
40
 
41
  export default function MessageList({ messages, isProcessing }: MessageListProps) {
42
  const bottomRef = useRef<HTMLDivElement>(null);
43
- const traceBoxRef = useRef<HTMLDivElement>(null);
44
- const { traceLogs } = useAgentStore();
45
  const { activeSessionId } = useSessionStore();
46
 
47
  // Auto-scroll to bottom when new messages arrive
48
  useEffect(() => {
49
  bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
50
- }, [messages, isProcessing, traceLogs]);
51
-
52
- // Auto-scroll trace box
53
- useEffect(() => {
54
- if (traceBoxRef.current) {
55
- traceBoxRef.current.scrollTop = traceBoxRef.current.scrollHeight;
56
- }
57
- }, [traceLogs]);
58
 
59
  return (
60
  <Box
@@ -67,7 +57,7 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
67
  }}
68
  >
69
  <Box sx={{ maxWidth: 'md', mx: 'auto', width: '100%', display: 'flex', flexDirection: 'column', gap: 2 }}>
70
- {messages.length === 0 && traceLogs.length === 0 && !isProcessing ? (
71
  <Box
72
  sx={{
73
  flex: 1,
@@ -95,37 +85,6 @@ export default function MessageList({ messages, isProcessing }: MessageListProps
95
  </Typography>
96
  <TechnicalIndicator />
97
  </Box>
98
-
99
- {traceLogs.length > 0 && (
100
- <Box
101
- sx={{
102
- bgcolor: 'background.default',
103
- borderRadius: 1,
104
- p: 2,
105
- border: 1,
106
- borderColor: 'divider',
107
- width: '100%',
108
- fontFamily: 'monospace',
109
- maxHeight: 120,
110
- overflowY: 'auto',
111
- }}
112
- ref={traceBoxRef}
113
- >
114
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
115
- {traceLogs.map((log) => (
116
- <Box key={log.id}>
117
- <Typography
118
- variant="caption"
119
- component="div"
120
- sx={{ color: 'common.white', fontFamily: 'monospace' }}
121
- >
122
- &gt; {log.text}
123
- </Typography>
124
- </Box>
125
- ))}
126
- </Box>
127
- </Box>
128
- )}
129
  </Box>
130
  )}
131
 
 
1
  import { useEffect, useRef } from 'react';
2
  import { Box, Typography } from '@mui/material';
 
3
  import { useSessionStore } from '@/store/sessionStore';
4
  import MessageBubble from './MessageBubble';
5
  import type { Message } from '@/types/agent';
 
39
 
40
  export default function MessageList({ messages, isProcessing }: MessageListProps) {
41
  const bottomRef = useRef<HTMLDivElement>(null);
 
 
42
  const { activeSessionId } = useSessionStore();
43
 
44
  // Auto-scroll to bottom when new messages arrive
45
  useEffect(() => {
46
  bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
47
+ }, [messages, isProcessing]);
 
 
 
 
 
 
 
48
 
49
  return (
50
  <Box
 
57
  }}
58
  >
59
  <Box sx={{ maxWidth: 'md', mx: 'auto', width: '100%', display: 'flex', flexDirection: 'column', gap: 2 }}>
60
+ {messages.length === 0 && !isProcessing ? (
61
  <Box
62
  sx={{
63
  flex: 1,
 
85
  </Typography>
86
  <TechnicalIndicator />
87
  </Box>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  </Box>
89
  )}
90
 
frontend/src/components/SessionSidebar/SessionSidebar.tsx CHANGED
@@ -47,18 +47,21 @@ const RunningIndicator = () => (
47
  export default function SessionSidebar({ onClose }: SessionSidebarProps) {
48
  const { sessions, activeSessionId, createSession, deleteSession, switchSession } =
49
  useSessionStore();
50
- const { clearMessages, isConnected, isProcessing } = useAgentStore();
51
 
52
  const handleNewSession = useCallback(async () => {
53
  try {
54
  const response = await fetch('/api/session', { method: 'POST' });
55
  const data = await response.json();
56
  createSession(data.session_id);
 
 
 
57
  onClose?.();
58
  } catch (e) {
59
  console.error('Failed to create session:', e);
60
  }
61
- }, [createSession, onClose]);
62
 
63
  const handleDeleteSession = useCallback(
64
  async (sessionId: string, e: React.MouseEvent) => {
@@ -77,9 +80,12 @@ export default function SessionSidebar({ onClose }: SessionSidebarProps) {
77
  const handleSelectSession = useCallback(
78
  (sessionId: string) => {
79
  switchSession(sessionId);
 
 
 
80
  onClose?.();
81
  },
82
- [switchSession, onClose]
83
  );
84
 
85
  const handleUndo = useCallback(async () => {
 
47
  export default function SessionSidebar({ onClose }: SessionSidebarProps) {
48
  const { sessions, activeSessionId, createSession, deleteSession, switchSession } =
49
  useSessionStore();
50
+ const { clearMessages, isConnected, isProcessing, setPlan, setPanelContent } = useAgentStore();
51
 
52
  const handleNewSession = useCallback(async () => {
53
  try {
54
  const response = await fetch('/api/session', { method: 'POST' });
55
  const data = await response.json();
56
  createSession(data.session_id);
57
+ // Clear plan and code panel for new session
58
+ setPlan([]);
59
+ setPanelContent(null);
60
  onClose?.();
61
  } catch (e) {
62
  console.error('Failed to create session:', e);
63
  }
64
+ }, [createSession, setPlan, setPanelContent, onClose]);
65
 
66
  const handleDeleteSession = useCallback(
67
  async (sessionId: string, e: React.MouseEvent) => {
 
80
  const handleSelectSession = useCallback(
81
  (sessionId: string) => {
82
  switchSession(sessionId);
83
+ // Clear plan and code panel when switching sessions
84
+ setPlan([]);
85
+ setPanelContent(null);
86
  onClose?.();
87
  },
88
+ [switchSession, setPlan, setPanelContent, onClose]
89
  );
90
 
91
  const handleUndo = useCallback(async () => {
frontend/src/hooks/useAgentWebSocket.ts CHANGED
@@ -25,15 +25,19 @@ export function useAgentWebSocket({
25
 
26
  const {
27
  addMessage,
 
28
  setProcessing,
29
  setConnected,
30
  setPendingApprovals,
31
  setError,
32
  addTraceLog,
 
33
  clearTraceLogs,
34
  setPanelContent,
35
  setPlan,
36
  traceLogs,
 
 
37
  } = useAgentStore();
38
 
39
  const { setRightPanelOpen, setLeftSidebarOpen } = useLayoutStore();
@@ -55,34 +59,61 @@ export function useAgentWebSocket({
55
  case 'processing':
56
  setProcessing(true);
57
  clearTraceLogs();
 
58
  break;
59
 
60
  case 'assistant_message': {
61
  const content = (event.data?.content as string) || '';
62
  const currentTrace = useAgentStore.getState().traceLogs;
63
- const message: Message = {
64
- id: `msg_${Date.now()}`,
65
- role: 'assistant',
66
- content,
67
- timestamp: new Date().toISOString(),
68
- trace: currentTrace.length > 0 ? [...currentTrace] : undefined,
69
- };
70
- addMessage(sessionId, message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  break;
72
  }
73
 
74
  case 'tool_call': {
75
  const toolName = (event.data?.tool as string) || 'unknown';
76
  const args = (event.data?.arguments as Record<string, any>) || {};
77
- const log: TraceLog = {
78
- id: `tool_${Date.now()}`,
79
- type: 'call',
80
- text: `Calling ${toolName} with ${JSON.stringify(args)}`,
81
- tool: toolName,
82
- timestamp: new Date().toISOString(),
83
- };
84
- addTraceLog(log);
85
-
 
 
 
 
 
 
 
86
  // Auto-expand Right Panel for specific tools
87
  if (toolName === 'hf_jobs' && (args.operation === 'run' || args.operation === 'scheduled run') && args.script) {
88
  setPanelContent({
@@ -111,18 +142,21 @@ export function useAgentWebSocket({
111
  const toolName = (event.data?.tool as string) || 'unknown';
112
  const output = (event.data?.output as string) || '';
113
  const success = event.data?.success as boolean;
114
-
 
 
 
 
 
 
115
  if (toolName === 'hf_jobs') {
116
- // Find the last message with approval (likely the one that triggered this job)
117
  const messages = useAgentStore.getState().getMessages(sessionId);
118
- // Reverse to find the most recent one
119
  const lastApprovalMsg = [...messages].reverse().find(m => m.approval);
120
-
121
  if (lastApprovalMsg) {
122
- // Append output if there's already some (for multiple jobs in batch)
123
  const currentOutput = lastApprovalMsg.toolOutput || '';
124
  const newOutput = currentOutput ? currentOutput + '\n\n' + output : output;
125
-
126
  useAgentStore.getState().updateMessage(sessionId, lastApprovalMsg.id, {
127
  toolOutput: newOutput
128
  });
@@ -130,19 +164,9 @@ export function useAgentWebSocket({
130
  } else {
131
  console.warn('Received hf_jobs output but no approval message found to update.');
132
  }
133
- // CRITICAL: Always break for hf_jobs to prevent a separate "Tool" bubble from appearing
134
- break;
135
  }
136
-
137
- const message: Message = {
138
- id: `msg_tool_${Date.now()}`,
139
- role: 'tool',
140
- content: output,
141
- timestamp: new Date().toISOString(),
142
- toolName: toolName,
143
- };
144
- addMessage(sessionId, message);
145
-
146
  console.log('Tool output:', toolName, success);
147
  break;
148
  }
@@ -218,6 +242,7 @@ export function useAgentWebSocket({
218
 
219
  case 'turn_complete':
220
  setProcessing(false);
 
221
  break;
222
 
223
  case 'compacted': {
 
25
 
26
  const {
27
  addMessage,
28
+ updateMessage,
29
  setProcessing,
30
  setConnected,
31
  setPendingApprovals,
32
  setError,
33
  addTraceLog,
34
+ updateTraceLog,
35
  clearTraceLogs,
36
  setPanelContent,
37
  setPlan,
38
  traceLogs,
39
+ setCurrentTurnMessageId,
40
+ updateCurrentTurnTrace,
41
  } = useAgentStore();
42
 
43
  const { setRightPanelOpen, setLeftSidebarOpen } = useLayoutStore();
 
59
  case 'processing':
60
  setProcessing(true);
61
  clearTraceLogs();
62
+ setCurrentTurnMessageId(null); // Start a new turn
63
  break;
64
 
65
  case 'assistant_message': {
66
  const content = (event.data?.content as string) || '';
67
  const currentTrace = useAgentStore.getState().traceLogs;
68
+ const currentTurnMsgId = useAgentStore.getState().currentTurnMessageId;
69
+
70
+ if (currentTurnMsgId) {
71
+ // Update existing message - append content and update trace
72
+ const messages = useAgentStore.getState().getMessages(sessionId);
73
+ const existingMsg = messages.find(m => m.id === currentTurnMsgId);
74
+
75
+ if (existingMsg) {
76
+ const newContent = existingMsg.content ? existingMsg.content + '\n\n' + content : content;
77
+ updateMessage(sessionId, currentTurnMsgId, {
78
+ content: newContent,
79
+ trace: currentTrace.length > 0 ? [...currentTrace] : undefined,
80
+ });
81
+ }
82
+ } else {
83
+ // Create new message
84
+ const messageId = `msg_${Date.now()}`;
85
+ const message: Message = {
86
+ id: messageId,
87
+ role: 'assistant',
88
+ content,
89
+ timestamp: new Date().toISOString(),
90
+ trace: currentTrace.length > 0 ? [...currentTrace] : undefined,
91
+ };
92
+ addMessage(sessionId, message);
93
+ setCurrentTurnMessageId(messageId);
94
+ }
95
  break;
96
  }
97
 
98
  case 'tool_call': {
99
  const toolName = (event.data?.tool as string) || 'unknown';
100
  const args = (event.data?.arguments as Record<string, any>) || {};
101
+
102
+ // Don't display plan_tool in trace logs (it shows up elsewhere in the UI)
103
+ if (toolName !== 'plan_tool') {
104
+ const log: TraceLog = {
105
+ id: `tool_${Date.now()}`,
106
+ type: 'call',
107
+ text: `Agent is executing ${toolName}...`,
108
+ tool: toolName,
109
+ timestamp: new Date().toISOString(),
110
+ completed: false,
111
+ };
112
+ addTraceLog(log);
113
+ // Update the current turn message's trace in real-time
114
+ updateCurrentTurnTrace(sessionId);
115
+ }
116
+
117
  // Auto-expand Right Panel for specific tools
118
  if (toolName === 'hf_jobs' && (args.operation === 'run' || args.operation === 'scheduled run') && args.script) {
119
  setPanelContent({
 
142
  const toolName = (event.data?.tool as string) || 'unknown';
143
  const output = (event.data?.output as string) || '';
144
  const success = event.data?.success as boolean;
145
+
146
+ // Mark the corresponding trace log as completed
147
+ updateTraceLog(toolName, { completed: true });
148
+ // Update the current turn message's trace in real-time
149
+ updateCurrentTurnTrace(sessionId);
150
+
151
+ // Special handling for hf_jobs - update the approval message with output
152
  if (toolName === 'hf_jobs') {
 
153
  const messages = useAgentStore.getState().getMessages(sessionId);
 
154
  const lastApprovalMsg = [...messages].reverse().find(m => m.approval);
155
+
156
  if (lastApprovalMsg) {
 
157
  const currentOutput = lastApprovalMsg.toolOutput || '';
158
  const newOutput = currentOutput ? currentOutput + '\n\n' + output : output;
159
+
160
  useAgentStore.getState().updateMessage(sessionId, lastApprovalMsg.id, {
161
  toolOutput: newOutput
162
  });
 
164
  } else {
165
  console.warn('Received hf_jobs output but no approval message found to update.');
166
  }
 
 
167
  }
168
+
169
+ // Don't create message bubbles for tool outputs - they only show in trace logs
 
 
 
 
 
 
 
 
170
  console.log('Tool output:', toolName, success);
171
  break;
172
  }
 
242
 
243
  case 'turn_complete':
244
  setProcessing(false);
245
+ setCurrentTurnMessageId(null); // Clear the current turn
246
  break;
247
 
248
  case 'compacted': {
frontend/src/store/agentStore.ts CHANGED
@@ -18,6 +18,7 @@ interface AgentStore {
18
  traceLogs: TraceLog[];
19
  panelContent: { title: string; content: string; language?: string; parameters?: any } | null;
20
  plan: PlanItem[];
 
21
 
22
  // Actions
23
  addMessage: (sessionId: string, message: Message) => void;
@@ -30,9 +31,12 @@ interface AgentStore {
30
  setError: (error: string | null) => void;
31
  getMessages: (sessionId: string) => Message[];
32
  addTraceLog: (log: TraceLog) => void;
 
33
  clearTraceLogs: () => void;
34
  setPanelContent: (content: { title: string; content: string; language?: string; parameters?: any } | null) => void;
35
  setPlan: (plan: PlanItem[]) => void;
 
 
36
  }
37
 
38
  export const useAgentStore = create<AgentStore>((set, get) => ({
@@ -45,6 +49,7 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
45
  traceLogs: [],
46
  panelContent: null,
47
  plan: [],
 
48
 
49
  addMessage: (sessionId: string, message: Message) => {
50
  set((state) => {
@@ -112,6 +117,20 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
112
  }));
113
  },
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  clearTraceLogs: () => {
116
  set({ traceLogs: [] });
117
  },
@@ -123,4 +142,26 @@ export const useAgentStore = create<AgentStore>((set, get) => ({
123
  setPlan: (plan: PlanItem[]) => {
124
  set({ plan });
125
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  }));
 
18
  traceLogs: TraceLog[];
19
  panelContent: { title: string; content: string; language?: string; parameters?: any } | null;
20
  plan: PlanItem[];
21
+ currentTurnMessageId: string | null; // Track the current turn's assistant message
22
 
23
  // Actions
24
  addMessage: (sessionId: string, message: Message) => void;
 
31
  setError: (error: string | null) => void;
32
  getMessages: (sessionId: string) => Message[];
33
  addTraceLog: (log: TraceLog) => void;
34
+ updateTraceLog: (toolName: string, updates: Partial<TraceLog>) => void;
35
  clearTraceLogs: () => void;
36
  setPanelContent: (content: { title: string; content: string; language?: string; parameters?: any } | null) => void;
37
  setPlan: (plan: PlanItem[]) => void;
38
+ setCurrentTurnMessageId: (id: string | null) => void;
39
+ updateCurrentTurnTrace: (sessionId: string) => void;
40
  }
41
 
42
  export const useAgentStore = create<AgentStore>((set, get) => ({
 
49
  traceLogs: [],
50
  panelContent: null,
51
  plan: [],
52
+ currentTurnMessageId: null,
53
 
54
  addMessage: (sessionId: string, message: Message) => {
55
  set((state) => {
 
117
  }));
118
  },
119
 
120
+ updateTraceLog: (toolName: string, updates: Partial<TraceLog>) => {
121
+ set((state) => {
122
+ // Find the last trace log with this tool name and update it
123
+ const traceLogs = [...state.traceLogs];
124
+ for (let i = traceLogs.length - 1; i >= 0; i--) {
125
+ if (traceLogs[i].tool === toolName && traceLogs[i].type === 'call') {
126
+ traceLogs[i] = { ...traceLogs[i], ...updates };
127
+ break;
128
+ }
129
+ }
130
+ return { traceLogs };
131
+ });
132
+ },
133
+
134
  clearTraceLogs: () => {
135
  set({ traceLogs: [] });
136
  },
 
142
  setPlan: (plan: PlanItem[]) => {
143
  set({ plan });
144
  },
145
+
146
+ setCurrentTurnMessageId: (id: string | null) => {
147
+ set({ currentTurnMessageId: id });
148
+ },
149
+
150
+ updateCurrentTurnTrace: (sessionId: string) => {
151
+ const state = get();
152
+ if (state.currentTurnMessageId) {
153
+ const currentMessages = state.messagesBySession[sessionId] || [];
154
+ const updatedMessages = currentMessages.map((msg) =>
155
+ msg.id === state.currentTurnMessageId
156
+ ? { ...msg, trace: state.traceLogs.length > 0 ? [...state.traceLogs] : undefined }
157
+ : msg
158
+ );
159
+ set({
160
+ messagesBySession: {
161
+ ...state.messagesBySession,
162
+ [sessionId]: updatedMessages,
163
+ },
164
+ });
165
+ }
166
+ },
167
  }));
frontend/src/types/agent.ts CHANGED
@@ -54,6 +54,7 @@ export interface TraceLog {
54
  text: string;
55
  tool: string;
56
  timestamp: string;
 
57
  }
58
 
59
  export interface User {
 
54
  text: string;
55
  tool: string;
56
  timestamp: string;
57
+ completed?: boolean;
58
  }
59
 
60
  export interface User {