Skip to content

Commit 9b3d64d

Browse files
committed
feat(showcase/langgraph-python): add subagent-card and subagent-result testids
- subagent-activity-card: emit data-testid="subagent-card-<role>" on each card wrapper (researcher | writer | critic), data-testid= "subagent-result" on the Result content div, and data-testid= "subagent-status" on the status pill. The previous testids (subagent-activity-card, subagent-activity-result) collapsed across roles, so the e2e suite couldn't count or content-assert per role. - delegation-log: render a fixed row of 3 always-visible role indicators (data-testid="subagent-indicator-<role>") so the page exposes a stable hook for the load-state assertion regardless of whether the supervisor has delegated yet.
1 parent 527e46c commit 9b3d64d

2 files changed

Lines changed: 59 additions & 3 deletions

File tree

showcase/integrations/langgraph-python/src/app/demos/subagents/delegation-log.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,25 @@ const SUB_AGENT_STYLE: Record<
5151
* The parent header shows how many sub-agents have been called and
5252
* whether the supervisor is still running.
5353
*/
54+
// Fixed list of the three sub-agent roles the supervisor can call.
55+
// Rendered as always-visible indicator chips at the top of the log
56+
// (regardless of whether the supervisor has delegated yet) so the user
57+
// — and the e2e suite — can see at a glance which sub-agents exist and
58+
// which are currently active.
59+
const INDICATOR_ROLES: ReadonlyArray<{
60+
role: "researcher" | "writer" | "critic";
61+
subAgent: SubAgentName;
62+
}> = [
63+
{ role: "researcher", subAgent: "research_agent" },
64+
{ role: "writer", subAgent: "writing_agent" },
65+
{ role: "critic", subAgent: "critique_agent" },
66+
];
67+
5468
export function DelegationLog({ delegations, isRunning }: DelegationLogProps) {
69+
const calledRoles = new Set<SubAgentName>(
70+
delegations.map((d) => d.sub_agent),
71+
);
72+
5573
return (
5674
<div
5775
data-testid="delegation-log"
@@ -80,6 +98,30 @@ export function DelegationLog({ delegations, isRunning }: DelegationLogProps) {
8098
</span>
8199
</div>
82100

101+
<div
102+
data-testid="subagent-indicators"
103+
className="flex items-center gap-2 border-b border-[#E9E9EF] bg-white px-6 py-2"
104+
>
105+
{INDICATOR_ROLES.map(({ role, subAgent }) => {
106+
const style = SUB_AGENT_STYLE[subAgent];
107+
const fired = calledRoles.has(subAgent);
108+
return (
109+
<span
110+
key={role}
111+
data-testid={`subagent-indicator-${role}`}
112+
data-role={role}
113+
data-fired={fired ? "true" : "false"}
114+
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-semibold uppercase tracking-[0.1em] border ${style.color} ${
115+
fired ? "" : "opacity-60"
116+
}`}
117+
>
118+
<span aria-hidden>{style.emoji}</span>
119+
<span>{style.label}</span>
120+
</span>
121+
);
122+
})}
123+
</div>
124+
83125
<div className="flex-1 overflow-y-auto p-4 space-y-3">
84126
{delegations.length === 0 ? (
85127
<p className="text-[#838389] italic text-sm">

showcase/integrations/langgraph-python/src/app/demos/subagents/subagent-activity-card.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ export interface SubAgentActivityCardProps {
5555
result: string | undefined;
5656
}
5757

58+
// Map the backend tool name (`research_agent`, `writing_agent`,
59+
// `critique_agent`) to the short role used in test selectors. The
60+
// per-role `[data-testid="subagent-card-<role>"]` testid is the stable
61+
// hook the e2e suite uses to count cards and assert per-card content.
62+
const ROLE_TESTID: Record<SubAgentName, "researcher" | "writer" | "critic"> = {
63+
research_agent: "researcher",
64+
writing_agent: "writer",
65+
critique_agent: "critic",
66+
};
67+
5868
export function SubAgentActivityCard({
5969
subAgent,
6070
task,
@@ -64,10 +74,12 @@ export function SubAgentActivityCard({
6474
const meta = SUB_AGENT_META[subAgent];
6575
const done = status === "complete";
6676
const running = !done;
77+
const roleTestId = `subagent-card-${ROLE_TESTID[subAgent]}`;
6778

6879
return (
6980
<div
70-
data-testid="subagent-activity-card"
81+
data-testid={roleTestId}
82+
data-subagent-card={ROLE_TESTID[subAgent]}
7183
data-sub-agent={subAgent}
7284
data-status={status}
7385
className={`my-3 overflow-hidden rounded-2xl border bg-white shadow-sm ${meta.accent}`}
@@ -106,7 +118,8 @@ export function SubAgentActivityCard({
106118
<Section label="Result">
107119
{done ? (
108120
<div
109-
data-testid="subagent-activity-result"
121+
data-testid="subagent-result"
122+
data-subagent-result-role={ROLE_TESTID[subAgent]}
110123
className="rounded-lg border border-[#E9E9EF] bg-white p-2.5 text-xs text-[#010507] whitespace-pre-wrap"
111124
>
112125
{result?.trim() ? result : "(empty)"}
@@ -150,7 +163,8 @@ function StatusBadge({
150163
const label = describeStatus(status);
151164
return (
152165
<span
153-
data-testid="subagent-activity-status"
166+
data-testid="subagent-status"
167+
data-status={status}
154168
className={`rounded-full border px-2.5 py-0.5 text-[10px] font-medium uppercase tracking-[0.14em] ${chipTone}`}
155169
>
156170
{label}

0 commit comments

Comments
 (0)