Skip to content

Commit d3e5594

Browse files
feat: improve cross reference agent ui (#69)
1 parent 708ffb4 commit d3e5594

File tree

5 files changed

+97
-34
lines changed

5 files changed

+97
-34
lines changed

examples/agents-researcher/app/components/agent-block.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@ export const AgentBlock = ({
55
children,
66
name,
77
agentInfoDisplay,
8-
setAgentInfoDisplay
8+
setAgentInfoDisplay,
9+
isDisabled
910
}: {
1011
children: React.ReactNode;
1112
name: AgentName;
12-
agentInfoDisplay: AgentName;
13+
agentInfoDisplay: AgentName | false;
1314
setAgentInfoDisplay: (name: AgentName) => void;
15+
isDisabled: boolean;
1416
}) => {
1517
return (
1618
<button
1719
className={cx(
18-
'aspect-square w-1/3 bg-white border-2 flex items-center justify-center text-opacity-60 rounded-xl',
20+
'aspect-[3] w-1/3 bg-white border-2 flex items-center justify-center text-opacity-60 rounded-xl',
1921
agentInfoDisplay === name ? 'border-purple-400' : 'border-gray-300'
2022
)}
2123
onClick={() => setAgentInfoDisplay(name)}
24+
disabled={isDisabled}
2225
>
2326
{children}
2427
</button>

examples/agents-researcher/app/components/agent-info.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const AgentInfo = ({
5252
state: false | 'loading' | StepRecord[];
5353
}) => {
5454
const [displayCode, setDisplayCode] = useState(false);
55-
const [displayOutput, setDisplayOutput] = useState(true);
55+
const [displayOutput, setDisplayOutput] = useState(false);
5656
return (
5757
<div className="flex flex-col gap-4">
5858
<div className="flex flex-col gap-2 w-full">

examples/agents-researcher/app/components/step-list.tsx

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable @typescript-eslint/no-empty-object-type */
22
import * as React from 'react'
33
import cx from '../utils/cx'
4+
import type { StepStatus } from '../types'
5+
import { IconLoader } from '../icons/loader'
46

57
export interface StepProps extends React.ComponentPropsWithoutRef<'div'> {}
68

@@ -9,13 +11,17 @@ const Step = ({ className, ...props }: StepProps) => {
911
}
1012
Step.displayName = 'Step'
1113

12-
export interface StepItemProps extends React.ComponentPropsWithoutRef<'div'> {}
14+
export interface StepItemProps
15+
extends React.ComponentPropsWithoutRef<'span'> {
16+
status: StepStatus
17+
}
1318

14-
const StepItem = ({ className, ...props }: StepItemProps) => {
19+
const StepItem = ({ status, className, ...props }: StepItemProps) => {
1520
return (
1621
<div
1722
className={cx(
1823
'relative border-l-2 border-l-zinc-200 pb-16 pl-6 last:pb-0 sm:ml-4 sm:pl-8 dark:border-l-zinc-900',
24+
(status === "init" ? 'border-l-zinc-200' : (status === "loading" ? 'border-l-purple-500' : 'border-l-emerald-500')),
1925
className,
2026
)}
2127
{...props}
@@ -27,22 +33,38 @@ StepItem.displayName = 'StepItem'
2733
export interface StepNumberProps
2834
extends React.ComponentPropsWithoutRef<'span'> {
2935
order?: number
36+
status: StepStatus
3037
}
3138

32-
const StepNumber = ({ order=1, className, ...props }: StepNumberProps) => {
39+
const StepNumber = ({ order=1, status, className, ...props }: StepNumberProps) => {
3340
return (
3441
<span className="absolute top-0 left-0 h-6 sm:h-8">
42+
{status !== "loading" ? (
3543
<span
3644
className={cx(
3745
'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
3846
'flex size-8 items-center justify-center sm:size-10',
39-
'text-center rounded-full bg-zinc-100 border-4 border-white',
47+
'text-center rounded-full border-4 border-white',
48+
(status === "init" ? 'bg-zinc-100' : 'bg-emerald-500'),
4049
className,
4150
)}
4251
{...props}
4352
>
44-
<span className="font-mono text-sm font-semibold opacity-60">{order}</span>
53+
<span className={cx("font-mono text-sm font-semibold", (status === "init" ? 'opacity-60' : 'text-white opacity-100'))}>{order}</span>
4554
</span>
55+
) : (
56+
<span
57+
className={cx(
58+
'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
59+
'flex size-8 items-center justify-center sm:size-10',
60+
'text-center rounded-full border-4 border-white',
61+
)}
62+
>
63+
<span className='bg-white'>
64+
<IconLoader className="animate-spin size-8 text-purple-500" />
65+
</span>
66+
</span>
67+
)}
4668
</span>
4769
)
4870
}

examples/agents-researcher/app/page.tsx

+61-25
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,17 @@ const Page = () => {
4141
});
4242

4343
const [agentInfoDisplay, setAgentInfoDisplay] =
44-
useState<AgentName>('Wikipedia');
44+
useState<AgentName | false>(false);
45+
const [currentStep, setCurrentStep] = useState(0);
4546

4647
// form submit handler
4748
const handleSend = async (e: FormEvent<HTMLFormElement>) => {
4849
e.preventDefault();
4950
let scrolledIntermediate = false;
51+
let intermediateLogged = false;
5052

5153
try {
54+
setCurrentStep(1);
5255
setLoading(true);
5356
setProgress(null);
5457
setAgentStates({
@@ -90,6 +93,11 @@ const Page = () => {
9093

9194
if (result.progress) {
9295
setLoading(false);
96+
if (result.progress === 'Call Agent Manager LLM') {
97+
setCurrentStep(intermediateLogged ? 3 : 2);
98+
} else {
99+
intermediateLogged = true;
100+
}
93101
}
94102

95103
setProgress(result.progress);
@@ -122,6 +130,7 @@ const Page = () => {
122130
}
123131

124132
if (result.crossReferenceOutput) {
133+
setCurrentStep(5);
125134
document
126135
.getElementById('cross-reference-output')
127136
?.scrollIntoView({ behavior: 'smooth' });
@@ -160,6 +169,14 @@ const Page = () => {
160169
}
161170
};
162171

172+
const resolveStepStatus = (stepNumber: number) => {
173+
return (currentStep === stepNumber ? "loading" : currentStep > stepNumber ? "done" : "init");
174+
}
175+
176+
const displayUsedResources = [...Object.entries(agentStates)]
177+
.filter(([key, value]) => value && key !== "Cross Reference")
178+
.map(([key]) => key)
179+
163180
return (
164181
<main className="h-screen">
165182
{progress && (
@@ -171,17 +188,18 @@ const Page = () => {
171188
<div className="max-w-screen-sm px-8 pt-16 mx-auto pb-44">
172189
{/* header */}
173190
<header>
174-
<div className="mb-8">
175-
<WorkflowIcon size={40} />
176-
</div>
177-
178-
<h1 className="text-2xl font-semibold text-balance">
191+
<h1 className="text-2xl font-semibold text-balance mb-8">
179192
Cross Reference Agent
180193
</h1>
181-
<h2 className="text-lg text-balance opacity-60">
182-
This is a simple example to demonstrate how to use Upstash Workflow
183-
Agents to cross-reference information from different sources.
184-
</h2>
194+
<div className="text-lg text-balance flex flex-row flex-wrap items-center gap-2">
195+
<span className="text-zinc-500">
196+
This is a simple example to demonstrate how to use
197+
<div className="inline-block mx-1 align-text-bottom">
198+
<WorkflowIcon size={22} />
199+
</div>
200+
Upstash Workflow Agents to cross-reference information from different sources.
201+
</span>
202+
</div>
185203

186204
<div className="flex flex-wrap items-center gap-2 mt-4">
187205
<a
@@ -233,8 +251,8 @@ const Page = () => {
233251
{/* step-by-step */}
234252
<Step className="mt-16 md:mt-16">
235253
{/* step-1 */}
236-
<StepItem>
237-
<StepNumber order={1} />
254+
<StepItem status={resolveStepStatus(1)}>
255+
<StepNumber order={1} status={resolveStepStatus(1)} />
238256

239257
<StepTitle>Ask a Question</StepTitle>
240258
<StepDesc>
@@ -268,27 +286,36 @@ const Page = () => {
268286
</StepItem>
269287

270288
{/* step-2 */}
271-
<StepItem>
272-
<StepNumber order={2} />
289+
<StepItem status={resolveStepStatus(2)}>
290+
<StepNumber order={2} status={resolveStepStatus(2)} />
273291

274292
<StepTitle>View Answers From Various Resources</StepTitle>
275293
<StepDesc>
276294
The cross-reference agent will orchestrate worker agents to get
277295
answers from different resources.
278296
</StepDesc>
279297

298+
{currentStep > 1 && (
280299
<StepContent>
281300
<div className="flex flex-col gap-4">
301+
{displayUsedResources.length > 0 &&
302+
<span className='opacity-60'>
303+
Your agent chose to use {
304+
displayUsedResources
305+
.join(', ')} to answer your question.
306+
</span>
307+
}
282308
<div className="flex gap-4 w-full">
283309
<AgentBlock
284310
name="Wikipedia"
285311
agentInfoDisplay={agentInfoDisplay}
286312
setAgentInfoDisplay={setAgentInfoDisplay}
313+
isDisabled={agentStates['Wikipedia'] === false}
287314
>
288315
<Img
289316
src="/icons/wikipedia.png"
290-
width={68}
291-
height={68}
317+
width={44}
318+
height={44}
292319
alt="Wikipedia"
293320
className={agentStates['Wikipedia'] === false ? 'opacity-60' : 'opacity-100'}
294321
/>
@@ -297,11 +324,12 @@ const Page = () => {
297324
name="WolframAlpha"
298325
agentInfoDisplay={agentInfoDisplay}
299326
setAgentInfoDisplay={setAgentInfoDisplay}
327+
isDisabled={agentStates['WolframAlpha'] === false}
300328
>
301329
<Img
302330
src="/icons/wolfram-alpha.png"
303-
width={72}
304-
height={72}
331+
width={48}
332+
height={48}
305333
alt="WolframAlpha"
306334
className={agentStates['WolframAlpha'] === false ? 'opacity-60' : 'opacity-100'}
307335
/>
@@ -310,57 +338,64 @@ const Page = () => {
310338
name="DuckDuckGo"
311339
agentInfoDisplay={agentInfoDisplay}
312340
setAgentInfoDisplay={setAgentInfoDisplay}
341+
isDisabled={agentStates['DuckDuckGo'] === false}
313342
>
314343
<Img
315344
src="/icons/duckduckgo.png"
316-
width={62}
317-
height={62}
345+
width={40}
346+
height={40}
318347
alt="DuckDuckGo"
319348
className={agentStates['DuckDuckGo'] === false ? 'opacity-60' : 'opacity-100'}
320349
/>
321350
</AgentBlock>
322351
</div>
352+
{agentInfoDisplay &&
323353
<AgentInfo
324354
name={agentInfoDisplay}
325355
code={CODES[agentInfoDisplay]}
326356
state={agentStates[agentInfoDisplay]}
327357
/>
358+
}
328359
</div>
329360
</StepContent>
361+
)}
330362
</StepItem>
331363

332364
{/* step-3 */}
333-
<StepItem>
334-
<StepNumber order={3} />
365+
<StepItem status={resolveStepStatus(3)}>
366+
<StepNumber order={3} status={resolveStepStatus(3)} />
335367

336368
<StepTitle>See Final Summary with References</StepTitle>
337369
<StepDesc>
338370
The cross-reference agent will summarize the answers with
339371
references.
340372
</StepDesc>
341373

374+
{currentStep > 2 && (
342375
<StepContent>
343376
<AgentInfo
344377
name="Cross Reference"
345378
code={CODES['Cross Reference']}
346379
state={agentStates['Cross Reference']}
347380
/>
348381
</StepContent>
382+
)}
349383
</StepItem>
350384

351385
{/* step-4 */}
352-
<StepItem>
353-
<StepNumber order={4} />
386+
<StepItem status={resolveStepStatus(4)}>
387+
<StepNumber order={4} status={resolveStepStatus(4)} />
354388

355389
<StepTitle>See Logs in Upstash Console</StepTitle>
356390
<StepDesc>
357391
After running the workflow, navigate to the Upstash Console to see
358392
the related logs.
359393
</StepDesc>
360394

395+
{currentStep > 3 && (
361396
<StepContent>
362397
<a
363-
className="inline-flex items-center gap-1 px-3 py-2 bg-gray-100 rounded-md hover:bg-purple-100"
398+
className="inline-flex items-center gap-1 px-3 py-2 rounded-md bg-purple-500 text-white hover:bg-purple-400"
364399
href="https://console.upstash.com/qstash?tab=workflow"
365400
>
366401
<svg
@@ -390,6 +425,7 @@ const Page = () => {
390425
alt="s"
391426
/>
392427
</StepContent>
428+
)}
393429
</StepItem>
394430
</Step>
395431
</div>

examples/agents-researcher/app/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export type StepRecord = {
88
stepName: string;
99
stepOut: string;
1010
};
11+
12+
export type StepStatus = "init" | "loading" | "done"

0 commit comments

Comments
 (0)