Skip to content

Commit 98e1776

Browse files
Python: Fix DevUI streaming memory growth and add cross-platform regression coverage (#5221)
* fix for memory leak in devui * update async sleep * remove old func
1 parent 7bb0fec commit 98e1776

6 files changed

Lines changed: 1210 additions & 308 deletions

File tree

python/packages/devui/agent_framework_devui/ui/assets/index.js

Lines changed: 117 additions & 125 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

python/packages/devui/frontend/src/App.tsx

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Features: Entity selection, layout management, debug coordination
44
*/
55

6-
import { useEffect, useCallback, useState } from "react";
6+
import { useEffect, useCallback, useRef, useState } from "react";
77
import { AppHeader, DebugPanel, SettingsModal, DeploymentModal } from "@/components/layout";
88
import { GalleryView } from "@/components/features/gallery";
99
import { AgentView } from "@/components/features/agent";
@@ -15,17 +15,22 @@ import type {
1515
AgentInfo,
1616
WorkflowInfo,
1717
ExtendedResponseStreamEvent,
18+
ResponseTextDeltaEvent,
1819
} from "@/types";
1920
import { Button } from "./components/ui/button";
2021
import { Input } from "./components/ui/input";
2122
import { useDevUIStore } from "@/stores";
2223

24+
const DEBUG_TEXT_EVENT_FLUSH_INTERVAL_MS = 50;
25+
2326
export default function App() {
2427
// Local state for auth handling
2528
const [authRequired, setAuthRequired] = useState(false);
2629
const [authToken, setAuthToken] = useState("");
2730
const [isTestingToken, setIsTestingToken] = useState(false);
2831
const [authError, setAuthError] = useState("");
32+
const bufferedDebugTextRef = useRef<ResponseTextDeltaEvent | null>(null);
33+
const lastBufferedDebugFlushAtRef = useRef(0);
2934

3035
// Entity state from Zustand
3136
const agents = useDevUIStore((state) => state.agents);
@@ -303,16 +308,63 @@ export default function App() {
303308
[selectEntity, updateAgent, updateWorkflow, addToast]
304309
);
305310

311+
const flushBufferedDebugText = useCallback(() => {
312+
const bufferedEvent = bufferedDebugTextRef.current;
313+
if (!bufferedEvent) {
314+
return;
315+
}
316+
317+
bufferedDebugTextRef.current = null;
318+
lastBufferedDebugFlushAtRef.current = performance.now();
319+
addDebugEvent(bufferedEvent);
320+
}, [addDebugEvent]);
321+
306322
// Handle debug events from active view
307323
const handleDebugEvent = useCallback(
308324
(event: ExtendedResponseStreamEvent | "clear") => {
309325
if (event === "clear") {
326+
bufferedDebugTextRef.current = null;
310327
clearDebugEvents();
311-
} else {
312-
addDebugEvent(event);
328+
return;
313329
}
330+
331+
if (
332+
event.type === "response.output_text.delta" &&
333+
"delta" in event &&
334+
typeof event.delta === "string" &&
335+
event.delta.length > 0
336+
) {
337+
const bufferedEvent = bufferedDebugTextRef.current;
338+
const isSameOutput =
339+
bufferedEvent !== null &&
340+
bufferedEvent.item_id === event.item_id &&
341+
bufferedEvent.output_index === event.output_index &&
342+
bufferedEvent.content_index === event.content_index;
343+
344+
if (isSameOutput && bufferedEvent) {
345+
bufferedDebugTextRef.current = {
346+
...bufferedEvent,
347+
delta: bufferedEvent.delta + event.delta,
348+
sequence_number: event.sequence_number ?? bufferedEvent.sequence_number,
349+
};
350+
} else {
351+
flushBufferedDebugText();
352+
bufferedDebugTextRef.current = { ...event } as ResponseTextDeltaEvent;
353+
}
354+
355+
if (
356+
performance.now() - lastBufferedDebugFlushAtRef.current >=
357+
DEBUG_TEXT_EVENT_FLUSH_INTERVAL_MS
358+
) {
359+
flushBufferedDebugText();
360+
}
361+
return;
362+
}
363+
364+
flushBufferedDebugText();
365+
addDebugEvent(event);
314366
},
315-
[addDebugEvent, clearDebugEvents]
367+
[addDebugEvent, clearDebugEvents, flushBufferedDebugText]
316368
);
317369

318370
// Show loading state while initializing

0 commit comments

Comments
 (0)