Skip to content

Commit d7f1dd8

Browse files
committed
feat: enhance TurnDebugShow component with collapsible details and copy functionality for debug data
1 parent c64f763 commit d7f1dd8

1 file changed

Lines changed: 124 additions & 36 deletions

File tree

dev-demo/custom/TurnDebugShow.vue

Lines changed: 124 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,39 @@
3838
No debug data stored for this turn.
3939
</div>
4040

41-
<div
41+
<details
4242
v-for="sequence in debugSequences"
4343
:key="sequence.sequenceId"
4444
class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-700 dark:bg-slate-900"
4545
>
46-
<div class="flex flex-wrap items-start justify-between gap-3">
47-
<div>
48-
<div class="text-lg font-semibold text-slate-900 dark:text-white">
49-
Sequence #{{ sequence.sequenceId }}
50-
</div>
51-
<div class="mt-1 text-xs text-slate-500 dark:text-slate-400">
52-
Started {{ formatTimestamp(sequence.startedAt) }}
53-
<span v-if="sequence.endedAt"> • Ended {{ formatTimestamp(sequence.endedAt) }}</span>
54-
<span v-if="sequenceDuration(sequence)"> • {{ sequenceDuration(sequence) }}</span>
46+
<summary class="cursor-pointer list-none">
47+
<div class="flex flex-wrap items-start justify-between gap-3">
48+
<div>
49+
<div class="text-lg font-semibold text-slate-900 dark:text-white">
50+
Sequence #{{ sequence.sequenceId }}
51+
</div>
52+
<div class="mt-1 text-xs text-slate-500 dark:text-slate-400">
53+
Started {{ formatTimestamp(sequence.startedAt) }}
54+
<span v-if="sequence.endedAt"> • Ended {{ formatTimestamp(sequence.endedAt) }}</span>
55+
<span v-if="sequenceDuration(sequence)"> • {{ sequenceDuration(sequence) }}</span>
56+
</div>
5557
</div>
56-
</div>
5758

58-
<div
59-
class="rounded-full px-3 py-1 text-xs font-semibold uppercase tracking-wide"
60-
:class="sequence.resultType === 'tool_calls'
61-
? 'bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-200'
62-
: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/50 dark:text-emerald-200'"
63-
>
64-
{{ sequence.resultType.replace('_', ' ') }}
59+
<div class="flex items-center gap-3">
60+
<div
61+
class="rounded-full px-3 py-1 text-xs font-semibold uppercase tracking-wide"
62+
:class="sequence.resultType === 'tool_calls'
63+
? 'bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-200'
64+
: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/50 dark:text-emerald-200'"
65+
>
66+
{{ sequence.resultType.replace('_', ' ') }}
67+
</div>
68+
<div class="text-xs font-semibold uppercase tracking-[0.2em] text-slate-400 dark:text-slate-500">
69+
Toggle
70+
</div>
71+
</div>
6572
</div>
66-
</div>
73+
</summary>
6774

6875
<div class="mt-4 grid gap-3 md:grid-cols-4">
6976
<div class="rounded-lg bg-slate-50 p-3 dark:bg-slate-800/70">
@@ -93,22 +100,49 @@
93100
</div>
94101

95102
<section v-if="sequence.prompt" class="mt-4">
96-
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
97-
Prompt Sent To LLM
103+
<div class="mb-2 flex items-center justify-between gap-3">
104+
<div class="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
105+
Prompt Sent To LLM
106+
</div>
107+
<button
108+
type="button"
109+
class="rounded-md border border-slate-200 px-2.5 py-1 text-[11px] font-semibold text-slate-600 transition hover:bg-slate-100 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800"
110+
@click="copyText(`sequence-${sequence.sequenceId}-prompt`, sequence.prompt)"
111+
>
112+
{{ copyLabel(`sequence-${sequence.sequenceId}-prompt`) }}
113+
</button>
98114
</div>
99115
<pre class="max-h-80 overflow-auto rounded-lg bg-slate-950 p-4 text-xs leading-6 text-sky-100 whitespace-pre-wrap">{{ sequence.prompt }}</pre>
100116
</section>
101117

