mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-01 15:17:15 +00:00
fix(agents): align debug event selection
This commit is contained in:
@@ -49,6 +49,12 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
|||||||
const [runnerStatus, setRunnerStatus] = useState<AgentRunnerStatus | null>(
|
const [runnerStatus, setRunnerStatus] = useState<AgentRunnerStatus | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
const [availableEventTypes, setAvailableEventTypes] = useState<string[]>([
|
||||||
|
'message.received',
|
||||||
|
]);
|
||||||
|
const [supportedEventPatterns, setSupportedEventPatterns] = useState<
|
||||||
|
string[]
|
||||||
|
>(['*']);
|
||||||
const agentFormRef = useRef<AgentFormHandle>(null);
|
const agentFormRef = useRef<AgentFormHandle>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -70,10 +76,25 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
|||||||
if (isCreateMode) return;
|
if (isCreateMode) return;
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
httpClient
|
Promise.all([
|
||||||
.getAgent(id)
|
httpClient.getAgent(id),
|
||||||
.then((resp) => {
|
httpClient.getAdapters().catch(() => ({ adapters: [] })),
|
||||||
if (!cancelled) setAgent(resp.agent);
|
])
|
||||||
|
.then(([resp, adaptersResp]) => {
|
||||||
|
if (cancelled) return;
|
||||||
|
const adapterEvents = adaptersResp.adapters.flatMap(
|
||||||
|
(adapter) => adapter.spec.supported_events ?? [],
|
||||||
|
);
|
||||||
|
setAvailableEventTypes(
|
||||||
|
adapterEvents.length > 0
|
||||||
|
? Array.from(new Set(adapterEvents)).sort()
|
||||||
|
: ['message.received'],
|
||||||
|
);
|
||||||
|
setSupportedEventPatterns(
|
||||||
|
resp.agent.supported_event_patterns ??
|
||||||
|
resp.agent.capability?.supported_event_patterns ?? ['*'],
|
||||||
|
);
|
||||||
|
setAgent(resp.agent);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (!cancelled) setLoading(false);
|
if (!cancelled) setLoading(false);
|
||||||
@@ -177,6 +198,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
|||||||
<AgentFormComponent
|
<AgentFormComponent
|
||||||
ref={agentFormRef}
|
ref={agentFormRef}
|
||||||
agentId={id}
|
agentId={id}
|
||||||
|
availableEventTypes={availableEventTypes}
|
||||||
onFinish={(updatedAgent) => {
|
onFinish={(updatedAgent) => {
|
||||||
if (updatedAgent) {
|
if (updatedAgent) {
|
||||||
setAgent((current) =>
|
setAgent((current) =>
|
||||||
@@ -188,6 +210,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
|||||||
onDirtyChange={setFormDirty}
|
onDirtyChange={setFormDirty}
|
||||||
onSavingChange={setFormSaving}
|
onSavingChange={setFormSaving}
|
||||||
onRunnerStatusChange={setRunnerStatus}
|
onRunnerStatusChange={setRunnerStatus}
|
||||||
|
onSupportedEventPatternsChange={setSupportedEventPatterns}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
}
|
}
|
||||||
@@ -201,10 +224,8 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
|||||||
onOpenRunnerConfig={() =>
|
onOpenRunnerConfig={() =>
|
||||||
agentFormRef.current?.openSection('runner_config')
|
agentFormRef.current?.openSection('runner_config')
|
||||||
}
|
}
|
||||||
supportedEventPatterns={
|
supportedEventPatterns={supportedEventPatterns}
|
||||||
agent.supported_event_patterns ??
|
availableEventTypes={availableEventTypes}
|
||||||
agent.capability?.supported_event_patterns ?? ['*']
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import { Label } from '@/components/ui/label';
|
|||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} from '@/components/ui/select';
|
||||||
@@ -28,9 +30,16 @@ import {
|
|||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from '@/components/ui/collapsible';
|
} from '@/components/ui/collapsible';
|
||||||
|
import {
|
||||||
|
eventGroupLabel,
|
||||||
|
eventPatternDescription,
|
||||||
|
eventPatternLabel,
|
||||||
|
groupEventPatterns,
|
||||||
|
} from '@/app/home/components/event-patterns/event-pattern-groups';
|
||||||
|
|
||||||
interface AgentDebugPanelProps {
|
interface AgentDebugPanelProps {
|
||||||
agentId: string;
|
agentId: string;
|
||||||
|
availableEventTypes: string[];
|
||||||
supportedEventPatterns?: string[];
|
supportedEventPatterns?: string[];
|
||||||
beforeRun?: () => Promise<boolean>;
|
beforeRun?: () => Promise<boolean>;
|
||||||
hasUnsavedChanges?: boolean;
|
hasUnsavedChanges?: boolean;
|
||||||
@@ -46,16 +55,15 @@ interface DebugEntry {
|
|||||||
detail?: string;
|
detail?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EVENT_PRESETS = [
|
const EVENT_PRESET_DATA: Record<
|
||||||
{
|
string,
|
||||||
value: 'message.received',
|
{ text: string; data: Record<string, unknown> }
|
||||||
labelKey: 'agents.debugMessageReceived',
|
> = {
|
||||||
|
'message.received': {
|
||||||
text: '',
|
text: '',
|
||||||
data: {},
|
data: {},
|
||||||
},
|
},
|
||||||
{
|
'group.member_joined': {
|
||||||
value: 'group.member.joined',
|
|
||||||
labelKey: 'agents.debugGroupMemberJoined',
|
|
||||||
text: 'A new member joined the group.',
|
text: 'A new member joined the group.',
|
||||||
data: {
|
data: {
|
||||||
group_id: 'debug-group',
|
group_id: 'debug-group',
|
||||||
@@ -63,9 +71,7 @@ const EVENT_PRESETS = [
|
|||||||
member_name: 'Debug User',
|
member_name: 'Debug User',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
'group.member_left': {
|
||||||
value: 'group.member.left',
|
|
||||||
labelKey: 'agents.debugGroupMemberLeft',
|
|
||||||
text: 'A member left the group.',
|
text: 'A member left the group.',
|
||||||
data: {
|
data: {
|
||||||
group_id: 'debug-group',
|
group_id: 'debug-group',
|
||||||
@@ -73,9 +79,7 @@ const EVENT_PRESETS = [
|
|||||||
member_name: 'Debug User',
|
member_name: 'Debug User',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
'friend.request_received': {
|
||||||
value: 'friend.requested',
|
|
||||||
labelKey: 'agents.debugFriendRequested',
|
|
||||||
text: 'A user sent a friend request.',
|
text: 'A user sent a friend request.',
|
||||||
data: {
|
data: {
|
||||||
requester_id: 'debug-user',
|
requester_id: 'debug-user',
|
||||||
@@ -83,22 +87,14 @@ const EVENT_PRESETS = [
|
|||||||
message: 'Hello',
|
message: 'Hello',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
'feedback.received': {
|
||||||
value: 'feedback.received',
|
|
||||||
labelKey: 'agents.debugFeedbackReceived',
|
|
||||||
text: 'The user submitted feedback.',
|
text: 'The user submitted feedback.',
|
||||||
data: {
|
data: {
|
||||||
rating: 5,
|
rating: 5,
|
||||||
content: 'Debug feedback',
|
content: 'Debug feedback',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
};
|
||||||
value: 'custom',
|
|
||||||
labelKey: 'agents.debugCustomEvent',
|
|
||||||
text: '',
|
|
||||||
data: {},
|
|
||||||
},
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
function createDebugSessionId(agentId: string) {
|
function createDebugSessionId(agentId: string) {
|
||||||
const nonce = globalThis.crypto?.randomUUID?.() ?? String(Date.now());
|
const nonce = globalThis.crypto?.randomUUID?.() ?? String(Date.now());
|
||||||
@@ -112,6 +108,7 @@ function matchesEventPattern(pattern: string, eventType: string) {
|
|||||||
|
|
||||||
export default function AgentDebugPanel({
|
export default function AgentDebugPanel({
|
||||||
agentId,
|
agentId,
|
||||||
|
availableEventTypes,
|
||||||
supportedEventPatterns = ['*'],
|
supportedEventPatterns = ['*'],
|
||||||
beforeRun,
|
beforeRun,
|
||||||
hasUnsavedChanges = false,
|
hasUnsavedChanges = false,
|
||||||
@@ -132,27 +129,39 @@ export default function AgentDebugPanel({
|
|||||||
() => supportedEventPatterns.join(', '),
|
() => supportedEventPatterns.join(', '),
|
||||||
[supportedEventPatterns],
|
[supportedEventPatterns],
|
||||||
);
|
);
|
||||||
const availablePresets = useMemo(
|
const availableEvents = useMemo(() => {
|
||||||
() =>
|
const concretePatterns = supportedEventPatterns.filter(
|
||||||
EVENT_PRESETS.filter(
|
(pattern) => pattern !== '*' && !pattern.endsWith('.*'),
|
||||||
(item) =>
|
);
|
||||||
item.value === 'custom' ||
|
return Array.from(new Set([...availableEventTypes, ...concretePatterns]))
|
||||||
supportedEventPatterns.some((pattern) =>
|
.filter((candidate) =>
|
||||||
matchesEventPattern(pattern, item.value),
|
supportedEventPatterns.some((pattern) =>
|
||||||
),
|
matchesEventPattern(pattern, candidate),
|
||||||
),
|
),
|
||||||
[supportedEventPatterns],
|
)
|
||||||
|
.sort();
|
||||||
|
}, [availableEventTypes, supportedEventPatterns]);
|
||||||
|
const eventGroups = useMemo(
|
||||||
|
() => groupEventPatterns(availableEvents),
|
||||||
|
[availableEvents],
|
||||||
|
);
|
||||||
|
const supportsCustomEvent = supportedEventPatterns.some(
|
||||||
|
(pattern) => pattern === '*' || pattern.endsWith('.*'),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (availablePresets.some((item) => item.value === preset)) return;
|
if (
|
||||||
selectPreset(availablePresets[0]?.value ?? 'custom');
|
availableEvents.includes(preset) ||
|
||||||
}, [availablePresets, preset]);
|
(preset === 'custom' && supportsCustomEvent)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectPreset(availableEvents[0] ?? 'custom');
|
||||||
|
}, [availableEvents, preset, supportsCustomEvent]);
|
||||||
|
|
||||||
function selectPreset(value: string) {
|
function selectPreset(value: string) {
|
||||||
setPreset(value);
|
setPreset(value);
|
||||||
const nextPreset = EVENT_PRESETS.find((item) => item.value === value);
|
const nextPreset = EVENT_PRESET_DATA[value] ?? { text: '', data: {} };
|
||||||
if (!nextPreset) return;
|
|
||||||
setInputText(nextPreset.text);
|
setInputText(nextPreset.text);
|
||||||
setEventDataText(JSON.stringify(nextPreset.data, null, 2));
|
setEventDataText(JSON.stringify(nextPreset.data, null, 2));
|
||||||
}
|
}
|
||||||
@@ -275,15 +284,42 @@ export default function AgentDebugPanel({
|
|||||||
<div className="min-w-0 flex-1 space-y-1.5">
|
<div className="min-w-0 flex-1 space-y-1.5">
|
||||||
<Label>{t('agents.debugEventType')}</Label>
|
<Label>{t('agents.debugEventType')}</Label>
|
||||||
<Select value={preset} onValueChange={selectPreset}>
|
<Select value={preset} onValueChange={selectPreset}>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger
|
||||||
|
className="w-full"
|
||||||
|
aria-label={t('agents.debugEventType')}
|
||||||
|
>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent className="w-[var(--radix-select-trigger-width)] max-w-[calc(100vw-2rem)]">
|
||||||
{availablePresets.map((item) => (
|
{eventGroups.map((group) => (
|
||||||
<SelectItem key={item.value} value={item.value}>
|
<SelectGroup key={group.namespace}>
|
||||||
{t(item.labelKey)}
|
<SelectLabel>
|
||||||
</SelectItem>
|
{eventGroupLabel(group.namespace, t)}
|
||||||
|
</SelectLabel>
|
||||||
|
{group.patterns.map((event) => (
|
||||||
|
<SelectItem
|
||||||
|
key={event}
|
||||||
|
value={event}
|
||||||
|
description={eventPatternDescription(event, t)}
|
||||||
|
className="py-2"
|
||||||
|
>
|
||||||
|
{eventPatternLabel(event, t)}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
))}
|
))}
|
||||||
|
{supportsCustomEvent && (
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>{t('agents.debugCustomEvent')}</SelectLabel>
|
||||||
|
<SelectItem
|
||||||
|
value="custom"
|
||||||
|
description={t('bots.eventDescriptions.custom')}
|
||||||
|
className="py-2"
|
||||||
|
>
|
||||||
|
{t('agents.debugCustomEvent')}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import { cn } from '@/lib/utils';
|
|||||||
import {
|
import {
|
||||||
eventGroupLabel,
|
eventGroupLabel,
|
||||||
eventNamespaces,
|
eventNamespaces,
|
||||||
|
eventPatternDescription,
|
||||||
|
eventPatternLabel,
|
||||||
groupEventPatterns,
|
groupEventPatterns,
|
||||||
} from '@/app/home/components/event-patterns/event-pattern-groups';
|
} from '@/app/home/components/event-patterns/event-pattern-groups';
|
||||||
|
|
||||||
@@ -61,30 +63,6 @@ export default function AgentEventPatternPicker({
|
|||||||
}, [events, selectedPatterns]);
|
}, [events, selectedPatterns]);
|
||||||
const optionGroups = useMemo(() => groupEventPatterns(options), [options]);
|
const optionGroups = useMemo(() => groupEventPatterns(options), [options]);
|
||||||
|
|
||||||
function eventLabel(pattern: string) {
|
|
||||||
if (pattern === '*') return t('bots.eventWildcard');
|
|
||||||
if (pattern.endsWith('.*')) {
|
|
||||||
return t('bots.eventNamespaceWildcard', {
|
|
||||||
namespace: pattern.replace('.*', ''),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const key = `bots.eventNames.${pattern.replace(/\./g, '_')}`;
|
|
||||||
const label = t(key);
|
|
||||||
return label === key ? pattern : label;
|
|
||||||
}
|
|
||||||
|
|
||||||
function eventDescription(pattern: string) {
|
|
||||||
if (pattern === '*') return t('bots.eventDescriptions.all');
|
|
||||||
if (pattern.endsWith('.*')) {
|
|
||||||
return t('bots.eventDescriptions.namespace');
|
|
||||||
}
|
|
||||||
const key = `bots.eventDescriptions.${pattern.replace(/\./g, '_')}`;
|
|
||||||
const description = t(key);
|
|
||||||
return description === key
|
|
||||||
? t('bots.eventDescriptions.custom')
|
|
||||||
: description;
|
|
||||||
}
|
|
||||||
|
|
||||||
function togglePattern(pattern: string) {
|
function togglePattern(pattern: string) {
|
||||||
if (pattern === '*') {
|
if (pattern === '*') {
|
||||||
onChange(['*']);
|
onChange(['*']);
|
||||||
@@ -127,7 +105,9 @@ export default function AgentEventPatternPicker({
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="max-w-full rounded-md font-normal"
|
className="max-w-full rounded-md font-normal"
|
||||||
>
|
>
|
||||||
<span className="truncate">{eventLabel(pattern)}</span>
|
<span className="truncate">
|
||||||
|
{eventPatternLabel(pattern, t)}
|
||||||
|
</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
{selectedPatterns.length > 3 && (
|
{selectedPatterns.length > 3 && (
|
||||||
@@ -157,7 +137,7 @@ export default function AgentEventPatternPicker({
|
|||||||
return (
|
return (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={pattern}
|
key={pattern}
|
||||||
value={`${eventLabel(pattern)} ${pattern}`}
|
value={`${eventPatternLabel(pattern, t)} ${pattern}`}
|
||||||
onSelect={() => togglePattern(pattern)}
|
onSelect={() => togglePattern(pattern)}
|
||||||
className="items-start gap-2 py-2"
|
className="items-start gap-2 py-2"
|
||||||
>
|
>
|
||||||
@@ -170,14 +150,14 @@ export default function AgentEventPatternPicker({
|
|||||||
<span className="min-w-0 flex-1">
|
<span className="min-w-0 flex-1">
|
||||||
<span className="flex min-w-0 items-center gap-2">
|
<span className="flex min-w-0 items-center gap-2">
|
||||||
<span className="truncate font-medium">
|
<span className="truncate font-medium">
|
||||||
{eventLabel(pattern)}
|
{eventPatternLabel(pattern, t)}
|
||||||
</span>
|
</span>
|
||||||
<code className="shrink-0 text-[10px] text-muted-foreground">
|
<code className="shrink-0 text-[10px] text-muted-foreground">
|
||||||
{pattern}
|
{pattern}
|
||||||
</code>
|
</code>
|
||||||
</span>
|
</span>
|
||||||
<span className="mt-0.5 block text-xs text-muted-foreground">
|
<span className="mt-0.5 block text-xs text-muted-foreground">
|
||||||
{eventDescription(pattern)}
|
{eventPatternDescription(pattern, t)}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
|
|||||||
@@ -47,10 +47,12 @@ export interface AgentRunnerStatus {
|
|||||||
|
|
||||||
interface AgentFormComponentProps {
|
interface AgentFormComponentProps {
|
||||||
agentId: string;
|
agentId: string;
|
||||||
|
availableEventTypes: string[];
|
||||||
onFinish: (agent?: Partial<Agent>) => void;
|
onFinish: (agent?: Partial<Agent>) => void;
|
||||||
onDirtyChange?: (dirty: boolean) => void;
|
onDirtyChange?: (dirty: boolean) => void;
|
||||||
onSavingChange?: (saving: boolean) => void;
|
onSavingChange?: (saving: boolean) => void;
|
||||||
onRunnerStatusChange?: (status: AgentRunnerStatus) => void;
|
onRunnerStatusChange?: (status: AgentRunnerStatus) => void;
|
||||||
|
onSupportedEventPatternsChange?: (patterns: string[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AgentConfigSection = 'events' | 'runner' | 'runner_config';
|
export type AgentConfigSection = 'events' | 'runner' | 'runner_config';
|
||||||
@@ -98,10 +100,12 @@ function isRunnerFieldVisible(
|
|||||||
function AgentFormComponent(
|
function AgentFormComponent(
|
||||||
{
|
{
|
||||||
agentId,
|
agentId,
|
||||||
|
availableEventTypes,
|
||||||
onFinish,
|
onFinish,
|
||||||
onDirtyChange,
|
onDirtyChange,
|
||||||
onSavingChange,
|
onSavingChange,
|
||||||
onRunnerStatusChange,
|
onRunnerStatusChange,
|
||||||
|
onSupportedEventPatternsChange,
|
||||||
}: AgentFormComponentProps,
|
}: AgentFormComponentProps,
|
||||||
ref: ForwardedRef<AgentFormHandle>,
|
ref: ForwardedRef<AgentFormHandle>,
|
||||||
) {
|
) {
|
||||||
@@ -112,9 +116,6 @@ function AgentFormComponent(
|
|||||||
useState<ApiRespPluginSystemStatus | null>(null);
|
useState<ApiRespPluginSystemStatus | null>(null);
|
||||||
const [pluginStatusLoading, setPluginStatusLoading] = useState(true);
|
const [pluginStatusLoading, setPluginStatusLoading] = useState(true);
|
||||||
const [pluginStatusError, setPluginStatusError] = useState(false);
|
const [pluginStatusError, setPluginStatusError] = useState(false);
|
||||||
const [availableEventTypes, setAvailableEventTypes] = useState<string[]>([
|
|
||||||
'message.received',
|
|
||||||
]);
|
|
||||||
const [activeSection, setActiveSection] =
|
const [activeSection, setActiveSection] =
|
||||||
useState<AgentConfigSection>('runner');
|
useState<AgentConfigSection>('runner');
|
||||||
const isSavingRef = useRef(false);
|
const isSavingRef = useRef(false);
|
||||||
@@ -159,24 +160,17 @@ function AgentFormComponent(
|
|||||||
onDirtyChange?.(hasUnsavedChanges);
|
onDirtyChange?.(hasUnsavedChanges);
|
||||||
}, [hasUnsavedChanges, onDirtyChange]);
|
}, [hasUnsavedChanges, onDirtyChange]);
|
||||||
|
|
||||||
|
const supportedEventPatterns = form.watch('supported_event_patterns');
|
||||||
|
useEffect(() => {
|
||||||
|
onSupportedEventPatternsChange?.(supportedEventPatterns);
|
||||||
|
}, [onSupportedEventPatternsChange, supportedEventPatterns]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
Promise.all([
|
Promise.all([httpClient.getAgentMetadata(), httpClient.getAgent(agentId)])
|
||||||
httpClient.getAgentMetadata(),
|
.then(([metadata, resp]) => {
|
||||||
httpClient.getAgent(agentId),
|
|
||||||
httpClient.getAdapters().catch(() => ({ adapters: [] })),
|
|
||||||
])
|
|
||||||
.then(([metadata, resp, adaptersResp]) => {
|
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
setRunnerConfigSchema(metadata.runner_config ?? null);
|
setRunnerConfigSchema(metadata.runner_config ?? null);
|
||||||
const adapterEvents = adaptersResp.adapters.flatMap(
|
|
||||||
(adapter) => adapter.spec.supported_events ?? [],
|
|
||||||
);
|
|
||||||
setAvailableEventTypes(
|
|
||||||
adapterEvents.length > 0
|
|
||||||
? Array.from(new Set(adapterEvents)).sort()
|
|
||||||
: ['message.received'],
|
|
||||||
);
|
|
||||||
const agent = resp.agent;
|
const agent = resp.agent;
|
||||||
const config = (agent.config ?? {}) as Record<string, any>;
|
const config = (agent.config ?? {}) as Record<string, any>;
|
||||||
const loadedValues: FormValues = {
|
const loadedValues: FormValues = {
|
||||||
|
|||||||
@@ -55,3 +55,25 @@ export function eventGroupLabel(namespace: string, t: TFunction) {
|
|||||||
const label = t(key);
|
const label = t(key);
|
||||||
return label === key ? namespace : label;
|
return label === key ? namespace : label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function eventPatternLabel(pattern: string, t: TFunction) {
|
||||||
|
if (pattern === '*') return t('bots.eventWildcard');
|
||||||
|
if (pattern.endsWith('.*')) {
|
||||||
|
return t('bots.eventNamespaceWildcard', {
|
||||||
|
namespace: pattern.replace('.*', ''),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const key = `bots.eventNames.${pattern.replace(/\./g, '_')}`;
|
||||||
|
const label = t(key);
|
||||||
|
return label === key ? pattern : label;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function eventPatternDescription(pattern: string, t: TFunction) {
|
||||||
|
if (pattern === '*') return t('bots.eventDescriptions.all');
|
||||||
|
if (pattern.endsWith('.*')) {
|
||||||
|
return t('bots.eventDescriptions.namespace');
|
||||||
|
}
|
||||||
|
const key = `bots.eventDescriptions.${pattern.replace(/\./g, '_')}`;
|
||||||
|
const description = t(key);
|
||||||
|
return description === key ? t('bots.eventDescriptions.custom') : description;
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,21 @@ test.describe('processor detail workbench', () => {
|
|||||||
expect(debugBox!.x).toBeLessThan(configBox!.x);
|
expect(debugBox!.x).toBeLessThan(configBox!.x);
|
||||||
expect(configBox!.width).toBeGreaterThan(debugBox!.width);
|
expect(configBox!.width).toBeGreaterThan(debugBox!.width);
|
||||||
|
|
||||||
|
const debugEventPicker = debugPanel.getByRole('combobox', {
|
||||||
|
name: 'Event type',
|
||||||
|
});
|
||||||
|
await debugEventPicker.click();
|
||||||
|
await expect(page.getByRole('group', { name: 'Messages' })).toBeVisible();
|
||||||
|
await expect(page.getByRole('group', { name: 'Groups' })).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('option').filter({ hasText: 'Member joined group' }),
|
||||||
|
).toContainText('A member joins a group where the bot is present.');
|
||||||
|
await expect(
|
||||||
|
page.getByRole('option').filter({ hasText: 'Message edited' }),
|
||||||
|
).toContainText('The platform reports that an existing message changed.');
|
||||||
|
await expect(page.getByRole('option')).toHaveCount(6);
|
||||||
|
await page.keyboard.press('Escape');
|
||||||
|
|
||||||
const appShell = page.locator('[class*="group/sidebar-wrapper"]');
|
const appShell = page.locator('[class*="group/sidebar-wrapper"]');
|
||||||
const sidebarInset = page.locator('[data-slot="sidebar-inset"]');
|
const sidebarInset = page.locator('[data-slot="sidebar-inset"]');
|
||||||
await expect(appShell).toHaveCSS('overflow', 'clip');
|
await expect(appShell).toHaveCSS('overflow', 'clip');
|
||||||
@@ -108,6 +123,25 @@ test.describe('processor detail workbench', () => {
|
|||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(page.getByRole('group', { name: 'Messages' })).toHaveCount(1);
|
await expect(page.getByRole('group', { name: 'Messages' })).toHaveCount(1);
|
||||||
await expect(page.getByRole('group', { name: 'Groups' })).toHaveCount(1);
|
await expect(page.getByRole('group', { name: 'Groups' })).toHaveCount(1);
|
||||||
|
await page
|
||||||
|
.getByRole('option')
|
||||||
|
.filter({ hasText: 'message.*' })
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
await page.keyboard.press('Escape');
|
||||||
|
|
||||||
|
await debugEventPicker.click();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('option').filter({ hasText: 'Message edited' }),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.getByRole('option').filter({ hasText: 'Member joined group' }),
|
||||||
|
).toHaveCount(0);
|
||||||
|
await expect(page.getByRole('option')).toHaveCount(3);
|
||||||
|
await page.keyboard.press('Escape');
|
||||||
|
|
||||||
|
await eventPicker.click();
|
||||||
|
await page.getByRole('option').filter({ hasText: 'All events' }).click();
|
||||||
await page.keyboard.press('Escape');
|
await page.keyboard.press('Escape');
|
||||||
|
|
||||||
await flow.getByRole('tab').nth(1).click();
|
await flow.getByRole('tab').nth(1).click();
|
||||||
|
|||||||
Reference in New Issue
Block a user