Skip to content

Commit d63b347

Browse files
committed
Merge branch 'dev'
2 parents 5746064 + 24b6f4f commit d63b347

22 files changed

+2639
-858
lines changed

app/src/components/AgentActivityModal.tsx renamed to app/src/components/AgentCard/AgentActivityModal.tsx

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// components/AgentActivityModal.tsx
2-
import React from 'react';
3-
import { X, Activity } from 'lucide-react';
2+
import React, { useState } from 'react';
3+
import { X, Activity, Database } from 'lucide-react';
44
import AgentLogViewer from './AgentLogViewer';
5+
import IterationStoreDebug from './IterationStoreDebug';
6+
import FeedbackBubble from '../FeedbackBubble';
57

68
interface AgentActivityModalProps {
79
isOpen: boolean;
@@ -20,6 +22,8 @@ const AgentActivityModal: React.FC<AgentActivityModalProps> = ({
2022
getToken,
2123
isAuthenticated
2224
}) => {
25+
const [activeTab, setActiveTab] = useState<'logs' | 'debug'>('logs');
26+
2327
if (!isOpen) return null;
2428

2529
// Handle backdrop click to close modal
@@ -63,24 +67,52 @@ const AgentActivityModal: React.FC<AgentActivityModalProps> = ({
6367
Agent Activity - "{agentName}"
6468
</h2>
6569
</div>
66-
<button
67-
onClick={onClose}
68-
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
69-
aria-label="Close modal"
70-
>
71-
<X className="h-5 w-5 text-gray-500" />
72-
</button>
70+
<div className="flex items-center gap-3">
71+
{/* FeedbackBubble in header */}
72+
<div className="mr-2">
73+
<FeedbackBubble
74+
agentId={agentId}
75+
getToken={getToken}
76+
isAuthenticated={isAuthenticated}
77+
/>
78+
</div>
79+
<button
80+
onClick={onClose}
81+
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
82+
aria-label="Close modal"
83+
>
84+
<X className="h-5 w-5 text-gray-500" />
85+
</button>
86+
</div>
7387
</div>
7488

89+
7590
{/* Modal Content */}
76-
<div className="flex-1 overflow-y-auto p-6">
77-
<AgentLogViewer
78-
agentId={agentId}
79-
getToken={getToken}
80-
isAuthenticated={isAuthenticated}
81-
maxHeight="none"
82-
maxEntries={100} // More entries in modal
83-
/>
91+
<div className="flex-1 overflow-y-auto p-6 relative">
92+
{activeTab === 'logs' ? (
93+
<AgentLogViewer
94+
agentId={agentId}
95+
getToken={getToken}
96+
isAuthenticated={isAuthenticated}
97+
maxHeight="none"
98+
maxEntries={100} // More entries in modal
99+
/>
100+
) : (
101+
<IterationStoreDebug agentId={agentId} />
102+
)}
103+
104+
{/* Power User Debug Button - Lower Right */}
105+
<button
106+
onClick={() => setActiveTab(activeTab === 'debug' ? 'logs' : 'debug')}
107+
className={`fixed bottom-6 right-6 p-2 rounded-full shadow-lg transition-all duration-200 ${
108+
activeTab === 'debug'
109+
? 'bg-blue-600 text-white hover:bg-blue-700'
110+
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
111+
}`}
112+
title={activeTab === 'debug' ? 'Hide Debug' : 'Show Debug (Power Users)'}
113+
>
114+
<Database className="h-4 w-4" />
115+
</button>
84116
</div>
85117
</div>
86118
</div>

app/src/components/AgentCard/AgentCard.tsx

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { CompleteAgent } from '@utils/agent_database';
55
import { isJupyterConnected } from '@utils/handlers/JupyterConfig';
66
import { listModels } from '@utils/ollamaServer';
77
import { getOllamaServerAddress } from '@utils/main_loop';
8+
import { AGENT_ITERATION_START_EVENT, AGENT_WAITING_START_EVENT, AGENT_ITERATION_SKIPPED_EVENT } from '@utils/TimerEventManager';
89
import { Logger, LogEntry } from '@utils/logging';
910
import { StreamManager, StreamState } from '@utils/streamManager';
1011

@@ -64,6 +65,7 @@ const AgentCard: React.FC<AgentCardProps> = ({
6465
const [lastResponse, setLastResponse] = useState<string>('...');
6566
const [responseKey, setResponseKey] = useState(0);
6667
const [loopProgress, setLoopProgress] = useState(0);
68+
const [lastProgressUpdate, setLastProgressUpdate] = useState(0);
6769
const [currentModel, setCurrentModel] = useState(agent.model_name);
6870
const initialModelRef = useRef(agent.model_name);
6971

@@ -103,7 +105,12 @@ const AgentCard: React.FC<AgentCardProps> = ({
103105
return;
104106
}
105107
if (isRunning) {
106-
if (liveStatus === 'STARTING' || liveStatus === 'IDLE') setLiveStatus('CAPTURING');
108+
if (liveStatus === 'STARTING' || liveStatus === 'IDLE') {
109+
setLiveStatus('CAPTURING');
110+
// Reset progress when agent starts
111+
setLoopProgress(0);
112+
setLastProgressUpdate(0);
113+
}
107114
const handleNewLog = (log: LogEntry) => {
108115
if (log.source !== agent.id) return;
109116
if (log.details?.logType === 'model-prompt') setLiveStatus('THINKING');
@@ -121,22 +128,54 @@ const AgentCard: React.FC<AgentCardProps> = ({
121128
}, [isRunning, showStartingState, agent.id, liveStatus, hasQuotaError]);
122129

123130
useEffect(() => {
124-
let timer: NodeJS.Timeout | null = null;
125-
if (isRunning && liveStatus === 'WAITING' && !hasQuotaError) {
126-
timer = setInterval(() => {
127-
setLoopProgress(prev => {
128-
const newProgress = prev + (100 / (agent.loop_interval_seconds * 10));
129-
if (newProgress >= 100) {
130-
clearInterval(timer!);
131-
setLiveStatus('CAPTURING');
132-
return 0;
133-
}
134-
return newProgress;
135-
});
136-
}, 100);
131+
let progressTimer: NodeJS.Timeout | null = null;
132+
let nextIterationTime = 0;
133+
let intervalMs = 0;
134+
135+
const handleWaitingStart = (event: CustomEvent) => {
136+
if (event.detail.agentId !== agent.id) return;
137+
nextIterationTime = event.detail.nextIterationTime;
138+
intervalMs = event.detail.intervalMs;
139+
140+
// Start progress timer
141+
progressTimer = setInterval(() => {
142+
const now = Date.now();
143+
const elapsed = now - (nextIterationTime - intervalMs);
144+
const progress = Math.min(100, Math.max(0, (elapsed / intervalMs) * 100));
145+
setLoopProgress(progress);
146+
setLastProgressUpdate(now);
147+
}, 50); // Update more frequently for smoothness
148+
};
149+
150+
const handleIterationStart = (event: CustomEvent) => {
151+
if (event.detail.agentId !== agent.id) return;
152+
if (progressTimer) {
153+
clearInterval(progressTimer);
154+
progressTimer = null;
155+
}
156+
setLoopProgress(0);
157+
};
158+
159+
const handleIterationSkipped = (event: CustomEvent) => {
160+
if (event.detail.agentId !== agent.id) return;
161+
nextIterationTime = event.detail.nextIterationTime;
162+
intervalMs = event.detail.intervalMs;
163+
// Continue showing progress for next possible iteration
164+
};
165+
166+
if (isRunning && !hasQuotaError) {
167+
window.addEventListener(AGENT_WAITING_START_EVENT as any, handleWaitingStart);
168+
window.addEventListener(AGENT_ITERATION_START_EVENT as any, handleIterationStart);
169+
window.addEventListener(AGENT_ITERATION_SKIPPED_EVENT as any, handleIterationSkipped);
137170
}
138-
return () => { if (timer) clearInterval(timer); };
139-
}, [isRunning, liveStatus, agent.loop_interval_seconds, hasQuotaError]);
171+
172+
return () => {
173+
if (progressTimer) clearInterval(progressTimer);
174+
window.removeEventListener(AGENT_WAITING_START_EVENT as any, handleWaitingStart);
175+
window.removeEventListener(AGENT_ITERATION_START_EVENT as any, handleIterationStart);
176+
window.removeEventListener(AGENT_ITERATION_SKIPPED_EVENT as any, handleIterationSkipped);
177+
};
178+
}, [isRunning, hasQuotaError, agent.id]);
140179

141180
const handleToggle = async () => {
142181
if (isRunning) {
@@ -181,7 +220,8 @@ const AgentCard: React.FC<AgentCardProps> = ({
181220

182221
return (
183222
<div className="relative bg-white rounded-xl shadow-sm border border-gray-200 transition-all duration-300 flex flex-col">
184-
{isRunning && liveStatus === 'WAITING' && (
223+
{isRunning && (liveStatus === 'WAITING' || liveStatus === 'THINKING') &&
224+
(Date.now() - lastProgressUpdate < 5000) && ( // Hide if progress hasn't updated in 5 seconds
185225
<div className="absolute top-0 left-0 right-0 h-1 z-10">
186226
<div className="h-full bg-green-500" style={{ width: `${loopProgress}%`, transition: 'width 0.1s linear' }} />
187227
</div>

0 commit comments

Comments
 (0)