102118
<section v-if="sequence.reasoning" class="mt-4">
103-
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
104-
Reasoning
119+
<div class="mb-2 flex items-center justify-between gap-3">
120+
<div class="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
121+
Reasoning
122+
</div>
123+
<button
124+
type="button"
125+
class="rounded-md border border-slate-200 px-2.5 py-1 text-[11px] font-semibold text-slate-600 transition hover:bg-slate-100 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800"
126+
@click="copyText(`sequence-${sequence.sequenceId}-reasoning`, sequence.reasoning)"
127+
>
128+
{{ copyLabel(`sequence-${sequence.sequenceId}-reasoning`) }}
129+
</button>
105130
</div>
106131
<pre class="max-h-80 overflow-auto rounded-lg bg-slate-950 p-4 text-xs leading-6 text-slate-100 whitespace-pre-wrap">{{ sequence.reasoning }}</pre>
107132
</section>
108133

109134
<section v-if="sequence.text" class="mt-4">
110-
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
111-
Text
135+
<div class="mb-2 flex items-center justify-between gap-3">
136+
<div class="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
137+
Text
138+
</div>
139+
<button
140+
type="button"
141+
class="rounded-md border border-slate-200 px-2.5 py-1 text-[11px] font-semibold text-slate-600 transition hover:bg-slate-100 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800"
142+
@click="copyText(`sequence-${sequence.sequenceId}-text`, sequence.text)"
143+
>
144+
{{ copyLabel(`sequence-${sequence.sequenceId}-text`) }}
145+
</button>
112146
</div>
113147
<pre class="max-h-80 overflow-auto rounded-lg bg-slate-100 p-4 text-xs leading-6 text-slate-800 whitespace-pre-wrap dark:bg-slate-800 dark:text-slate-100">{{ sequence.text }}</pre>
114148
</section>
@@ -121,7 +155,6 @@
121155
<details
122156
v-for="(toolCall, toolCallIndex) in sequence.toolCalls"
123157
:key="`${sequence.sequenceId}-${toolCallIndex}-${toolCall.toolName}`"
124-
:open="toolCallIndex === 0"
125158
class="overflow-hidden rounded-lg border border-slate-200 bg-slate-50 dark:border-slate-700 dark:bg-slate-800/60"
126159
>
127160
<summary class="cursor-pointer list-none px-4 py-3">
@@ -142,41 +175,75 @@
142175

143176
<div class="grid gap-3 border-t border-slate-200 p-4 dark:border-slate-700">
144177
<div>
145-
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
146-
Input YAML
178+
<div class="mb-2 flex items-center justify-between gap-3">
179+
<div class="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
180+
Input YAML
181+
</div>
182+
<button
183+
type="button"
184+
class="rounded-md border border-slate-200 px-2.5 py-1 text-[11px] font-semibold text-slate-600 transition hover:bg-slate-100 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800"
185+
@click="copyText(`sequence-${sequence.sequenceId}-tool-${toolCall.toolCallId}-input`, toolCall.input)"
186+
>
187+
{{ copyLabel(`sequence-${sequence.sequenceId}-tool-${toolCall.toolCallId}-input`) }}
188+
</button>
147189
</div>
148190
<pre class="max-h-72 overflow-auto rounded-lg bg-slate-950 p-4 text-xs leading-6 text-slate-100 whitespace-pre-wrap">{{ toolCall.input }}</pre>
149191
</div>
150192

151193
<div v-if="toolCall.output">
152-
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
153-
Output YAML
194+
<div class="mb-2 flex items-center justify-between gap-3">
195+
<div class="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
196+
Output YAML
197+
</div>
198+
<button
199+
type="button"
200+
class="rounded-md border border-slate-200 px-2.5 py-1 text-[11px] font-semibold text-slate-600 transition hover:bg-slate-100 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800"
201+
@click="copyText(`sequence-${sequence.sequenceId}-tool-${toolCall.toolCallId}-output`, toolCall.output)"
202+
>
203+
{{ copyLabel(`sequence-${sequence.sequenceId}-tool-${toolCall.toolCallId}-output`) }}
204+
</button>
154205
</div>
155206
<pre class="max-h-72 overflow-auto rounded-lg bg-slate-950 p-4 text-xs leading-6 text-emerald-100 whitespace-pre-wrap">{{ toolCall.output }}</pre>
156207
</div>
157208

