fix(agent-debug): simplify and localize tool call traces

This commit is contained in:
RockChinQ
2026-09-06 00:33:32 +08:00
parent 75f117ae50
commit 2a46325232
5 changed files with 78 additions and 37 deletions
@@ -5,7 +5,7 @@ import { toast } from 'sonner';
import { AlertTriangle, Trash2 } from 'lucide-react'; import { AlertTriangle, Trash2 } from 'lucide-react';
import { httpClient } from '@/app/infra/http/HttpClient'; import { httpClient } from '@/app/infra/http/HttpClient';
import { useCurrentWorkspace } from '@/app/infra/http'; import { useCurrentWorkspace } from '@/app/infra/http';
import { Agent } from '@/app/infra/entities/api'; import { Agent, AgentPlatformTool } from '@/app/infra/entities/api';
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext'; import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
import ProcessorDetailWorkbench from '@/app/home/components/processor-detail/ProcessorDetailWorkbench'; import ProcessorDetailWorkbench from '@/app/home/components/processor-detail/ProcessorDetailWorkbench';
import EntityBasicInfoDialog, { import EntityBasicInfoDialog, {
@@ -41,6 +41,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
currentWorkspace?.permissions.includes('runtime.operate') ?? false; currentWorkspace?.permissions.includes('runtime.operate') ?? false;
const { refreshPipelines, pipelines, setDetailEntityName } = useSidebarData(); const { refreshPipelines, pipelines, setDetailEntityName } = useSidebarData();
const [agent, setAgent] = useState<Agent | null>(null); const [agent, setAgent] = useState<Agent | null>(null);
const [platformTools, setPlatformTools] = useState<AgentPlatformTool[]>([]);
const [loading, setLoading] = useState(!isCreateMode); const [loading, setLoading] = useState(!isCreateMode);
const [formDirty, setFormDirty] = useState(false); const [formDirty, setFormDirty] = useState(false);
const [formSaving, setFormSaving] = useState(false); const [formSaving, setFormSaving] = useState(false);
@@ -224,14 +225,17 @@ export default function AgentDetailContent({ id }: { id: string }) {
onSavingChange={setFormSaving} onSavingChange={setFormSaving}
onRunnerStatusChange={setRunnerStatus} onRunnerStatusChange={setRunnerStatus}
onSupportedEventPatternsChange={setSupportedEventPatterns} onSupportedEventPatternsChange={setSupportedEventPatterns}
onPlatformToolsChange={setPlatformTools}
/> />
</fieldset> </fieldset>
} }
debugTitle={canOperate ? t('agents.debugTab') : undefined} debugTitle={canOperate ? t('agents.debugTab') : undefined}
debugDescription={t('agents.debugPlatformNotice')}
debugContent={ debugContent={
canOperate ? ( canOperate ? (
<AgentDebugPanel <AgentDebugPanel
agentId={id} agentId={id}
platformTools={platformTools}
hasUnsavedChanges={formDirty} hasUnsavedChanges={formDirty}
beforeRun={async () => agentFormRef.current?.save() ?? false} beforeRun={async () => agentFormRef.current?.save() ?? false}
onOpenRunnerConfig={() => onOpenRunnerConfig={() =>
@@ -10,6 +10,8 @@ import {
Play, Play,
} from 'lucide-react'; } from 'lucide-react';
import { httpClient } from '@/app/infra/http/HttpClient'; import { httpClient } from '@/app/infra/http/HttpClient';
import type { AgentPlatformTool } from '@/app/infra/entities/api';
import { extractI18nObject } from '@/i18n/I18nProvider';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
@@ -43,6 +45,7 @@ import { executionSteps, type DebugExecutionEvent } from './debug-execution';
interface AgentDebugPanelProps { interface AgentDebugPanelProps {
agentId: string; agentId: string;
availableEventTypes: string[]; availableEventTypes: string[];
platformTools?: AgentPlatformTool[];
supportedEventPatterns?: string[]; supportedEventPatterns?: string[];
beforeRun?: () => Promise<boolean>; beforeRun?: () => Promise<boolean>;
hasUnsavedChanges?: boolean; hasUnsavedChanges?: boolean;
@@ -171,12 +174,16 @@ function matchesEventPattern(pattern: string, eventType: string) {
export default function AgentDebugPanel({ export default function AgentDebugPanel({
agentId, agentId,
availableEventTypes, availableEventTypes,
platformTools = [],
supportedEventPatterns = ['*'], supportedEventPatterns = ['*'],
beforeRun, beforeRun,
hasUnsavedChanges = false, hasUnsavedChanges = false,
onOpenRunnerConfig, onOpenRunnerConfig,
}: AgentDebugPanelProps) { }: AgentDebugPanelProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const toolLabels = Object.fromEntries(
platformTools.map((tool) => [tool.name, extractI18nObject(tool.label)]),
);
const [preset, setPreset] = useState('message.received'); const [preset, setPreset] = useState('message.received');
const [customEventType, setCustomEventType] = useState('custom.event'); const [customEventType, setCustomEventType] = useState('custom.event');
const [inputText, setInputText] = useState(''); const [inputText, setInputText] = useState('');
@@ -428,9 +435,6 @@ export default function AgentDebugPanel({
return ( return (
<div className="flex h-full min-h-0 min-w-0 flex-col"> <div className="flex h-full min-h-0 min-w-0 flex-col">
<p className="shrink-0 border-b bg-muted/20 px-3 py-2 text-xs leading-relaxed text-muted-foreground">
{t('agents.debugPlatformNotice')}
</p>
<div ref={transcriptRef} className="min-h-0 flex-1 overflow-y-auto p-3"> <div ref={transcriptRef} className="min-h-0 flex-1 overflow-y-auto p-3">
<div className="mb-3"> <div className="mb-3">
<p className="text-sm font-medium">{t('agents.debugTranscript')}</p> <p className="text-sm font-medium">{t('agents.debugTranscript')}</p>
@@ -493,6 +497,7 @@ export default function AgentDebugPanel({
<AgentExecutionTrace <AgentExecutionTrace
events={entry.events} events={entry.events}
finished={entry.finished} finished={entry.finished}
toolLabels={toolLabels}
/> />
)} )}
{entry.text && ( {entry.text && (
@@ -22,9 +22,11 @@ const textClass =
export default function AgentExecutionTrace({ export default function AgentExecutionTrace({
events, events,
finished = false, finished = false,
toolLabels = {},
}: { }: {
events: DebugExecutionEvent[]; events: DebugExecutionEvent[];
finished?: boolean; finished?: boolean;
toolLabels?: Record<string, string>;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const steps = useMemo(() => executionSteps(events), [events]); const steps = useMemo(() => executionSteps(events), [events]);
@@ -36,9 +38,9 @@ export default function AgentExecutionTrace({
); );
return ( return (
<div className="min-w-0 space-y-3"> <div className="min-w-0 space-y-3">
{ended && ( {ended && toolCount > 0 && (
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{t(toolCount ? 'agents.debugToolCount' : 'agents.debugNoToolCalls', { {t('agents.debugToolCount', {
count: toolCount, count: toolCount,
})} })}
</p> </p>
@@ -68,15 +70,22 @@ export default function AgentExecutionTrace({
)} )}
</div> </div>
) : ( ) : (
<section <details
key={index} key={index}
className="min-w-0 space-y-2 rounded-md border bg-background p-3" className="min-w-0 rounded-md border bg-background p-3"
> >
<div className="flex flex-wrap items-center justify-between gap-2 text-xs"> <summary className="cursor-pointer text-xs">
<span className="min-w-0 break-all font-medium"> <span className="min-w-0 break-all font-medium">
<Wrench className="mr-1.5 inline size-3.5" /> <Wrench className="mr-1.5 inline size-3.5" />
{step.name} {toolLabels[step.name] || step.name}
</span> </span>
{toolLabels[step.name] && toolLabels[step.name] !== step.name && (
<code className="ml-2 break-all font-mono text-xs text-muted-foreground">
{step.name}
</code>
)}
</summary>
<div className="mt-2 space-y-2">
<span <span
className={ className={
step.status === 'failed' step.status === 'failed'
@@ -100,13 +109,12 @@ export default function AgentExecutionTrace({
: 'agents.debugToolCompleted', : 'agents.debugToolCompleted',
)} )}
</span> </span>
</div>
{step.parameters !== undefined && ( {step.parameters !== undefined && (
<div> <div>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{t('agents.debugToolArguments')} {t('agents.debugToolArguments')}
</p> </p>
<pre className="mt-1 max-h-64 overflow-auto whitespace-pre-wrap break-all rounded bg-muted/40 p-2 font-mono text-xs"> <pre className="mt-1 whitespace-pre-wrap break-all rounded bg-muted/40 p-2 font-mono text-xs">
{formatValue(step.parameters)} {formatValue(step.parameters)}
</pre> </pre>
</div> </div>
@@ -116,7 +124,7 @@ export default function AgentExecutionTrace({
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{t('agents.debugToolResult')} {t('agents.debugToolResult')}
</p> </p>
<pre className="mt-1 max-h-64 overflow-auto whitespace-pre-wrap break-all rounded bg-muted/40 p-2 font-mono text-xs"> <pre className="mt-1 whitespace-pre-wrap break-all rounded bg-muted/40 p-2 font-mono text-xs">
{formatValue(step.result)} {formatValue(step.result)}
</pre> </pre>
</div> </div>
@@ -126,7 +134,8 @@ export default function AgentExecutionTrace({
{step.error} {step.error}
</pre> </pre>
)} )}
</section> </div>
</details>
), ),
)} )}
</div> </div>
@@ -68,6 +68,7 @@ interface AgentFormComponentProps {
onSavingChange?: (saving: boolean) => void; onSavingChange?: (saving: boolean) => void;
onRunnerStatusChange?: (status: AgentRunnerStatus) => void; onRunnerStatusChange?: (status: AgentRunnerStatus) => void;
onSupportedEventPatternsChange?: (patterns: string[]) => void; onSupportedEventPatternsChange?: (patterns: string[]) => void;
onPlatformToolsChange?: (tools: AgentPlatformTool[]) => void;
} }
export type AgentConfigSection = export type AgentConfigSection =
@@ -124,6 +125,7 @@ function AgentFormComponent(
onSavingChange, onSavingChange,
onRunnerStatusChange, onRunnerStatusChange,
onSupportedEventPatternsChange, onSupportedEventPatternsChange,
onPlatformToolsChange,
}: AgentFormComponentProps, }: AgentFormComponentProps,
ref: ForwardedRef<AgentFormHandle>, ref: ForwardedRef<AgentFormHandle>,
) { ) {
@@ -131,6 +133,9 @@ function AgentFormComponent(
const [runnerConfigSchema, setRunnerConfigSchema] = const [runnerConfigSchema, setRunnerConfigSchema] =
useState<PipelineConfigTab | null>(null); useState<PipelineConfigTab | null>(null);
const [platformTools, setPlatformTools] = useState<AgentPlatformTool[]>([]); const [platformTools, setPlatformTools] = useState<AgentPlatformTool[]>([]);
useEffect(() => {
onPlatformToolsChange?.(platformTools);
}, [onPlatformToolsChange, platformTools]);
const [platformToolCatalogAvailable, setPlatformToolCatalogAvailable] = const [platformToolCatalogAvailable, setPlatformToolCatalogAvailable] =
useState(true); useState(true);
const [hostTools, setHostTools] = useState<PluginTool[]>([]); const [hostTools, setHostTools] = useState<PluginTool[]>([]);
@@ -1,5 +1,5 @@
import { ReactNode, useState } from 'react'; import { ReactNode, useState } from 'react';
import { BarChart3, Bug, Settings } from 'lucide-react'; import { BarChart3, Bug, Info, Settings } from 'lucide-react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
@@ -36,6 +36,7 @@ interface ProcessorDetailWorkbenchProps {
configTitle: string; configTitle: string;
configContent: ReactNode; configContent: ReactNode;
debugTitle?: string; debugTitle?: string;
debugDescription?: string;
debugContent?: ReactNode; debugContent?: ReactNode;
debugConnected?: boolean; debugConnected?: boolean;
debugConnectedLabel?: string; debugConnectedLabel?: string;
@@ -58,6 +59,7 @@ export default function ProcessorDetailWorkbench({
configTitle, configTitle,
configContent, configContent,
debugTitle, debugTitle,
debugDescription,
debugContent, debugContent,
debugConnected, debugConnected,
debugConnectedLabel, debugConnectedLabel,
@@ -196,6 +198,22 @@ export default function ProcessorDetailWorkbench({
<div className="flex min-w-0 items-center gap-2 font-medium"> <div className="flex min-w-0 items-center gap-2 font-medium">
<Bug className="size-4 shrink-0" /> <Bug className="size-4 shrink-0" />
<span className="truncate">{debugTitle}</span> <span className="truncate">{debugTitle}</span>
{debugDescription && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label={debugDescription}
className="inline-flex shrink-0 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
>
<Info className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent className="max-w-xs whitespace-normal leading-relaxed">
{debugDescription}
</TooltipContent>
</Tooltip>
)}
</div> </div>
{debugConnected !== undefined && ( {debugConnected !== undefined && (
<span className="flex items-center gap-1.5 text-xs text-muted-foreground"> <span className="flex items-center gap-1.5 text-xs text-muted-foreground">