mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-28 21:27:14 +00:00
feat(agents): improve processor creation choice
This commit is contained in:
@@ -5,15 +5,17 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { Bot, Workflow } from 'lucide-react';
|
import { Bot, CheckCircle2, Workflow } from 'lucide-react';
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
import { AgentKind } from '@/app/infra/entities/api';
|
import { AgentKind } from '@/app/infra/entities/api';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@/components/ui/card';
|
} from '@/components/ui/card';
|
||||||
@@ -27,6 +29,7 @@ import {
|
|||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import EmojiPicker from '@/components/ui/emoji-picker';
|
import EmojiPicker from '@/components/ui/emoji-picker';
|
||||||
|
import ProcessorTypeDiagram from './ProcessorTypeDiagram';
|
||||||
|
|
||||||
export default function AgentCreateContent({
|
export default function AgentCreateContent({
|
||||||
onCreated,
|
onCreated,
|
||||||
@@ -110,39 +113,113 @@ export default function AgentCreateContent({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-1 overflow-y-auto min-h-0">
|
<div className="flex-1 overflow-y-auto min-h-0">
|
||||||
<div className="mx-auto max-w-2xl space-y-6">
|
<div className="mx-auto max-w-5xl space-y-6 pb-6">
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
<div className="grid items-stretch gap-5 lg:grid-cols-[minmax(0,0.82fr)_minmax(0,1.18fr)]">
|
||||||
|
<section
|
||||||
|
aria-labelledby="processor-kind-heading"
|
||||||
|
className="space-y-3"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<h2
|
||||||
|
id="processor-kind-heading"
|
||||||
|
className="text-base font-semibold"
|
||||||
|
>
|
||||||
|
{t('agents.chooseType')}
|
||||||
|
</h2>
|
||||||
|
<p className="mt-1 text-sm text-muted-foreground">
|
||||||
|
{t('agents.chooseTypeDescription')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-1">
|
||||||
{typeOptions.map((option) => {
|
{typeOptions.map((option) => {
|
||||||
const Icon = option.icon;
|
const Icon = option.icon;
|
||||||
const selected = kind === option.kind;
|
const selected = kind === option.kind;
|
||||||
return (
|
return (
|
||||||
<button
|
<Card
|
||||||
key={option.kind}
|
key={option.kind}
|
||||||
type="button"
|
data-processor-kind={option.kind}
|
||||||
onClick={() => handleKindChange(option.kind)}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-lg border bg-card p-4 text-left transition-colors',
|
'gap-0 py-0 transition-[border-color,box-shadow,background-color]',
|
||||||
selected
|
selected
|
||||||
? 'border-primary ring-2 ring-primary/20'
|
? 'border-primary bg-primary/[0.035] shadow-sm ring-1 ring-primary/20'
|
||||||
: 'hover:border-primary/60',
|
: 'hover:border-primary/50',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-3">
|
<CardContent className="h-full p-0">
|
||||||
<Icon className="mt-0.5 size-5 text-blue-500" />
|
<Button
|
||||||
<div className="space-y-1">
|
type="button"
|
||||||
<div className="font-medium">{option.title}</div>
|
variant="ghost"
|
||||||
<div className="text-xs text-muted-foreground">
|
aria-pressed={selected}
|
||||||
|
onClick={() => handleKindChange(option.kind)}
|
||||||
|
className="h-full min-h-36 w-full items-start justify-start whitespace-normal rounded-xl p-4 text-left hover:bg-transparent"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'flex size-10 shrink-0 items-center justify-center rounded-lg border bg-background',
|
||||||
|
selected &&
|
||||||
|
'border-primary/30 bg-primary/10 text-primary',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon className="size-5" />
|
||||||
|
</span>
|
||||||
|
<span className="min-w-0 flex-1 space-y-2">
|
||||||
|
<span className="flex items-center justify-between gap-3">
|
||||||
|
<span className="font-semibold">
|
||||||
|
{option.title}
|
||||||
|
</span>
|
||||||
|
{selected && (
|
||||||
|
<CheckCircle2 className="size-4 shrink-0 text-primary" />
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<Badge
|
||||||
|
variant={selected ? 'default' : 'secondary'}
|
||||||
|
className="font-normal"
|
||||||
|
>
|
||||||
{option.badge}
|
{option.badge}
|
||||||
</div>
|
</Badge>
|
||||||
<p className="text-sm text-muted-foreground">
|
<span className="block text-sm leading-relaxed text-muted-foreground">
|
||||||
{option.description}
|
{option.description}
|
||||||
</p>
|
</span>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</Button>
|
||||||
</button>
|
</CardContent>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Card className="min-h-[360px] overflow-hidden py-0">
|
||||||
|
<CardHeader className="border-b bg-muted/20 py-5">
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<div>
|
||||||
|
<CardTitle>
|
||||||
|
{kind === 'agent'
|
||||||
|
? t('agents.agentDiagramTitle')
|
||||||
|
: t('agents.pipelineDiagramTitle')}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="mt-1.5">
|
||||||
|
{t('agents.diagramHint')}
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<Badge variant="outline">
|
||||||
|
{kind === 'agent'
|
||||||
|
? t('agents.kindBadgeAgent')
|
||||||
|
: t('agents.kindBadgePipeline')}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="flex flex-1 items-center p-4 sm:p-6">
|
||||||
|
<ProcessorTypeDiagram kind={kind} />
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="border-t bg-muted/20 px-6 py-4 text-sm leading-relaxed text-muted-foreground">
|
||||||
|
{kind === 'agent'
|
||||||
|
? t('agents.agentDiagramDescription')
|
||||||
|
: t('agents.pipelineDiagramDescription')}
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
@@ -0,0 +1,364 @@
|
|||||||
|
import { useId } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type { AgentKind } from '@/app/infra/entities/api';
|
||||||
|
|
||||||
|
function DiagramArrow({ id }: { id: string }) {
|
||||||
|
return (
|
||||||
|
<marker
|
||||||
|
id={id}
|
||||||
|
markerWidth="8"
|
||||||
|
markerHeight="8"
|
||||||
|
refX="7"
|
||||||
|
refY="4"
|
||||||
|
orient="auto"
|
||||||
|
markerUnits="strokeWidth"
|
||||||
|
>
|
||||||
|
<path d="M0,0 L8,4 L0,8 Z" fill="var(--muted-foreground)" />
|
||||||
|
</marker>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AgentDiagram() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const arrowId = `agent-arrow-${useId().replace(/:/g, '')}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 720 340"
|
||||||
|
role="img"
|
||||||
|
aria-label={t('agents.agentDiagramTitle')}
|
||||||
|
className="h-auto w-full"
|
||||||
|
data-testid="agent-diagram"
|
||||||
|
>
|
||||||
|
<title>{t('agents.agentDiagramTitle')}</title>
|
||||||
|
<desc>{t('agents.agentDiagramDescription')}</desc>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="agent-core" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stopColor="var(--primary)" stopOpacity="0.22" />
|
||||||
|
<stop offset="1" stopColor="var(--primary)" stopOpacity="0.06" />
|
||||||
|
</linearGradient>
|
||||||
|
<DiagramArrow id={arrowId} />
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect
|
||||||
|
x="24"
|
||||||
|
y="42"
|
||||||
|
width="184"
|
||||||
|
height="256"
|
||||||
|
rx="22"
|
||||||
|
fill="var(--muted)"
|
||||||
|
fillOpacity="0.48"
|
||||||
|
stroke="var(--border)"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x="116"
|
||||||
|
y="76"
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--foreground)"
|
||||||
|
fontSize="16"
|
||||||
|
fontWeight="600"
|
||||||
|
>
|
||||||
|
{t('agents.diagramEvents')}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
{[
|
||||||
|
{
|
||||||
|
y: 98,
|
||||||
|
label: t('agents.diagramMessages'),
|
||||||
|
icon: 'M44 116h12l8 8v-8h28v-24H44z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
y: 160,
|
||||||
|
label: t('agents.diagramMembers'),
|
||||||
|
icon: 'M51 167a7 7 0 1 0 0-14 7 7 0 0 0 0 14zm-11 14c1-8 6-12 11-12s10 4 11 12m8-22a6 6 0 1 0 0-12m5 28c-1-6-4-9-9-10',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
y: 222,
|
||||||
|
label: t('agents.diagramFeedback'),
|
||||||
|
icon: 'M52 213l4 9 10 1-7 7 2 10-9-5-9 5 2-10-7-7 10-1z',
|
||||||
|
},
|
||||||
|
].map((item) => (
|
||||||
|
<g key={item.label}>
|
||||||
|
<rect
|
||||||
|
x="38"
|
||||||
|
y={item.y}
|
||||||
|
width="156"
|
||||||
|
height="48"
|
||||||
|
rx="13"
|
||||||
|
fill="var(--card)"
|
||||||
|
stroke="var(--border)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d={item.icon}
|
||||||
|
transform={`translate(0 ${item.y - 92})`}
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--primary)"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x="120"
|
||||||
|
y={item.y + 30}
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--foreground)"
|
||||||
|
fontSize="14"
|
||||||
|
fontWeight="500"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<path
|
||||||
|
d="M208 170H270"
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--muted-foreground)"
|
||||||
|
strokeWidth="2"
|
||||||
|
markerEnd={`url(#${arrowId})`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<rect
|
||||||
|
x="284"
|
||||||
|
y="82"
|
||||||
|
width="164"
|
||||||
|
height="176"
|
||||||
|
rx="30"
|
||||||
|
fill="url(#agent-core)"
|
||||||
|
stroke="var(--primary)"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="331"
|
||||||
|
y="112"
|
||||||
|
width="70"
|
||||||
|
height="54"
|
||||||
|
rx="16"
|
||||||
|
fill="var(--card)"
|
||||||
|
stroke="var(--primary)"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
|
<circle cx="350" cy="139" r="5" fill="var(--primary)" />
|
||||||
|
<circle cx="382" cy="139" r="5" fill="var(--primary)" />
|
||||||
|
<path
|
||||||
|
d="M350 153c8 7 24 7 32 0M366 112V99m-8 0h16"
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--primary)"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x="366"
|
||||||
|
y="202"
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--foreground)"
|
||||||
|
fontSize="18"
|
||||||
|
fontWeight="700"
|
||||||
|
>
|
||||||
|
{t('agents.agentType')}
|
||||||
|
</text>
|
||||||
|
<text
|
||||||
|
x="366"
|
||||||
|
y="228"
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--muted-foreground)"
|
||||||
|
fontSize="13"
|
||||||
|
>
|
||||||
|
{t('agents.diagramDecide')}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<path
|
||||||
|
d="M448 170H506"
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--muted-foreground)"
|
||||||
|
strokeWidth="2"
|
||||||
|
markerEnd={`url(#${arrowId})`}
|
||||||
|
/>
|
||||||
|
{[
|
||||||
|
{ y: 70, label: t('agents.diagramModel') },
|
||||||
|
{ y: 145, label: t('agents.diagramTools') },
|
||||||
|
{ y: 220, label: t('agents.diagramActions') },
|
||||||
|
].map((item, index) => (
|
||||||
|
<g key={item.label}>
|
||||||
|
<path
|
||||||
|
d={`M476 170 C500 170 492 ${item.y + 26} 516 ${item.y + 26}`}
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--border)"
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
x="516"
|
||||||
|
y={item.y}
|
||||||
|
width="176"
|
||||||
|
height="52"
|
||||||
|
rx="15"
|
||||||
|
fill="var(--card)"
|
||||||
|
stroke={index === 1 ? 'var(--primary)' : 'var(--border)'}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx="544"
|
||||||
|
cy={item.y + 26}
|
||||||
|
r="10"
|
||||||
|
fill="var(--primary)"
|
||||||
|
fillOpacity={index === 1 ? 0.22 : 0.1}
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x="608"
|
||||||
|
y={item.y + 31}
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--foreground)"
|
||||||
|
fontSize="14"
|
||||||
|
fontWeight="500"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PipelineDiagram() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const arrowId = `pipeline-arrow-${useId().replace(/:/g, '')}`;
|
||||||
|
const stages = [
|
||||||
|
t('agents.diagramPreprocess'),
|
||||||
|
t('agents.diagramAI'),
|
||||||
|
t('agents.diagramPostprocess'),
|
||||||
|
t('agents.diagramOutput'),
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 720 340"
|
||||||
|
role="img"
|
||||||
|
aria-label={t('agents.pipelineDiagramTitle')}
|
||||||
|
className="h-auto w-full"
|
||||||
|
data-testid="pipeline-diagram"
|
||||||
|
>
|
||||||
|
<title>{t('agents.pipelineDiagramTitle')}</title>
|
||||||
|
<desc>{t('agents.pipelineDiagramDescription')}</desc>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="pipeline-flow" x1="0" y1="0" x2="1" y2="0">
|
||||||
|
<stop offset="0" stopColor="var(--primary)" stopOpacity="0.06" />
|
||||||
|
<stop offset="0.5" stopColor="var(--primary)" stopOpacity="0.18" />
|
||||||
|
<stop offset="1" stopColor="var(--primary)" stopOpacity="0.06" />
|
||||||
|
</linearGradient>
|
||||||
|
<DiagramArrow id={arrowId} />
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect
|
||||||
|
x="24"
|
||||||
|
y="65"
|
||||||
|
width="672"
|
||||||
|
height="210"
|
||||||
|
rx="28"
|
||||||
|
fill="url(#pipeline-flow)"
|
||||||
|
stroke="var(--border)"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x="360"
|
||||||
|
y="104"
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--muted-foreground)"
|
||||||
|
fontSize="14"
|
||||||
|
fontWeight="500"
|
||||||
|
>
|
||||||
|
{t('agents.pipelineDiagramFlow')}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<rect
|
||||||
|
x="40"
|
||||||
|
y="137"
|
||||||
|
width="104"
|
||||||
|
height="68"
|
||||||
|
rx="18"
|
||||||
|
fill="var(--card)"
|
||||||
|
stroke="var(--border)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M63 156h38v25H76l-9 8v-8h-4z"
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--primary)"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x="92"
|
||||||
|
y="228"
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--foreground)"
|
||||||
|
fontSize="13"
|
||||||
|
fontWeight="500"
|
||||||
|
>
|
||||||
|
{t('agents.diagramMessage')}
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<path
|
||||||
|
d="M144 171H168"
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--muted-foreground)"
|
||||||
|
strokeWidth="2"
|
||||||
|
markerEnd={`url(#${arrowId})`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{stages.map((stage, index) => {
|
||||||
|
const x = 178 + index * 128;
|
||||||
|
return (
|
||||||
|
<g key={stage}>
|
||||||
|
<rect
|
||||||
|
x={x}
|
||||||
|
y="127"
|
||||||
|
width="104"
|
||||||
|
height="88"
|
||||||
|
rx="18"
|
||||||
|
fill="var(--card)"
|
||||||
|
stroke={index === 1 ? 'var(--primary)' : 'var(--border)'}
|
||||||
|
strokeWidth={index === 1 ? 2 : 1}
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
cx={x + 52}
|
||||||
|
cy="154"
|
||||||
|
r="14"
|
||||||
|
fill="var(--primary)"
|
||||||
|
fillOpacity={index === 1 ? 0.22 : 0.1}
|
||||||
|
/>
|
||||||
|
<text
|
||||||
|
x={x + 52}
|
||||||
|
y="159"
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--primary)"
|
||||||
|
fontSize="13"
|
||||||
|
fontWeight="700"
|
||||||
|
>
|
||||||
|
{index + 1}
|
||||||
|
</text>
|
||||||
|
<text
|
||||||
|
x={x + 52}
|
||||||
|
y="190"
|
||||||
|
textAnchor="middle"
|
||||||
|
fill="var(--foreground)"
|
||||||
|
fontSize="13"
|
||||||
|
fontWeight="600"
|
||||||
|
>
|
||||||
|
{stage}
|
||||||
|
</text>
|
||||||
|
{index < stages.length - 1 && (
|
||||||
|
<path
|
||||||
|
d={`M${x + 104} 171H${x + 122}`}
|
||||||
|
fill="none"
|
||||||
|
stroke="var(--muted-foreground)"
|
||||||
|
strokeWidth="2"
|
||||||
|
markerEnd={`url(#${arrowId})`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ProcessorTypeDiagram({ kind }: { kind: AgentKind }) {
|
||||||
|
return kind === 'agent' ? <AgentDiagram /> : <PipelineDiagram />;
|
||||||
|
}
|
||||||
@@ -711,6 +711,30 @@ const enUS = {
|
|||||||
'Use a visual multi-stage flow to control message preprocessing, AI, postprocessing, extensions, and output. Message events only.',
|
'Use a visual multi-stage flow to control message preprocessing, AI, postprocessing, extensions, and output. Message events only.',
|
||||||
allEvents: 'Supports all events',
|
allEvents: 'Supports all events',
|
||||||
messageEventsOnly: 'Message events only',
|
messageEventsOnly: 'Message events only',
|
||||||
|
chooseType: 'Choose how it works',
|
||||||
|
chooseTypeDescription:
|
||||||
|
'Choose the processor model first. You can configure it further after creation.',
|
||||||
|
diagramHint: 'How it works',
|
||||||
|
agentDiagramTitle: 'Agent: decide and act',
|
||||||
|
agentDiagramDescription:
|
||||||
|
'An Agent can receive many platform events, use models and tools to understand the situation, and decide what to do next.',
|
||||||
|
pipelineDiagramTitle: 'Pipeline: follow a defined flow',
|
||||||
|
pipelineDiagramDescription:
|
||||||
|
'A Pipeline handles message events only and runs each step in the order you configure.',
|
||||||
|
pipelineDiagramFlow: 'Messages move through a fixed sequence',
|
||||||
|
diagramEvents: 'Platform events',
|
||||||
|
diagramMessages: 'Messages',
|
||||||
|
diagramMembers: 'Members',
|
||||||
|
diagramFeedback: 'Feedback',
|
||||||
|
diagramDecide: 'Understand and decide',
|
||||||
|
diagramModel: 'Use model',
|
||||||
|
diagramTools: 'Use tools',
|
||||||
|
diagramActions: 'Take action',
|
||||||
|
diagramMessage: 'Message',
|
||||||
|
diagramPreprocess: 'Prepare',
|
||||||
|
diagramAI: 'AI',
|
||||||
|
diagramPostprocess: 'Refine',
|
||||||
|
diagramOutput: 'Output',
|
||||||
basicInfo: 'Basic Information',
|
basicInfo: 'Basic Information',
|
||||||
basicInfoDescription: 'Set the name, icon and description',
|
basicInfoDescription: 'Set the name, icon and description',
|
||||||
runnerSettings: 'Runner',
|
runnerSettings: 'Runner',
|
||||||
|
|||||||
@@ -724,6 +724,30 @@ const jaJP = {
|
|||||||
'ビジュアルな多段フローで、メッセージの前処理、AI、後処理、拡張、出力を制御します。メッセージイベントのみ処理できます。',
|
'ビジュアルな多段フローで、メッセージの前処理、AI、後処理、拡張、出力を制御します。メッセージイベントのみ処理できます。',
|
||||||
allEvents: 'すべてのイベントに対応',
|
allEvents: 'すべてのイベントに対応',
|
||||||
messageEventsOnly: 'メッセージイベントのみ',
|
messageEventsOnly: 'メッセージイベントのみ',
|
||||||
|
chooseType: '処理方法を選択',
|
||||||
|
chooseTypeDescription:
|
||||||
|
'最初にプロセッサーの動作方式を選びます。作成後に詳しく設定できます。',
|
||||||
|
diagramHint: '動作イメージ',
|
||||||
|
agentDiagramTitle: 'Agent:判断して行動',
|
||||||
|
agentDiagramDescription:
|
||||||
|
'Agent はさまざまなプラットフォームイベントを受け取り、モデルやツールを使って状況を理解し、次の行動を判断します。',
|
||||||
|
pipelineDiagramTitle: 'Pipeline:決めたフローで処理',
|
||||||
|
pipelineDiagramDescription:
|
||||||
|
'Pipeline はメッセージイベントのみを、設定した固定ステップの順番で処理します。',
|
||||||
|
pipelineDiagramFlow: 'メッセージを固定フローで順番に処理',
|
||||||
|
diagramEvents: 'イベント',
|
||||||
|
diagramMessages: 'メッセージ',
|
||||||
|
diagramMembers: 'メンバー',
|
||||||
|
diagramFeedback: 'フィードバック',
|
||||||
|
diagramDecide: '理解して判断',
|
||||||
|
diagramModel: 'モデル',
|
||||||
|
diagramTools: 'ツール',
|
||||||
|
diagramActions: 'アクション',
|
||||||
|
diagramMessage: 'メッセージ',
|
||||||
|
diagramPreprocess: '前処理',
|
||||||
|
diagramAI: 'AI',
|
||||||
|
diagramPostprocess: '後処理',
|
||||||
|
diagramOutput: '出力',
|
||||||
basicInfo: '基本情報',
|
basicInfo: '基本情報',
|
||||||
basicInfoDescription: '名前、アイコン、説明を設定します',
|
basicInfoDescription: '名前、アイコン、説明を設定します',
|
||||||
runnerSettings: 'Runner',
|
runnerSettings: 'Runner',
|
||||||
|
|||||||
@@ -680,6 +680,29 @@ const zhHans = {
|
|||||||
'通过可视化多阶段流程控制消息的预处理、AI、后处理、扩展和输出;仅处理消息事件。',
|
'通过可视化多阶段流程控制消息的预处理、AI、后处理、扩展和输出;仅处理消息事件。',
|
||||||
allEvents: '支持全部事件',
|
allEvents: '支持全部事件',
|
||||||
messageEventsOnly: '仅支持消息事件',
|
messageEventsOnly: '仅支持消息事件',
|
||||||
|
chooseType: '选择处理方式',
|
||||||
|
chooseTypeDescription: '先选择处理器的工作方式,创建后可继续完善配置。',
|
||||||
|
diagramHint: '工作方式示意',
|
||||||
|
agentDiagramTitle: 'Agent:自主判断并行动',
|
||||||
|
agentDiagramDescription:
|
||||||
|
'Agent 可以接收多种平台事件,结合模型和工具理解当前情况,并自主决定下一步动作。',
|
||||||
|
pipelineDiagramTitle: 'Pipeline:按流程逐步处理',
|
||||||
|
pipelineDiagramDescription:
|
||||||
|
'Pipeline 只处理消息事件,并严格按照你编排的固定步骤依次执行。',
|
||||||
|
pipelineDiagramFlow: '消息沿固定流程依次处理',
|
||||||
|
diagramEvents: '平台事件',
|
||||||
|
diagramMessages: '消息',
|
||||||
|
diagramMembers: '群成员',
|
||||||
|
diagramFeedback: '反馈',
|
||||||
|
diagramDecide: '理解事件并决定下一步',
|
||||||
|
diagramModel: '调用模型',
|
||||||
|
diagramTools: '使用工具',
|
||||||
|
diagramActions: '执行动作',
|
||||||
|
diagramMessage: '收到消息',
|
||||||
|
diagramPreprocess: '预处理',
|
||||||
|
diagramAI: 'AI 处理',
|
||||||
|
diagramPostprocess: '后处理',
|
||||||
|
diagramOutput: '输出',
|
||||||
basicInfo: '基础信息',
|
basicInfo: '基础信息',
|
||||||
basicInfoDescription: '设置名称、图标和描述',
|
basicInfoDescription: '设置名称、图标和描述',
|
||||||
runnerSettings: '运行器',
|
runnerSettings: '运行器',
|
||||||
|
|||||||
@@ -171,7 +171,22 @@ test.describe('frontend CRUD smoke flows', () => {
|
|||||||
await installLangBotApiMocks(page, { authenticated: true });
|
await installLangBotApiMocks(page, { authenticated: true });
|
||||||
|
|
||||||
await page.goto('/home/agents?id=new');
|
await page.goto('/home/agents?id=new');
|
||||||
|
const agentTypeCard = page.locator('[data-processor-kind="agent"]');
|
||||||
|
const pipelineTypeCard = page.locator('[data-processor-kind="pipeline"]');
|
||||||
|
await expect(agentTypeCard).toHaveAttribute('data-slot', 'card');
|
||||||
|
await expect(pipelineTypeCard).toHaveAttribute('data-slot', 'card');
|
||||||
|
await expect(
|
||||||
|
agentTypeCard.getByRole('button', { name: /^Agent/ }),
|
||||||
|
).toHaveAttribute('aria-pressed', 'true');
|
||||||
|
await expect(page.getByTestId('agent-diagram')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('pipeline-diagram')).toHaveCount(0);
|
||||||
|
|
||||||
await page.getByRole('button', { name: /^Pipeline/ }).click();
|
await page.getByRole('button', { name: /^Pipeline/ }).click();
|
||||||
|
await expect(
|
||||||
|
pipelineTypeCard.getByRole('button', { name: /^Pipeline/ }),
|
||||||
|
).toHaveAttribute('aria-pressed', 'true');
|
||||||
|
await expect(page.getByTestId('pipeline-diagram')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('agent-diagram')).toHaveCount(0);
|
||||||
|
|
||||||
await expect(page.locator('input[name="name"]')).toBeVisible();
|
await expect(page.locator('input[name="name"]')).toBeVisible();
|
||||||
await page.locator('input[name="name"]').fill('Escalation Pipeline');
|
await page.locator('input[name="name"]').fill('Escalation Pipeline');
|
||||||
|
|||||||
Reference in New Issue
Block a user