158209
<div v-if="toolCall.error">
159-
<div class="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-rose-500 dark:text-rose-300">
160-
Error YAML
210+
<div class="mb-2 flex items-center justify-between gap-3">
211+
<div class="text-xs font-semibold uppercase tracking-[0.2em] text-rose-500 dark:text-rose-300">
212+
Error YAML
213+
</div>
214+
<button
215+
type="button"
216+
class="rounded-md border border-rose-200 px-2.5 py-1 text-[11px] font-semibold text-rose-600 transition hover:bg-rose-50 dark:border-rose-900/70 dark:text-rose-300 dark:hover:bg-rose-950/60"
217+
@click="copyText(`sequence-${sequence.sequenceId}-tool-${toolCall.toolCallId}-error`, toolCall.error)"
218+
>
219+
{{ copyLabel(`sequence-${sequence.sequenceId}-tool-${toolCall.toolCallId}-error`) }}
220+
</button>
161221
</div>
162222
<pre class="max-h-72 overflow-auto rounded-lg bg-rose-950 p-4 text-xs leading-6 text-rose-100 whitespace-pre-wrap">{{ toolCall.error }}</pre>
163223
</div>
164224
</div>
165225
</details>
166226
</section>
167-
</div>
227+
</details>
168228

169229
<details class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-700 dark:bg-slate-900">
170-
<summary class="cursor-pointer text-sm font-semibold text-slate-900 dark:text-white">
171-
Raw JSON
230+
<summary class="flex cursor-pointer items-center justify-between gap-3 text-sm font-semibold text-slate-900 dark:text-white">
231+
<span>Raw JSON</span>
232+
<button
233+
type="button"
234+
class="rounded-md border border-slate-200 px-2.5 py-1 text-[11px] font-semibold text-slate-600 transition hover:bg-slate-100 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800"
235+
@click.prevent.stop="copyText('raw-json', rawJson)"
236+
>
237+
{{ copyLabel('raw-json') }}
238+
</button>
172239
</summary>
173240
<pre class="mt-4 max-h-[32rem] overflow-auto rounded-lg bg-slate-950 p-4 text-xs leading-6 text-slate-100 whitespace-pre-wrap">{{ rawJson }}</pre>
174241
</details>
175242
</div>
176243
</template>
177244

178245
<script setup lang="ts">
179-
import { computed } from 'vue';
246+
import { computed, ref } from 'vue';
180247
import type {
181248
AdminForthResourceColumnCommon,
182249
AdminForthResourceCommon,
@@ -225,6 +292,8 @@ const toolCallSequences = computed(() =>
225292
);
226293
227294
const rawJson = computed(() => JSON.stringify(debugSequences.value, null, 2));
295+
const copiedFieldKey = ref<string | null>(null);
296+
let copiedFieldResetTimer: ReturnType<typeof setTimeout> | null = null;
228297
229298
function formatTimestamp(value: string) {
230299
return new Date(value).toLocaleString();
@@ -241,4 +310,23 @@ function sequenceDuration(sequence: DebugSequence) {
241310
242311
return `${(durationMs / 1000).toFixed(2)} s`;
243312
}
313+
314+
async function copyText(key: string, value: string) {
315+
await navigator.clipboard.writeText(value);
316+
copiedFieldKey.value = key;
317+
318+
if (copiedFieldResetTimer) {
319+
clearTimeout(copiedFieldResetTimer);
320+
}
321+
322+
copiedFieldResetTimer = setTimeout(() => {
323+
if (copiedFieldKey.value === key) {
324+
copiedFieldKey.value = null;
325+
}
326+
}, 1600);
327+
}
328+
329+
function copyLabel(key: string) {
330+
return copiedFieldKey.value === key ? 'Copied' : 'Copy';
331+
}
244332
</script>

0 commit comments

Comments
 (0)