@langgraph-js/ui 5.3.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{arc-ByBJT7ix.js → arc-DEKXWMgk.js} +1 -1
- package/dist/assets/architectureDiagram-VXUJARFQ-CCAnjRBf.js +36 -0
- package/dist/assets/blockDiagram-VD42YOAC-C45FMpSa.js +122 -0
- package/dist/assets/c4Diagram-YG6GDRKO-b2FfGD5V.js +10 -0
- package/dist/assets/channel-BnP1OHPk.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-DZUxlygh.js +1 -0
- package/dist/assets/{chunk-55IACEB6-B-dnHx34.js → chunk-55IACEB6-D99w6_wJ.js} +1 -1
- package/dist/assets/{chunk-B4BG7PRW-Bd_EmK5n.js → chunk-B4BG7PRW-CINymWls.js} +6 -6
- package/dist/assets/chunk-DI55MBZ5-KXNsA5jH.js +220 -0
- package/dist/assets/{chunk-FMBD7UC4-Benh4DyV.js → chunk-FMBD7UC4-Ze5lcP7r.js} +1 -1
- package/dist/assets/chunk-QN33PNHL-D6csHY9f.js +1 -0
- package/dist/assets/{chunk-QZHKN3VN-pD5jwMeq.js → chunk-QZHKN3VN-CQij6uxD.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-iXGBDV4R.js → chunk-TZMSLE5B-DHgJ2PzL.js} +1 -1
- package/dist/assets/classDiagram-2ON5EDUG-BhnxhKIx.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-BhnxhKIx.js +1 -0
- package/dist/assets/clone-CzxHCvWS.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-7XsZDF0x.js +1 -0
- package/dist/assets/cytoscape.esm-CyJtwmzi.js +331 -0
- package/dist/assets/dagre-6UL2VRFP-B7vIHoek.js +4 -0
- package/dist/assets/diagram-PSM6KHXK-Dwk0-3lg.js +24 -0
- package/dist/assets/diagram-QEK2KX5R-yRItHVGO.js +43 -0
- package/dist/assets/diagram-S2PKOQOG-Gl24csy5.js +24 -0
- package/dist/assets/{erDiagram-Q2GNP2WA-Cw-3m3iQ.js → erDiagram-Q2GNP2WA-DaoUsHC-.js} +4 -4
- package/dist/assets/flowDiagram-NV44I4VS-BvxRwSdK.js +162 -0
- package/dist/assets/{ganttDiagram-LVOFAZNH-HFaJpoPe.js → ganttDiagram-LVOFAZNH-DiyXT1u-.js} +1 -1
- package/dist/assets/gitGraphDiagram-NY62KEGX-C7GPXV9B.js +65 -0
- package/dist/assets/{graph-VOODAD3y.js → graph-BJHoUus9.js} +1 -1
- package/dist/assets/index-Cigf7okT.js +1 -0
- package/dist/assets/{index-BDnjyX2X.js → index-Cx_WzRmY.js} +410 -119
- package/dist/assets/index-z3jA17TL.css +1 -0
- package/dist/assets/{infoDiagram-F6ZHWCRC-BhGv_djV.js → infoDiagram-F6ZHWCRC-BPazewwG.js} +1 -1
- package/dist/assets/{isUndefined-0vq8NMH-.js → isUndefined-swG3RKv4.js} +1 -1
- package/dist/assets/{journeyDiagram-XKPGCS4Q-DyCiaoHe.js → journeyDiagram-XKPGCS4Q-7TQoyFvV.js} +1 -1
- package/dist/assets/kanban-definition-3W4ZIXB7-BPW9De0Q.js +89 -0
- package/dist/assets/{layout-0ID9q93j.js → layout-DPDnEj2p.js} +1 -1
- package/dist/assets/{linear-BuZdgMYG.js → linear-CnAIHHRL.js} +1 -1
- package/dist/assets/mermaid.core-Bt6sloa1.js +197 -0
- package/dist/assets/{min-piR6L-qa.js → min-DqUZylJ8.js} +1 -1
- package/dist/assets/mindmap-definition-VGOIOE7T-C-tiffs0.js +68 -0
- package/dist/assets/pieDiagram-ADFJNKIX-0IbvYAbJ.js +30 -0
- package/dist/assets/quadrantDiagram-AYHSOK5B-B76yzm9c.js +7 -0
- package/dist/assets/{requirementDiagram-UZGBJVZJ-DenFzO4x.js → requirementDiagram-UZGBJVZJ-CAEEUTLr.js} +7 -7
- package/dist/assets/{sankeyDiagram-TZEHDZUN-Cf4npYbH.js → sankeyDiagram-TZEHDZUN-DtnGWiEx.js} +7 -7
- package/dist/assets/{sequenceDiagram-WL72ISMW-D0B0sUMP.js → sequenceDiagram-WL72ISMW-Cu9Iphhk.js} +16 -16
- package/dist/assets/{stateDiagram-FKZM4ZOC-DCkjEhse.js → stateDiagram-FKZM4ZOC-DMmqlSc3.js} +1 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-C8PNiXpd.js +1 -0
- package/dist/assets/timeline-definition-IT6M3QCI-Z39dMt9m.js +61 -0
- package/dist/assets/treemap-KMMF4GRG-DMhNl-LE.js +128 -0
- package/dist/assets/{xychartDiagram-PRI3JC2R-BfqhPDDy.js → xychartDiagram-PRI3JC2R-3RFvJwDL.js} +1 -1
- package/dist/index.html +3 -3
- package/index.html +1 -1
- package/package.json +14 -8
- package/server/graph/debugAgent.ts +42 -0
- package/server/index.ts +6 -1
- package/src/chat/Chat.tsx +21 -17
- package/src/chat/components/MessageAI.tsx +3 -1
- package/src/chat/components/MessageTool.tsx +59 -13
- package/src/chat/components/Reasoning.tsx +21 -0
- package/src/chat/components/UsageMetadata.tsx +2 -2
- package/src/components/ai-elements/chain-of-thought.tsx +228 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/collapsible.tsx +31 -0
- package/src/debugPanel/Context.tsx +45 -0
- package/src/debugPanel/DebugPanel.tsx +237 -0
- package/src/debugPanel/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/monitor/index.tsx +1 -4
- package/test/App.tsx +8 -2
- package/tsconfig.json +1 -1
- package/dist/assets/architectureDiagram-VXUJARFQ-TBGn1Rfl.js +0 -36
- package/dist/assets/blockDiagram-VD42YOAC-H3aCjd8C.js +0 -122
- package/dist/assets/c4Diagram-YG6GDRKO-mLv4gXzg.js +0 -10
- package/dist/assets/channel-C2ZY7jsG.js +0 -1
- package/dist/assets/chunk-4BX2VUAB-q_t8Wicw.js +0 -1
- package/dist/assets/chunk-DI55MBZ5-BC-nx3CQ.js +0 -220
- package/dist/assets/chunk-QN33PNHL-evxJkdkh.js +0 -1
- package/dist/assets/classDiagram-2ON5EDUG-zN_blmEE.js +0 -1
- package/dist/assets/classDiagram-v2-WZHVMYZB-zN_blmEE.js +0 -1
- package/dist/assets/clone-DvlZSVEg.js +0 -1
- package/dist/assets/cose-bilkent-S5V4N54A-CpE55JUO.js +0 -1
- package/dist/assets/cytoscape.esm-TTflUzRS.js +0 -321
- package/dist/assets/dagre-6UL2VRFP-EvO-OLhz.js +0 -4
- package/dist/assets/diagram-PSM6KHXK-CFY939Qh.js +0 -24
- package/dist/assets/diagram-QEK2KX5R-C9Zr7m8k.js +0 -43
- package/dist/assets/diagram-S2PKOQOG-DNcS9v33.js +0 -24
- package/dist/assets/flowDiagram-NV44I4VS-CLWeIeeE.js +0 -162
- package/dist/assets/gitGraphDiagram-NY62KEGX-CCUv-KE0.js +0 -65
- package/dist/assets/index-BJ9C6D-a.js +0 -1
- package/dist/assets/index-DkUikjP8.css +0 -1
- package/dist/assets/kanban-definition-3W4ZIXB7-DSRBn_Rk.js +0 -89
- package/dist/assets/mermaid.core-B8WZioq4.js +0 -191
- package/dist/assets/mindmap-definition-VGOIOE7T-CZb-iOUH.js +0 -68
- package/dist/assets/pieDiagram-ADFJNKIX-Dzw0oI4o.js +0 -30
- package/dist/assets/quadrantDiagram-AYHSOK5B-C73fqE74.js +0 -7
- package/dist/assets/stateDiagram-v2-4FDKWEC3-BO7ffAwx.js +0 -1
- package/dist/assets/timeline-definition-IT6M3QCI-wtq4Ovqb.js +0 -61
- package/dist/assets/treemap-KMMF4GRG-HbrSuDxx.js +0 -128
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import {
|
|
6
|
+
Collapsible,
|
|
7
|
+
CollapsibleContent,
|
|
8
|
+
CollapsibleTrigger,
|
|
9
|
+
} from "@/components/ui/collapsible";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import {
|
|
12
|
+
BrainIcon,
|
|
13
|
+
ChevronDownIcon,
|
|
14
|
+
DotIcon,
|
|
15
|
+
type LucideIcon,
|
|
16
|
+
} from "lucide-react";
|
|
17
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
18
|
+
import { createContext, memo, useContext, useMemo } from "react";
|
|
19
|
+
|
|
20
|
+
type ChainOfThoughtContextValue = {
|
|
21
|
+
isOpen: boolean;
|
|
22
|
+
setIsOpen: (open: boolean) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const ChainOfThoughtContext = createContext<ChainOfThoughtContextValue | null>(
|
|
26
|
+
null
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const useChainOfThought = () => {
|
|
30
|
+
const context = useContext(ChainOfThoughtContext);
|
|
31
|
+
if (!context) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"ChainOfThought components must be used within ChainOfThought"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return context;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type ChainOfThoughtProps = ComponentProps<"div"> & {
|
|
40
|
+
open?: boolean;
|
|
41
|
+
defaultOpen?: boolean;
|
|
42
|
+
onOpenChange?: (open: boolean) => void;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const ChainOfThought = memo(
|
|
46
|
+
({
|
|
47
|
+
className,
|
|
48
|
+
open,
|
|
49
|
+
defaultOpen = false,
|
|
50
|
+
onOpenChange,
|
|
51
|
+
children,
|
|
52
|
+
...props
|
|
53
|
+
}: ChainOfThoughtProps) => {
|
|
54
|
+
const [isOpen, setIsOpen] = useControllableState({
|
|
55
|
+
prop: open,
|
|
56
|
+
defaultProp: defaultOpen,
|
|
57
|
+
onChange: onOpenChange,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const chainOfThoughtContext = useMemo(
|
|
61
|
+
() => ({ isOpen, setIsOpen }),
|
|
62
|
+
[isOpen, setIsOpen]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<ChainOfThoughtContext.Provider value={chainOfThoughtContext}>
|
|
67
|
+
<div
|
|
68
|
+
className={cn("not-prose max-w-prose space-y-4", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
</div>
|
|
73
|
+
</ChainOfThoughtContext.Provider>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export type ChainOfThoughtHeaderProps = ComponentProps<
|
|
79
|
+
typeof CollapsibleTrigger
|
|
80
|
+
>;
|
|
81
|
+
|
|
82
|
+
export const ChainOfThoughtHeader = memo(
|
|
83
|
+
({ className, children, ...props }: ChainOfThoughtHeaderProps) => {
|
|
84
|
+
const { isOpen, setIsOpen } = useChainOfThought();
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Collapsible onOpenChange={setIsOpen} open={isOpen}>
|
|
88
|
+
<CollapsibleTrigger
|
|
89
|
+
className={cn(
|
|
90
|
+
"flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",
|
|
91
|
+
className
|
|
92
|
+
)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<BrainIcon className="size-4" />
|
|
96
|
+
<span className="flex-1 text-left">
|
|
97
|
+
{children ?? "Chain of Thought"}
|
|
98
|
+
</span>
|
|
99
|
+
<ChevronDownIcon
|
|
100
|
+
className={cn(
|
|
101
|
+
"size-4 transition-transform",
|
|
102
|
+
isOpen ? "rotate-180" : "rotate-0"
|
|
103
|
+
)}
|
|
104
|
+
/>
|
|
105
|
+
</CollapsibleTrigger>
|
|
106
|
+
</Collapsible>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
export type ChainOfThoughtStepProps = ComponentProps<"div"> & {
|
|
112
|
+
icon?: LucideIcon;
|
|
113
|
+
label: ReactNode;
|
|
114
|
+
description?: ReactNode;
|
|
115
|
+
status?: "complete" | "active" | "pending";
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const ChainOfThoughtStep = memo(
|
|
119
|
+
({
|
|
120
|
+
className,
|
|
121
|
+
icon: Icon = DotIcon,
|
|
122
|
+
label,
|
|
123
|
+
description,
|
|
124
|
+
status = "complete",
|
|
125
|
+
children,
|
|
126
|
+
...props
|
|
127
|
+
}: ChainOfThoughtStepProps) => {
|
|
128
|
+
const statusStyles = {
|
|
129
|
+
complete: "text-muted-foreground",
|
|
130
|
+
active: "text-foreground",
|
|
131
|
+
pending: "text-muted-foreground/50",
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div
|
|
136
|
+
className={cn(
|
|
137
|
+
"flex gap-2 text-sm",
|
|
138
|
+
statusStyles[status],
|
|
139
|
+
"fade-in-0 slide-in-from-top-2 animate-in",
|
|
140
|
+
className
|
|
141
|
+
)}
|
|
142
|
+
{...props}
|
|
143
|
+
>
|
|
144
|
+
<div className="relative mt-0.5">
|
|
145
|
+
<Icon className="size-4" />
|
|
146
|
+
<div className="-mx-px absolute top-7 bottom-0 left-1/2 w-px bg-border" />
|
|
147
|
+
</div>
|
|
148
|
+
<div className="flex-1 space-y-2">
|
|
149
|
+
<div>{label}</div>
|
|
150
|
+
{description && (
|
|
151
|
+
<div className="text-muted-foreground text-xs">{description}</div>
|
|
152
|
+
)}
|
|
153
|
+
{children}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
export type ChainOfThoughtSearchResultsProps = ComponentProps<"div">;
|
|
161
|
+
|
|
162
|
+
export const ChainOfThoughtSearchResults = memo(
|
|
163
|
+
({ className, ...props }: ChainOfThoughtSearchResultsProps) => (
|
|
164
|
+
<div className={cn("flex items-center gap-2", className)} {...props} />
|
|
165
|
+
)
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
export type ChainOfThoughtSearchResultProps = ComponentProps<typeof Badge>;
|
|
169
|
+
|
|
170
|
+
export const ChainOfThoughtSearchResult = memo(
|
|
171
|
+
({ className, children, ...props }: ChainOfThoughtSearchResultProps) => (
|
|
172
|
+
<Badge
|
|
173
|
+
className={cn("gap-1 px-2 py-0.5 font-normal text-xs", className)}
|
|
174
|
+
variant="secondary"
|
|
175
|
+
{...props}
|
|
176
|
+
>
|
|
177
|
+
{children}
|
|
178
|
+
</Badge>
|
|
179
|
+
)
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
export type ChainOfThoughtContentProps = ComponentProps<
|
|
183
|
+
typeof CollapsibleContent
|
|
184
|
+
>;
|
|
185
|
+
|
|
186
|
+
export const ChainOfThoughtContent = memo(
|
|
187
|
+
({ className, children, ...props }: ChainOfThoughtContentProps) => {
|
|
188
|
+
const { isOpen } = useChainOfThought();
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<Collapsible open={isOpen}>
|
|
192
|
+
<CollapsibleContent
|
|
193
|
+
className={cn(
|
|
194
|
+
"mt-2 space-y-3",
|
|
195
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
196
|
+
className
|
|
197
|
+
)}
|
|
198
|
+
{...props}
|
|
199
|
+
>
|
|
200
|
+
{children}
|
|
201
|
+
</CollapsibleContent>
|
|
202
|
+
</Collapsible>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
export type ChainOfThoughtImageProps = ComponentProps<"div"> & {
|
|
208
|
+
caption?: string;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const ChainOfThoughtImage = memo(
|
|
212
|
+
({ className, children, caption, ...props }: ChainOfThoughtImageProps) => (
|
|
213
|
+
<div className={cn("mt-2 space-y-2", className)} {...props}>
|
|
214
|
+
<div className="relative flex max-h-[22rem] items-center justify-center overflow-hidden rounded-lg bg-muted p-3">
|
|
215
|
+
{children}
|
|
216
|
+
</div>
|
|
217
|
+
{caption && <p className="text-muted-foreground text-xs">{caption}</p>}
|
|
218
|
+
</div>
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
ChainOfThought.displayName = "ChainOfThought";
|
|
223
|
+
ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";
|
|
224
|
+
ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
|
|
225
|
+
ChainOfThoughtSearchResults.displayName = "ChainOfThoughtSearchResults";
|
|
226
|
+
ChainOfThoughtSearchResult.displayName = "ChainOfThoughtSearchResult";
|
|
227
|
+
ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
|
|
228
|
+
ChainOfThoughtImage.displayName = "ChainOfThoughtImage";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const badgeVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
14
|
+
secondary:
|
|
15
|
+
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
16
|
+
destructive:
|
|
17
|
+
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
18
|
+
outline:
|
|
19
|
+
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
function Badge({
|
|
29
|
+
className,
|
|
30
|
+
variant,
|
|
31
|
+
asChild = false,
|
|
32
|
+
...props
|
|
33
|
+
}: React.ComponentProps<"span"> &
|
|
34
|
+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
35
|
+
const Comp = asChild ? Slot : "span"
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Comp
|
|
39
|
+
data-slot="badge"
|
|
40
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
|
|
2
|
+
|
|
3
|
+
function Collapsible({
|
|
4
|
+
...props
|
|
5
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
6
|
+
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function CollapsibleTrigger({
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
|
12
|
+
return (
|
|
13
|
+
<CollapsiblePrimitive.CollapsibleTrigger
|
|
14
|
+
data-slot="collapsible-trigger"
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function CollapsibleContent({
|
|
21
|
+
...props
|
|
22
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
|
23
|
+
return (
|
|
24
|
+
<CollapsiblePrimitive.CollapsibleContent
|
|
25
|
+
data-slot="collapsible-content"
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { RenderMessage } from "@langgraph-js/sdk";
|
|
2
|
+
import React, { createContext, useContext, useState, ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
interface DebugPanelContextType {
|
|
5
|
+
isDebugPanelVisible: boolean;
|
|
6
|
+
setIsDebugPanelVisible: (visible: boolean) => void;
|
|
7
|
+
toggleDebugPanel: () => void;
|
|
8
|
+
messagesContext: string;
|
|
9
|
+
setMessagesContext: (messages: RenderMessage[]) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DebugPanelContext = createContext<DebugPanelContextType | undefined>(undefined);
|
|
13
|
+
|
|
14
|
+
export const useDebugPanel = () => {
|
|
15
|
+
const context = useContext(DebugPanelContext);
|
|
16
|
+
if (context === undefined) {
|
|
17
|
+
throw new Error("useDebugPanel must be used within a DebugPanelProvider");
|
|
18
|
+
}
|
|
19
|
+
return context;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
interface DebugPanelProviderProps {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DebugPanelProvider: React.FC<DebugPanelProviderProps> = ({ children }) => {
|
|
27
|
+
const [messagesContext, setMessagesContext] = useState<string>("");
|
|
28
|
+
const [isDebugPanelVisible, setIsDebugPanelVisible] = useState(false);
|
|
29
|
+
|
|
30
|
+
const toggleDebugPanel = () => {
|
|
31
|
+
setIsDebugPanelVisible((prev) => !prev);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const value = {
|
|
35
|
+
isDebugPanelVisible,
|
|
36
|
+
setIsDebugPanelVisible,
|
|
37
|
+
toggleDebugPanel,
|
|
38
|
+
messagesContext,
|
|
39
|
+
setMessagesContext(messages: RenderMessage[]) {
|
|
40
|
+
setMessagesContext(JSON.stringify(messages));
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return <DebugPanelContext.Provider value={value}>{children}</DebugPanelContext.Provider>;
|
|
45
|
+
};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
2
|
+
import { ChatProvider, useChat } from "@langgraph-js/sdk/react";
|
|
3
|
+
import { Message } from "@langgraph-js/sdk";
|
|
4
|
+
import { Send, Bug, Bot, Plus, Trash } from "lucide-react";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
import { useDebugPanel } from "./Context";
|
|
7
|
+
import ModelTesterPopup from "../chat/components/ModelTesterPopup";
|
|
8
|
+
import { MessagesBox } from "../chat/components/MessageBox";
|
|
9
|
+
import { ExtraParamsProvider } from "@/chat/context/ExtraParamsContext";
|
|
10
|
+
|
|
11
|
+
const DebugMessages: React.FC = () => {
|
|
12
|
+
const { renderMessages, loading, inChatError, client } = useChat();
|
|
13
|
+
const messagesEndRef = useRef<HTMLDivElement>(null);
|
|
14
|
+
|
|
15
|
+
const scrollToBottom = () => {
|
|
16
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "instant" });
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (renderMessages.length > 0 && !loading) {
|
|
21
|
+
scrollToBottom();
|
|
22
|
+
}
|
|
23
|
+
}, [renderMessages, loading]);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="flex-1 overflow-y-auto overflow-x-hidden p-4 bg-gray-50 [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-gray-300 [&::-webkit-scrollbar-thumb]:rounded [&::-webkit-scrollbar-thumb:hover]:bg-gray-400">
|
|
27
|
+
{renderMessages.length === 0 && !loading ? (
|
|
28
|
+
<div className="flex items-center justify-center h-full">
|
|
29
|
+
<div className="text-center">
|
|
30
|
+
<div className="flex items-center justify-center">
|
|
31
|
+
<div className="text-4xl mb-4">🐛</div>
|
|
32
|
+
</div>
|
|
33
|
+
<h1 className="text-2xl font-bold text-gray-700 mb-2">Debug Panel</h1>
|
|
34
|
+
<div className="text-sm text-gray-500">调试助手控制台</div>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
) : (
|
|
38
|
+
<div className="flex flex-col gap-5 w-full">
|
|
39
|
+
<MessagesBox renderMessages={renderMessages} collapsedTools={[]} toggleToolCollapse={() => {}} client={client!} />
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
{loading && (
|
|
43
|
+
<div className="flex items-center justify-center py-4 text-gray-500">
|
|
44
|
+
<div className="animate-spin rounded-full h-4 w-4 border-2 border-blue-500 border-t-transparent mr-2"></div>
|
|
45
|
+
调试中...
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
{inChatError && <div className="px-4 py-3 text-sm text-red-600 bg-red-50 rounded-lg">{JSON.stringify(inChatError)}</div>}
|
|
49
|
+
<div ref={messagesEndRef} />
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const DebugInput: React.FC = () => {
|
|
55
|
+
const { userInput, setUserInput, loading, sendMessage, stopGeneration, createNewChat, renderMessages } = useChat();
|
|
56
|
+
const { messagesContext } = useDebugPanel();
|
|
57
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
58
|
+
|
|
59
|
+
const sendDebugMessage = () => {
|
|
60
|
+
const item = localStorage.getItem("model-tester-config");
|
|
61
|
+
if (!item) {
|
|
62
|
+
toast.error("请先配置模型测试器");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let config;
|
|
67
|
+
try {
|
|
68
|
+
config = JSON.parse(item);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
toast.error("模型测试器配置格式错误,请重新配置");
|
|
71
|
+
console.error("Failed to parse model-tester-config:", error);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 验证配置对象的有效性
|
|
76
|
+
if (!config || typeof config !== "object") {
|
|
77
|
+
toast.error("模型测试器配置无效,请重新配置");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 检查必要的配置项
|
|
82
|
+
if (!config.token || typeof config.token !== "string" || config.token.trim() === "") {
|
|
83
|
+
toast.error("API Token 未配置或为空,请检查模型测试器配置");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const isFirstMessage = renderMessages.length === 0;
|
|
87
|
+
const content: Message[] = [
|
|
88
|
+
{
|
|
89
|
+
type: "human",
|
|
90
|
+
content: userInput,
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
if (isFirstMessage && messagesContext)
|
|
94
|
+
content.push({
|
|
95
|
+
type: "human",
|
|
96
|
+
content: "这些是我们的数据\n\n" + messagesContext,
|
|
97
|
+
});
|
|
98
|
+
sendMessage(content, {
|
|
99
|
+
extraParams: {
|
|
100
|
+
model_name: config.model_name || "qwen-plus",
|
|
101
|
+
api_key: config.token.trim(),
|
|
102
|
+
api_host: config.base_url,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const handleKeyPress = (event: React.KeyboardEvent) => {
|
|
108
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
sendDebugMessage();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (userInput && (userInput.length > 50 || userInput.includes("\n"))) {
|
|
118
|
+
setIsExpanded(true);
|
|
119
|
+
} else {
|
|
120
|
+
setIsExpanded(false);
|
|
121
|
+
}
|
|
122
|
+
}, [userInput]);
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (textareaRef.current) {
|
|
126
|
+
textareaRef.current.style.height = "auto";
|
|
127
|
+
if (isExpanded) {
|
|
128
|
+
textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 96) + "px";
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}, [userInput, isExpanded]);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className="p-4 bg-white border-t border-gray-200">
|
|
135
|
+
<div className={`bg-gray-50 border border-gray-200 rounded-lg px-3 py-3 ${isExpanded ? "rounded-xl" : "rounded-full"}`}>
|
|
136
|
+
<div className={`flex gap-3 ${isExpanded ? "items-end" : "items-center"}`}>
|
|
137
|
+
<button
|
|
138
|
+
onClick={() => createNewChat()}
|
|
139
|
+
className="w-8 h-8 flex items-center justify-center rounded-full focus:outline-none transition-colors bg-blue-500 hover:bg-blue-600 text-white disabled:opacity-40"
|
|
140
|
+
title="创建新对话"
|
|
141
|
+
>
|
|
142
|
+
<Trash className="w-4 h-4" />
|
|
143
|
+
</button>
|
|
144
|
+
<textarea
|
|
145
|
+
ref={textareaRef}
|
|
146
|
+
className="flex-1 text-sm resize-none active:outline-none focus:outline-none bg-transparent"
|
|
147
|
+
rows={1}
|
|
148
|
+
value={userInput}
|
|
149
|
+
onChange={(e) => setUserInput(e.target.value)}
|
|
150
|
+
onKeyDown={handleKeyPress}
|
|
151
|
+
placeholder="输入调试命令..."
|
|
152
|
+
disabled={loading}
|
|
153
|
+
style={{
|
|
154
|
+
maxHeight: isExpanded ? "6rem" : "2rem",
|
|
155
|
+
fontFamily: "inherit",
|
|
156
|
+
lineHeight: isExpanded ? "inherit" : "2rem",
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
<button
|
|
160
|
+
className={`w-8 h-8 flex items-center justify-center rounded-full focus:outline-none transition-colors disabled:cursor-not-allowed ${
|
|
161
|
+
loading ? "bg-red-500 hover:bg-red-600 text-white" : "bg-blue-500 hover:bg-blue-600 text-white disabled:opacity-40"
|
|
162
|
+
}`}
|
|
163
|
+
onClick={() => (loading ? stopGeneration() : sendDebugMessage())}
|
|
164
|
+
disabled={!loading && !userInput.trim()}
|
|
165
|
+
>
|
|
166
|
+
{loading ? (
|
|
167
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
168
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
169
|
+
</svg>
|
|
170
|
+
) : (
|
|
171
|
+
<Send className="w-4 h-4" />
|
|
172
|
+
)}
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const DebugPanel: React.FC = () => {
|
|
181
|
+
const [isModelTesterOpen, setIsModelTesterOpen] = useState(false);
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className="flex h-full w-full max-w-xl overflow-hidden bg-gray-50">
|
|
185
|
+
<section className="flex-1 flex flex-col overflow-hidden">
|
|
186
|
+
<header className="flex items-center gap-2 px-4 py-3 justify-between bg-white border-b border-gray-200">
|
|
187
|
+
<div className="flex items-center gap-2">
|
|
188
|
+
<Bug className="w-5 h-5 text-gray-600" />
|
|
189
|
+
<h1 className="text-lg font-semibold text-gray-900">调试面板</h1>
|
|
190
|
+
</div>
|
|
191
|
+
<div className="flex items-center gap-2">
|
|
192
|
+
<button
|
|
193
|
+
onClick={() => setIsModelTesterOpen(true)}
|
|
194
|
+
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-200 cursor-pointer rounded-xl hover:bg-gray-100 focus:outline-none transition-colors flex items-center gap-2"
|
|
195
|
+
>
|
|
196
|
+
<Bot className="w-4 h-4" />
|
|
197
|
+
模型测试器
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
</header>
|
|
201
|
+
<main className="flex-1 overflow-hidden flex flex-col">
|
|
202
|
+
<DebugMessages />
|
|
203
|
+
<DebugInput />
|
|
204
|
+
</main>
|
|
205
|
+
<ModelTesterPopup isOpen={isModelTesterOpen} onClose={() => setIsModelTesterOpen(false)} />
|
|
206
|
+
</section>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const DebugPanelWrapper: React.FC = () => {
|
|
212
|
+
const { isDebugPanelVisible } = useDebugPanel();
|
|
213
|
+
if (!isDebugPanelVisible) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return (
|
|
217
|
+
<ChatProvider
|
|
218
|
+
defaultAgent={"graph"}
|
|
219
|
+
apiUrl={new URL("/api/open-smith/graph", window.location.origin.toString()).href}
|
|
220
|
+
defaultHeaders={{}}
|
|
221
|
+
withCredentials={false}
|
|
222
|
+
showHistory={false}
|
|
223
|
+
fallbackToAvailableAssistants={false}
|
|
224
|
+
onInitError={(err, currentAgent) => {
|
|
225
|
+
toast.error("调试面板连接失败: " + currentAgent + "\n" + err, {
|
|
226
|
+
duration: 5000,
|
|
227
|
+
});
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
<ExtraParamsProvider>
|
|
231
|
+
<DebugPanel />
|
|
232
|
+
</ExtraParamsProvider>
|
|
233
|
+
</ChatProvider>
|
|
234
|
+
);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export default DebugPanelWrapper;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./DebugPanel";
|
package/src/index.ts
CHANGED
package/src/monitor/index.tsx
CHANGED
|
@@ -45,15 +45,12 @@ export const MonitorProvider: React.FC<{ children: ReactNode }> = ({ children })
|
|
|
45
45
|
// Hook 用于在组件中使用 modal
|
|
46
46
|
export const useMonitor = () => {
|
|
47
47
|
const context = useContext(MonitorContext);
|
|
48
|
-
if (context === undefined) {
|
|
49
|
-
throw new Error("useModal must be used within a ModalProvider");
|
|
50
|
-
}
|
|
51
48
|
return context;
|
|
52
49
|
};
|
|
53
50
|
|
|
54
51
|
// Modal 组件
|
|
55
52
|
export const Monitor: React.FC = () => {
|
|
56
|
-
const { isOpen, url, closeModal } = useMonitor()
|
|
53
|
+
const { isOpen, url, closeModal } = useMonitor()!;
|
|
57
54
|
|
|
58
55
|
if (!isOpen) return null;
|
|
59
56
|
|
package/test/App.tsx
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { Chat } from "../src/index";
|
|
1
|
+
import { Chat, DebugPanel } from "../src/index";
|
|
2
|
+
import { DebugPanelProvider } from "../src/debugPanel/Context";
|
|
2
3
|
|
|
3
4
|
function App() {
|
|
4
|
-
return
|
|
5
|
+
return (
|
|
6
|
+
<DebugPanelProvider>
|
|
7
|
+
<Chat />
|
|
8
|
+
<DebugPanel />
|
|
9
|
+
</DebugPanelProvider>
|
|
10
|
+
);
|
|
5
11
|
}
|
|
6
12
|
|
|
7
13
|
export default App;
|
package/tsconfig.json
CHANGED