@langgraph-js/ui 2.0.0 → 2.1.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.
@@ -1,80 +0,0 @@
1
- import { ToolManager, ToolRenderData, createToolUI } from "@langgraph-js/sdk";
2
- import { useState } from "react";
3
- import { CheckCircle2, XCircle, Search } from "lucide-react";
4
-
5
- interface RenderResponse {
6
- approved: boolean;
7
- feedback: string | null;
8
- }
9
-
10
- export const ask_user_for_approve = createToolUI({
11
- name: "ask_user_for_approve",
12
- description: "Request user review and approval for plans or content, wait for user feedback before proceeding",
13
- parameters: [
14
- {
15
- name: "title",
16
- type: "string",
17
- description: "Title or subject of the content to be reviewed",
18
- },
19
- ],
20
- onlyRender: true,
21
- handler: ToolManager.waitForUIDone,
22
- render(tool: ToolRenderData<{ title: string }, RenderResponse>) {
23
- const data = tool.getInputRepaired();
24
- const [feedback, setFeedback] = useState(tool.getJSONOutputSafe()?.feedback || "");
25
-
26
- const handleApprove = () => {
27
- const result = {
28
- approved: true,
29
- feedback: feedback.trim() || null,
30
- };
31
- tool.response(result);
32
- };
33
-
34
- const handleReject = () => {
35
- const result = {
36
- approved: false,
37
- feedback: feedback.trim() || "用户拒绝了请求",
38
- };
39
- tool.response(result);
40
- };
41
-
42
- return (
43
- <div className="flex flex-col gap-3 p-4 bg-white rounded-lg border border-gray-200">
44
- <div className="flex items-center gap-2 text-gray-700">
45
- <Search className="w-4 h-4 text-blue-500" />
46
- <span>请求审核批准</span>
47
- {data.title && <span className="text-sm text-gray-500 ml-2 truncate max-w-[200px]">{data.title}</span>}
48
- </div>
49
-
50
- <div className="flex gap-2">
51
- <textarea
52
- disabled={tool.state === "done"}
53
- className="flex-1 p-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500"
54
- placeholder="反馈意见(可选)"
55
- value={feedback}
56
- onChange={(e) => setFeedback(e.target.value)}
57
- rows={1}
58
- />
59
-
60
- <button
61
- disabled={tool.state === "done"}
62
- className="px-3 py-2 text-sm font-medium text-white bg-green-500 rounded-lg hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1.5"
63
- onClick={handleApprove}
64
- >
65
- <CheckCircle2 className="w-4 h-4" />
66
- 批准
67
- </button>
68
- <button
69
- disabled={tool.state === "done"}
70
- className="px-3 py-2 text-sm font-medium text-white bg-red-500 rounded-lg hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1.5"
71
- onClick={handleReject}
72
- >
73
- <XCircle className="w-4 h-4" />
74
- 拒绝
75
- </button>
76
- </div>
77
- </div>
78
- );
79
- },
80
- });
@@ -1,75 +0,0 @@
1
- import { ToolManager, ToolRenderData, createToolUI } from "@langgraph-js/sdk";
2
- import { FileEdit, Globe, Brain } from "lucide-react";
3
-
4
- interface Step {
5
- need_web_search: boolean;
6
- title: string;
7
- description: string;
8
- step_type: "research" | "processing";
9
- execution_res?: string;
10
- }
11
-
12
- interface Plan {
13
- locale: string;
14
- has_enough_context: boolean;
15
- thought: string;
16
- title?: string;
17
- steps: Step[];
18
- }
19
-
20
- export const update_plan = createToolUI({
21
- name: "update_plan",
22
- description: "展示当前执行计划,等待用户确认",
23
- parameters: [],
24
- onlyRender: true,
25
- render(tool: ToolRenderData<Plan, string>) {
26
- const data = tool.getInputRepaired();
27
- const plan = data || {
28
- locale: "zh-CN",
29
- has_enough_context: false,
30
- thought: "",
31
- steps: [],
32
- };
33
-
34
- return (
35
- <div className="p-3 bg-white rounded-lg border border-gray-200">
36
- <div className="flex items-center gap-1.5 text-gray-700 mb-2 font-bold">
37
- <FileEdit className="w-3.5 h-3.5 text-blue-500" />
38
- <span>执行计划</span>
39
- </div>
40
-
41
- <div className="space-y-2">
42
- {plan.title && (
43
- <div className="text-sm">
44
- <span className="text-gray-500">标题:</span>
45
- <span>{plan.title}</span>
46
- </div>
47
- )}
48
-
49
- {plan.thought && (
50
- <div className="text-sm">
51
- <span className="text-gray-500">思考:</span>
52
- <span className="whitespace-pre-wrap">{plan.thought}</span>
53
- </div>
54
- )}
55
-
56
- {plan.steps && plan.steps.length > 0 && (
57
- <div className="space-y-1.5">
58
- <div className="text-sm text-gray-500">步骤:</div>
59
- {plan.steps.map((step, index) => (
60
- <div key={index} className="pl-2 border-l-2 border-gray-200">
61
- <div className="flex items-center gap-1.5 text-sm">
62
- {step!.step_type === "research" ? <Globe className="w-3.5 h-3.5 text-blue-500" /> : <Brain className="w-3.5 h-3.5 text-purple-500" />}
63
- <span className="font-medium">{step!.title}</span>
64
- {step!.need_web_search && <span className="text-xs text-blue-500">[搜索]</span>}
65
- </div>
66
- <div className="text-sm text-gray-600 pl-5">{step!.description}</div>
67
- </div>
68
- ))}
69
- </div>
70
- )}
71
- </div>
72
- </div>
73
- );
74
- },
75
- });
@@ -1,89 +0,0 @@
1
- // 入参 {"query":"Gemini Diffusion vs other diffusion models advantages disadvantages unique features"}
2
-
3
- import { createToolUI, ToolRenderData } from "@langgraph-js/sdk";
4
- import { LinkIcon } from "lucide-react";
5
- import { useState } from "react";
6
-
7
- interface SearchResult {
8
- title: string;
9
- url: string;
10
- description: string;
11
- updateTime: string;
12
- metadata: {
13
- engines: string[];
14
- };
15
- }
16
-
17
- interface RenderResponse {
18
- engine: string;
19
- results: SearchResult[];
20
- }
21
-
22
- interface SearchInput {
23
- query: string;
24
- }
25
-
26
- export const web_search_tool = createToolUI({
27
- name: "web_search",
28
- description:
29
- "A powerful web search tool that provides comprehensive, real-time results using search engine. Returns relevant web content with customizable parameters for result count, content type, and domain filtering. Ideal for gathering current information, news, and detailed web content analysis.",
30
- parameters: [],
31
- onlyRender: true,
32
- render(tool: ToolRenderData<SearchInput, RenderResponse[]>) {
33
- const data = tool.getInputRepaired();
34
- const feedback = tool.getJSONOutputSafe()?.flatMap((i) => i.results) || [];
35
- const [expandedItems, setExpandedItems] = useState<Set<number>>(new Set());
36
-
37
- const toggleExpand = (index: number) => {
38
- const newExpanded = new Set(expandedItems);
39
- if (newExpanded.has(index)) {
40
- newExpanded.delete(index);
41
- } else {
42
- newExpanded.add(index);
43
- }
44
- setExpandedItems(newExpanded);
45
- };
46
-
47
- const openLink = (url: string) => {
48
- window.open(url, "_blank", "noopener,noreferrer");
49
- };
50
-
51
- return (
52
- <div className="p-4 space-y-4">
53
- <div className="text-sm text-gray-500">
54
- Search Query: {data.query};Get {feedback.length} results
55
- </div>
56
- <div className="space-y-3 max-h-[300px] overflow-y-auto">
57
- {feedback.map((result, index) => (
58
- <div key={index} className="border rounded-lg p-2 hover:bg-gray-50">
59
- <div className="flex items-center justify-between select-none cursor-pointer" onClick={() => toggleExpand(index)}>
60
- <span className="font-xs flex-1">{result.title}</span>
61
- <div
62
- onClick={(e) => {
63
- e.stopPropagation();
64
- openLink(result.url);
65
- }}
66
- className="px-3 py-1 text-sm"
67
- >
68
- <LinkIcon className="w-4 h-4" />
69
- </div>
70
- <span className="text-gray-400">{expandedItems.has(index) ? "▼" : "▶"}</span>
71
- </div>
72
-
73
- {expandedItems.has(index) && (
74
- <div className="mt-3 space-y-2">
75
- <p className="text-sm text-gray-600">{result.description}</p>
76
- <div className="flex items-center gap-2 text-xs text-gray-500">
77
- <span>{new Date(result.updateTime).toLocaleDateString()}</span>
78
- <span>•</span>
79
- <span>{result.metadata.engines.join(", ")}</span>
80
- </div>
81
- </div>
82
- )}
83
- </div>
84
- ))}
85
- </div>
86
- </div>
87
- );
88
- },
89
- });