diff --git a/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx b/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx index 0ba6278e..e5ce4596 100644 --- a/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx +++ b/ui/src/app/workflow/[workflowId]/run/[runId]/page.tsx @@ -19,7 +19,10 @@ import posthog from 'posthog-js'; import { useEffect, useRef, useState } from 'react'; import WorkflowLayout from '@/app/workflow/WorkflowLayout'; -import { getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet } from '@/client/sdk.gen'; +import { + getWorkflowApiV1WorkflowFetchWorkflowIdGet, + getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet, +} from '@/client/sdk.gen'; import { MediaPreviewButton, MediaPreviewDialog } from '@/components/MediaPreviewDialog'; import { OnboardingTooltip } from '@/components/onboarding/OnboardingTooltip'; import { Button } from '@/components/ui/button'; @@ -88,6 +91,35 @@ function MetricCard({ label, value }: { label: string; value: string }) { ); } +function CopyDebugIdButton({ label, value }: { label: string; value: string }) { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(value); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+
+

{label}

+

{value}

+
+ +
+ ); +} + function buildWaveformPeaks(audioBuffer: AudioBuffer) { const channel = audioBuffer.getChannelData(0); const samplesPerBar = Math.max(1, Math.floor(channel.length / WAVEFORM_BAR_COUNT)); @@ -569,6 +601,7 @@ export default function WorkflowRunPage() { const [isLoading, setIsLoading] = useState(true); const auth = useAuth(); const [workflowRun, setWorkflowRun] = useState(null); + const [workflowName, setWorkflowName] = useState(null); const { hasSeenTooltip, markTooltipSeen } = useOnboarding(); const customizeButtonRef = useRef(null); @@ -586,37 +619,52 @@ export default function WorkflowRunPage() { if (!auth.isAuthenticated || auth.loading) return; setIsLoading(true); - const workflowId = params.workflowId; - const runId = params.runId; - const response = await getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet({ - path: { - workflow_id: Number(workflowId), - run_id: Number(runId), - }, - }); - setIsLoading(false); - const runData = { - mode: response.data?.mode ?? '', - is_completed: response.data?.is_completed ?? false, - transcript_url: response.data?.transcript_url ?? null, - recording_url: response.data?.recording_url ?? null, - user_recording_url: response.data?.user_recording_url ?? null, - bot_recording_url: response.data?.bot_recording_url ?? null, - cost_info: response.data?.cost_info ?? null, - initial_context: response.data?.initial_context as Record | null ?? null, - gathered_context: response.data?.gathered_context as Record | null ?? null, - logs: response.data?.logs as WorkflowRunLogs | null ?? null, - annotations: response.data?.annotations as Record | null ?? null, - }; - setWorkflowRun(runData); - posthog.capture(PostHogEvent.WORKFLOW_RUN_DETAILS_VIEWED, { - workflow_id: Number(workflowId), - run_id: Number(runId), - is_completed: runData.is_completed, - has_recording: !!runData.recording_url, - has_split_recordings: !!runData.user_recording_url && !!runData.bot_recording_url, - has_transcript: !!runData.transcript_url, - }); + setWorkflowName(null); + const workflowId = Number(params.workflowId); + const runId = Number(params.runId); + + try { + const [runResponse, workflowResponse] = await Promise.all([ + getWorkflowRunApiV1WorkflowWorkflowIdRunsRunIdGet({ + path: { + workflow_id: workflowId, + run_id: runId, + }, + }), + getWorkflowApiV1WorkflowFetchWorkflowIdGet({ + path: { + workflow_id: workflowId, + }, + }), + ]); + + setWorkflowName(workflowResponse.data?.name ?? null); + const runData = { + mode: runResponse.data?.mode ?? '', + is_completed: runResponse.data?.is_completed ?? false, + transcript_url: runResponse.data?.transcript_url ?? null, + recording_url: runResponse.data?.recording_url ?? null, + user_recording_url: runResponse.data?.user_recording_url ?? null, + bot_recording_url: runResponse.data?.bot_recording_url ?? null, + cost_info: runResponse.data?.cost_info ?? null, + initial_context: runResponse.data?.initial_context as Record | null ?? null, + gathered_context: runResponse.data?.gathered_context as Record | null ?? null, + logs: runResponse.data?.logs as WorkflowRunLogs | null ?? null, + annotations: runResponse.data?.annotations as Record | null ?? null, + }; + setWorkflowRun(runData); + posthog.capture(PostHogEvent.WORKFLOW_RUN_DETAILS_VIEWED, { + workflow_id: workflowId, + workflow_name: workflowResponse.data?.name ?? null, + run_id: runId, + is_completed: runData.is_completed, + has_recording: !!runData.recording_url, + has_split_recordings: !!runData.user_recording_url && !!runData.bot_recording_url, + has_transcript: !!runData.transcript_url, + }); + } finally { + setIsLoading(false); + } }; fetchWorkflowRun(); }, [params.workflowId, params.runId, auth]); @@ -627,6 +675,8 @@ export default function WorkflowRunPage() { const userSplitRecordingUrl = workflowRun?.user_recording_url ?? null; const botSplitRecordingUrl = workflowRun?.bot_recording_url ?? null; const hasSplitTracks = Boolean(userSplitRecordingUrl && botSplitRecordingUrl); + const workflowId = String(params.workflowId); + const runId = String(params.runId); if (isLoading) { returnValue = ( @@ -656,22 +706,43 @@ export default function WorkflowRunPage() {
- -
- - {isTextChatRun ? 'Text Chat Session' : 'Agent Run Completed'} - -
- {isTextChatRun ? ( - - ) : ( - - - - )} + +
+ {workflowName && ( +
+
+ +
+
+

+ Agent +

+

+ {workflowName} +

+
+
+ )} +
+ + +
+
+ + {isTextChatRun ? 'Text Chat Session' : 'Agent Run Completed'} + +
+ {isTextChatRun ? ( + + ) : ( + + + + )} +
-
+