Update ag-ui-example UI with STATE_DELTA instead of ACTIVITY_SNAPSHOT since these are not handled by copilotkit

This commit is contained in:
Yiorgis Gozadinos 2025-12-03 15:32:48 +02:00
parent 84f25f0627
commit 0f5b9ae208
No known key found for this signature in database
3 changed files with 117 additions and 108 deletions

View file

@ -108,9 +108,35 @@ async def stream_research_agent(request: Request) -> StreamingResponse:
async for event in emitter:
# Log events for debugging
logger.info(f"AG-UI Event: {event}")
# Filter out ACTIVITY_SNAPSHOT - not supported by CopilotKit
if event.get("type") == "ACTIVITY_SNAPSHOT":
event_type = event.get("type")
# Convert ACTIVITY_SNAPSHOT to STATE_DELTA for CopilotKit
# As CopilotKit does not handle ACTIVITY_SNAPSHOT events
if event_type == "ACTIVITY_SNAPSHOT":
activity_type = event.get("activityType", "")
content = event.get("content", {})
message = content.get("message", "")
# Emit STATE_DELTA to patch activity info into state
# Use "add" op which creates or replaces the value
delta_event = {
"type": "STATE_DELTA",
"delta": [
{
"op": "add",
"path": "/current_activity",
"value": activity_type,
},
{
"op": "add",
"path": "/current_activity_message",
"value": message,
},
],
}
await send_stream.send(format_sse_event(delta_event))
continue
await send_stream.send(format_sse_event(event))
# Run agent and event forwarding concurrently

View file

@ -70,6 +70,8 @@ interface ResearchState {
gaps_identified: GapRecord[];
} | null;
result?: ResearchReport;
current_activity?: string;
current_activity_message?: string;
}
function AgentContent() {

View file

@ -86,6 +86,8 @@ interface ResearchState {
gaps_identified: GapRecord[];
} | null;
result?: ResearchReport;
current_activity?: string;
current_activity_message?: string;
}
interface StateDisplayProps {
@ -187,112 +189,6 @@ export default function StateDisplay({ state }: StateDisplayProps) {
gap: "1rem",
}}
>
{/* Current Status */}
<div
style={{
background: "white",
borderRadius: "8px",
padding: "1.5rem",
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
}}
>
<div
style={{
fontSize: "0.875rem",
color: "#718096",
marginBottom: "0.5rem",
}}
>
Research Progress
</div>
<div style={{ display: "flex", gap: "0.75rem", alignItems: "center" }}>
<div
style={{
padding: "0.5rem 1rem",
background:
state.iterations === 0
? "#e2e8f0"
: state.result
? "#d1fae5"
: "#dbeafe",
color:
state.iterations === 0
? "#718096"
: state.result
? "#065f46"
: "#1e40af",
borderRadius: "6px",
fontSize: "1rem",
fontWeight: "700",
}}
>
{state.iterations === 0
? "Ready"
: state.result
? "Complete"
: "Researching"}
</div>
{state.iterations > 0 && (
<div
style={{
fontSize: "0.875rem",
color: "#4a5568",
}}
>
Iteration {state.iterations} of {state.max_iterations}
</div>
)}
</div>
{/* Research Progress Bar */}
{state.iterations > 0 && (
<div style={{ marginTop: "1rem" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "0.5rem",
}}
>
<span
style={{
fontSize: "0.75rem",
color: "#718096",
}}
>
Iterations
</span>
<span
style={{
fontSize: "0.75rem",
fontWeight: "600",
color: "#2d3748",
}}
>
{state.iterations}/{state.max_iterations}
</span>
</div>
<div
style={{
height: "0.5rem",
background: "#e2e8f0",
borderRadius: "4px",
overflow: "hidden",
}}
>
<div
style={{
width: `${researchProgress}%`,
height: "100%",
background: "#48bb78",
transition: "width 0.3s ease",
}}
/>
</div>
</div>
)}
</div>
{/* Question */}
{state.context.original_question && (
<div
@ -324,6 +220,91 @@ export default function StateDisplay({ state }: StateDisplayProps) {
</div>
)}
{/* Research Progress - only show when research has started */}
{(state.iterations > 0 || (state.current_activity && !state.result)) && (
<div
style={{
background: "white",
borderRadius: "8px",
padding: "1.5rem",
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
}}
>
{/* Current Activity - hide when complete */}
{state.current_activity && !state.result && (
<div
style={{
padding: "0.75rem",
background: "#ebf8ff",
borderRadius: "6px",
border: "1px solid #90cdf4",
marginBottom: state.iterations > 0 ? "1rem" : 0,
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
<span></span>
<span style={{ fontWeight: "600", color: "#2b6cb0" }}>
{state.current_activity.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())}
</span>
</div>
{state.current_activity_message && (
<div style={{ fontSize: "0.875rem", color: "#4299e1", marginTop: "0.25rem", marginLeft: "1.5rem" }}>
{state.current_activity_message}
</div>
)}
</div>
)}
{/* Iteration Progress Bar */}
{state.iterations > 0 && (
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "0.5rem",
}}
>
<span
style={{
fontSize: "0.75rem",
color: "#718096",
}}
>
Iterations
</span>
<span
style={{
fontSize: "0.75rem",
fontWeight: "600",
color: "#2d3748",
}}
>
{state.iterations}/{state.max_iterations}
</span>
</div>
<div
style={{
height: "0.5rem",
background: "#e2e8f0",
borderRadius: "4px",
overflow: "hidden",
}}
>
<div
style={{
width: `${researchProgress}%`,
height: "100%",
background: state.result ? "#48bb78" : "#4299e1",
transition: "width 0.3s ease",
}}
/>
</div>
</div>
)}
</div>
)}
{/* Confidence Meter */}
{confidence > 0 && (
<div