feat: add JsonTreeViewer component and refactor AgentRunLogDetailDialog to use it for JSON display
This commit is contained in:
@@ -5,6 +5,7 @@ import { BotMessageSquareIcon, WorkflowIcon } from "lucide-react"
|
|||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
import { ImMessageHTML } from "@/components/im-message-html"
|
import { ImMessageHTML } from "@/components/im-message-html"
|
||||||
|
import { JsonTreeViewer } from "@/components/json-tree-viewer"
|
||||||
import { ProjectDialog } from "@/components/project-dialog"
|
import { ProjectDialog } from "@/components/project-dialog"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { fetchAgentRunLog, type AgentRunLog } from "@/lib/api/admin"
|
import { fetchAgentRunLog, type AgentRunLog } from "@/lib/api/admin"
|
||||||
@@ -149,21 +150,15 @@ export function AgentRunLogDetailDialog({
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextBlock
|
<JsonBlock
|
||||||
title="动态工具选择"
|
title="动态工具选择"
|
||||||
value={
|
jsonValue={activeToolSearchTrace}
|
||||||
activeToolSearchTrace
|
fallbackValue={activeLog.toolSearchTrace}
|
||||||
? JSON.stringify(activeToolSearchTrace, null, 2)
|
|
||||||
: activeLog.toolSearchTrace
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<TextBlock
|
<JsonBlock
|
||||||
title="Graph Tool 调用"
|
title="Graph Tool 调用"
|
||||||
value={
|
jsonValue={activeGraphToolTrace}
|
||||||
activeGraphToolTrace
|
fallbackValue={activeLog.graphToolTrace}
|
||||||
? JSON.stringify(activeGraphToolTrace, null, 2)
|
|
||||||
: activeLog.graphToolTrace
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
icon={<BotMessageSquareIcon className="size-4" />}
|
icon={<BotMessageSquareIcon className="size-4" />}
|
||||||
@@ -177,13 +172,10 @@ export function AgentRunLogDetailDialog({
|
|||||||
value={activeLog.replyText}
|
value={activeLog.replyText}
|
||||||
/>
|
/>
|
||||||
<TextBlock title="错误信息" value={activeLog.errorMessage} tone="danger" />
|
<TextBlock title="错误信息" value={activeLog.errorMessage} tone="danger" />
|
||||||
<TextBlock
|
<JsonBlock
|
||||||
title="链路 Trace"
|
title="链路 Trace"
|
||||||
value={
|
jsonValue={activeTraceData}
|
||||||
activeTraceData
|
fallbackValue={activeLog.traceData}
|
||||||
? JSON.stringify(activeTraceData, null, 2)
|
|
||||||
: activeLog.traceData
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -283,6 +275,31 @@ function TextBlock({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function JsonBlock({
|
||||||
|
title,
|
||||||
|
jsonValue,
|
||||||
|
fallbackValue,
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
jsonValue: unknown
|
||||||
|
fallbackValue?: string
|
||||||
|
}) {
|
||||||
|
const normalizedFallback = fallbackValue?.trim() || ""
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-lg border p-4">
|
||||||
|
<div className="text-sm font-medium">{title}</div>
|
||||||
|
{jsonValue ? (
|
||||||
|
<JsonTreeViewer value={jsonValue} className="mt-3" />
|
||||||
|
) : (
|
||||||
|
<div className="mt-3 select-text whitespace-pre-wrap wrap-break-word text-sm text-muted-foreground">
|
||||||
|
{normalizedFallback || "-"}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function sanitizeRichHTML(value: string) {
|
function sanitizeRichHTML(value: string) {
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return value
|
return value
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import type { CSSProperties } from "react"
|
||||||
|
import JsonView from "@uiw/react-json-view"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
type JsonTreeViewerProps = {
|
||||||
|
value: unknown
|
||||||
|
emptyText?: string
|
||||||
|
className?: string
|
||||||
|
collapsed?: boolean | number
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewerTheme = {
|
||||||
|
"--w-rjv-font-family":
|
||||||
|
'ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace',
|
||||||
|
"--w-rjv-background-color": "transparent",
|
||||||
|
"--w-rjv-color": "hsl(var(--foreground))",
|
||||||
|
"--w-rjv-line-color": "hsl(var(--border))",
|
||||||
|
"--w-rjv-arrow-color": "hsl(var(--muted-foreground))",
|
||||||
|
"--w-rjv-info-color": "hsl(var(--muted-foreground))",
|
||||||
|
"--w-rjv-curlybraces-color": "hsl(var(--foreground))",
|
||||||
|
"--w-rjv-brackets-color": "hsl(var(--foreground))",
|
||||||
|
"--w-rjv-colon-color": "hsl(var(--muted-foreground))",
|
||||||
|
"--w-rjv-ellipsis-color": "hsl(var(--muted-foreground))",
|
||||||
|
"--w-rjv-key-string": "hsl(199 89% 58%)",
|
||||||
|
"--w-rjv-type-string-color": "hsl(142 76% 45%)",
|
||||||
|
"--w-rjv-type-int-color": "hsl(262 83% 70%)",
|
||||||
|
"--w-rjv-type-float-color": "hsl(262 83% 70%)",
|
||||||
|
"--w-rjv-type-boolean-color": "hsl(38 92% 50%)",
|
||||||
|
"--w-rjv-type-null-color": "hsl(347 77% 50%)",
|
||||||
|
} as CSSProperties
|
||||||
|
|
||||||
|
export function JsonTreeViewer({
|
||||||
|
value,
|
||||||
|
emptyText = "暂无数据",
|
||||||
|
className,
|
||||||
|
collapsed = 2,
|
||||||
|
}: JsonTreeViewerProps) {
|
||||||
|
if (value == null || value === "") {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"rounded-md border bg-muted/20 px-4 py-3 font-mono text-xs text-muted-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{emptyText}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn("rounded-md border bg-muted/20 p-3", className)}>
|
||||||
|
<JsonView
|
||||||
|
value={value as Record<string, unknown>}
|
||||||
|
collapsed={collapsed}
|
||||||
|
displayDataTypes={false}
|
||||||
|
displayObjectSize
|
||||||
|
enableClipboard
|
||||||
|
shortenTextAfterLength={120}
|
||||||
|
style={viewerTheme}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
"@tiptap/extension-underline": "3.20.2",
|
"@tiptap/extension-underline": "3.20.2",
|
||||||
"@tiptap/react": "^3.20.2",
|
"@tiptap/react": "^3.20.2",
|
||||||
"@tiptap/starter-kit": "^3.20.2",
|
"@tiptap/starter-kit": "^3.20.2",
|
||||||
|
"@uiw/react-json-view": "2.0.0-alpha.41",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
|
|||||||
Generated
+16
@@ -47,6 +47,9 @@ importers:
|
|||||||
'@tiptap/starter-kit':
|
'@tiptap/starter-kit':
|
||||||
specifier: ^3.20.2
|
specifier: ^3.20.2
|
||||||
version: 3.20.2
|
version: 3.20.2
|
||||||
|
'@uiw/react-json-view':
|
||||||
|
specifier: 2.0.0-alpha.41
|
||||||
|
version: 2.0.0-alpha.41(@babel/runtime@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -1564,6 +1567,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==}
|
resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
|
'@uiw/react-json-view@2.0.0-alpha.41':
|
||||||
|
resolution: {integrity: sha512-botRpQ5AgymYEsqXSdT2/1LefAJEYfMntvdnx1SqhTQCTW9HygeFZXx9inkYqUmiQZ3+0QlZnodjBvwnUfZhVA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@babel/runtime': '>=7.10.0'
|
||||||
|
react: '>=18.0.0'
|
||||||
|
react-dom: '>=18.0.0'
|
||||||
|
|
||||||
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
|
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
|
||||||
resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
|
resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
@@ -5726,6 +5736,12 @@ snapshots:
|
|||||||
'@typescript-eslint/types': 8.57.0
|
'@typescript-eslint/types': 8.57.0
|
||||||
eslint-visitor-keys: 5.0.1
|
eslint-visitor-keys: 5.0.1
|
||||||
|
|
||||||
|
'@uiw/react-json-view@2.0.0-alpha.41(@babel/runtime@7.28.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.28.6
|
||||||
|
react: 19.2.3
|
||||||
|
react-dom: 19.2.3(react@19.2.3)
|
||||||
|
|
||||||
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
|
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user