@langgraph-js/ui 1.1.0 → 1.2.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/LICENSE +201 -201
- package/README.md +6 -6
- package/cli.mjs +36 -36
- package/dist/assets/{index-CCaE6qp1.js → index-BHPbGlnP.js} +33 -33
- package/dist/assets/index-Du4LMUX2.css +1 -0
- package/dist/index.html +14 -14
- package/index.html +22 -22
- package/package.json +9 -8
- package/src/chat/Chat.tsx +164 -150
- package/src/chat/FileUpload/index.ts +105 -105
- package/src/chat/chat.css +403 -388
- package/src/chat/components/FileList.css +128 -115
- package/src/chat/components/FileList.tsx +73 -70
- package/src/chat/components/HistoryList.tsx +192 -192
- package/src/chat/components/JsonEditorPopup.css +81 -0
- package/src/chat/components/JsonEditorPopup.tsx +57 -0
- package/src/chat/components/MessageAI.tsx +24 -24
- package/src/chat/components/MessageHuman.tsx +55 -55
- package/src/chat/components/MessageTool.tsx +46 -46
- package/src/chat/components/UsageMetadata.tsx +40 -40
- package/src/chat/context/ChatContext.tsx +29 -29
- package/src/chat/context/ExtraParamsContext.tsx +42 -0
- package/src/chat/store/index.ts +24 -24
- package/src/chat/tools.ts +33 -33
- package/src/chat/types.ts +16 -16
- package/src/hooks/useLocalStorage.ts +27 -27
- package/src/index.ts +1 -1
- package/src/login/Login.css +93 -93
- package/src/login/Login.tsx +92 -92
- package/test/App.tsx +9 -9
- package/test/main.tsx +5 -5
- package/test/vite-env.d.ts +1 -1
- package/tsconfig.json +21 -21
- package/tsconfig.node.json +9 -9
- package/vite.config.ts +17 -17
- package/dist/assets/index-CBfok6qC.css +0 -1
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
interface MessageHumanProps {
|
|
3
|
-
content: string | any[];
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
const MessageHuman: React.FC<MessageHumanProps> = ({ content }) => {
|
|
7
|
-
const renderContent = () => {
|
|
8
|
-
if (typeof content === "string") {
|
|
9
|
-
return <div className="message-text">{content}</div>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (Array.isArray(content)) {
|
|
13
|
-
return content.map((item, index) => {
|
|
14
|
-
switch (item.type) {
|
|
15
|
-
case "text":
|
|
16
|
-
return (
|
|
17
|
-
<div key={index} className="message-text">
|
|
18
|
-
{item.text}
|
|
19
|
-
</div>
|
|
20
|
-
);
|
|
21
|
-
case "image_url":
|
|
22
|
-
return (
|
|
23
|
-
<div key={index} className="message-image">
|
|
24
|
-
<img src={item.image_url.url} alt="用户上传的图片" style={{ maxWidth: "200px", borderRadius: "4px" }} />
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
case "audio":
|
|
28
|
-
return (
|
|
29
|
-
<div key={index} className="message-audio">
|
|
30
|
-
<audio controls src={item.audio_url}>
|
|
31
|
-
您的浏览器不支持音频播放
|
|
32
|
-
</audio>
|
|
33
|
-
</div>
|
|
34
|
-
);
|
|
35
|
-
default:
|
|
36
|
-
return (
|
|
37
|
-
<div key={index} className="message-text">
|
|
38
|
-
{JSON.stringify(item)}
|
|
39
|
-
</div>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return <div className="message-text">{JSON.stringify(content)}</div>;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<div className="message human">
|
|
50
|
-
<div className="message-content">{renderContent()}</div>
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export default MessageHuman;
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface MessageHumanProps {
|
|
3
|
+
content: string | any[];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const MessageHuman: React.FC<MessageHumanProps> = ({ content }) => {
|
|
7
|
+
const renderContent = () => {
|
|
8
|
+
if (typeof content === "string") {
|
|
9
|
+
return <div className="message-text">{content}</div>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (Array.isArray(content)) {
|
|
13
|
+
return content.map((item, index) => {
|
|
14
|
+
switch (item.type) {
|
|
15
|
+
case "text":
|
|
16
|
+
return (
|
|
17
|
+
<div key={index} className="message-text">
|
|
18
|
+
{item.text}
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
case "image_url":
|
|
22
|
+
return (
|
|
23
|
+
<div key={index} className="message-image">
|
|
24
|
+
<img src={item.image_url.url} alt="用户上传的图片" style={{ maxWidth: "200px", borderRadius: "4px" }} />
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
case "audio":
|
|
28
|
+
return (
|
|
29
|
+
<div key={index} className="message-audio">
|
|
30
|
+
<audio controls src={item.audio_url}>
|
|
31
|
+
您的浏览器不支持音频播放
|
|
32
|
+
</audio>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
default:
|
|
36
|
+
return (
|
|
37
|
+
<div key={index} className="message-text">
|
|
38
|
+
{JSON.stringify(item)}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return <div className="message-text">{JSON.stringify(content)}</div>;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="message human">
|
|
50
|
+
<div className="message-content">{renderContent()}</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default MessageHuman;
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { LangGraphClient, RenderMessage, ToolMessage } from "@langgraph-js/sdk";
|
|
3
|
-
import { UsageMetadata } from "./UsageMetadata";
|
|
4
|
-
interface MessageToolProps {
|
|
5
|
-
message: ToolMessage & RenderMessage;
|
|
6
|
-
client: LangGraphClient;
|
|
7
|
-
getMessageContent: (content: any) => string;
|
|
8
|
-
formatTokens: (tokens: number) => string;
|
|
9
|
-
isCollapsed: boolean;
|
|
10
|
-
onToggleCollapse: () => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const MessageTool: React.FC<MessageToolProps> = ({ message, client, getMessageContent, formatTokens, isCollapsed, onToggleCollapse }) => {
|
|
14
|
-
return (
|
|
15
|
-
<div className="message tool">
|
|
16
|
-
{message.name === "ask_user" && !message.additional_kwargs?.done && (
|
|
17
|
-
<div>
|
|
18
|
-
<div>询问 {message.tool_input}</div>
|
|
19
|
-
<input
|
|
20
|
-
type="text"
|
|
21
|
-
onKeyDown={(e) => {
|
|
22
|
-
if (e.key === "Enter") {
|
|
23
|
-
client.doneFEToolWaiting(message.id!, (e.target as any).value);
|
|
24
|
-
}
|
|
25
|
-
}}
|
|
26
|
-
/>
|
|
27
|
-
</div>
|
|
28
|
-
)}
|
|
29
|
-
<div className="tool-message">
|
|
30
|
-
<div className="tool-header" onClick={onToggleCollapse}>
|
|
31
|
-
<div className="tool-title">{message.name}</div>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
{!isCollapsed && (
|
|
35
|
-
<div className="tool-content">
|
|
36
|
-
<div className="tool-input">{message.tool_input}</div>
|
|
37
|
-
<div className="tool-output">{getMessageContent(message.content)}</div>
|
|
38
|
-
<UsageMetadata response_metadata={message.response_metadata as any} usage_metadata={message.usage_metadata || {}} spend_time={message.spend_time} />
|
|
39
|
-
</div>
|
|
40
|
-
)}
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export default MessageTool;
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { LangGraphClient, RenderMessage, ToolMessage } from "@langgraph-js/sdk";
|
|
3
|
+
import { UsageMetadata } from "./UsageMetadata";
|
|
4
|
+
interface MessageToolProps {
|
|
5
|
+
message: ToolMessage & RenderMessage;
|
|
6
|
+
client: LangGraphClient;
|
|
7
|
+
getMessageContent: (content: any) => string;
|
|
8
|
+
formatTokens: (tokens: number) => string;
|
|
9
|
+
isCollapsed: boolean;
|
|
10
|
+
onToggleCollapse: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const MessageTool: React.FC<MessageToolProps> = ({ message, client, getMessageContent, formatTokens, isCollapsed, onToggleCollapse }) => {
|
|
14
|
+
return (
|
|
15
|
+
<div className="message tool">
|
|
16
|
+
{message.name === "ask_user" && !message.additional_kwargs?.done && (
|
|
17
|
+
<div>
|
|
18
|
+
<div>询问 {message.tool_input}</div>
|
|
19
|
+
<input
|
|
20
|
+
type="text"
|
|
21
|
+
onKeyDown={(e) => {
|
|
22
|
+
if (e.key === "Enter") {
|
|
23
|
+
client.doneFEToolWaiting(message.id!, (e.target as any).value);
|
|
24
|
+
}
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
)}
|
|
29
|
+
<div className="tool-message">
|
|
30
|
+
<div className="tool-header" onClick={onToggleCollapse}>
|
|
31
|
+
<div className="tool-title">{message.name}</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
{!isCollapsed && (
|
|
35
|
+
<div className="tool-content">
|
|
36
|
+
<div className="tool-input">{message.tool_input}</div>
|
|
37
|
+
<div className="tool-output">{getMessageContent(message.content)}</div>
|
|
38
|
+
<UsageMetadata response_metadata={message.response_metadata as any} usage_metadata={message.usage_metadata || {}} spend_time={message.spend_time} />
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default MessageTool;
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
interface UsageMetadataProps {
|
|
2
|
-
usage_metadata: Partial<{
|
|
3
|
-
input_tokens: number;
|
|
4
|
-
output_tokens: number;
|
|
5
|
-
total_tokens: number;
|
|
6
|
-
}>;
|
|
7
|
-
response_metadata?:{
|
|
8
|
-
model_name?: string;
|
|
9
|
-
}
|
|
10
|
-
spend_time?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const UsageMetadata: React.FC<UsageMetadataProps> = ({ usage_metadata, spend_time ,response_metadata}) => {
|
|
14
|
-
const formatTokens = (tokens: number) => {
|
|
15
|
-
return tokens.toString();
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<div className="message-meta">
|
|
20
|
-
<div className="token-info">
|
|
21
|
-
<span className="token-item">
|
|
22
|
-
<span className="token-emoji">📥</span>
|
|
23
|
-
{formatTokens(usage_metadata.input_tokens || 0)}
|
|
24
|
-
</span>
|
|
25
|
-
<span className="token-item">
|
|
26
|
-
<span className="token-emoji">📤</span>
|
|
27
|
-
{formatTokens(usage_metadata.output_tokens || 0)}
|
|
28
|
-
</span>
|
|
29
|
-
<span className="token-item">
|
|
30
|
-
<span className="token-emoji">📊</span>
|
|
31
|
-
{formatTokens(usage_metadata.total_tokens || 0)}
|
|
32
|
-
</span>
|
|
33
|
-
</div>
|
|
34
|
-
<div>
|
|
35
|
-
{response_metadata?.model_name}
|
|
36
|
-
</div>
|
|
37
|
-
<span className="message-time">{spend_time ? `${(spend_time / 1000).toFixed(2)}s` : ""}</span>
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
1
|
+
interface UsageMetadataProps {
|
|
2
|
+
usage_metadata: Partial<{
|
|
3
|
+
input_tokens: number;
|
|
4
|
+
output_tokens: number;
|
|
5
|
+
total_tokens: number;
|
|
6
|
+
}>;
|
|
7
|
+
response_metadata?:{
|
|
8
|
+
model_name?: string;
|
|
9
|
+
}
|
|
10
|
+
spend_time?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const UsageMetadata: React.FC<UsageMetadataProps> = ({ usage_metadata, spend_time ,response_metadata}) => {
|
|
14
|
+
const formatTokens = (tokens: number) => {
|
|
15
|
+
return tokens.toString();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="message-meta">
|
|
20
|
+
<div className="token-info">
|
|
21
|
+
<span className="token-item">
|
|
22
|
+
<span className="token-emoji">📥</span>
|
|
23
|
+
{formatTokens(usage_metadata.input_tokens || 0)}
|
|
24
|
+
</span>
|
|
25
|
+
<span className="token-item">
|
|
26
|
+
<span className="token-emoji">📤</span>
|
|
27
|
+
{formatTokens(usage_metadata.output_tokens || 0)}
|
|
28
|
+
</span>
|
|
29
|
+
<span className="token-item">
|
|
30
|
+
<span className="token-emoji">📊</span>
|
|
31
|
+
{formatTokens(usage_metadata.total_tokens || 0)}
|
|
32
|
+
</span>
|
|
33
|
+
</div>
|
|
34
|
+
<div>
|
|
35
|
+
{response_metadata?.model_name}
|
|
36
|
+
</div>
|
|
37
|
+
<span className="message-time">{spend_time ? `${(spend_time / 1000).toFixed(2)}s` : ""}</span>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from "react";
|
|
2
|
-
type ChatContextType = UnionStore<typeof globalChatStore>;
|
|
3
|
-
|
|
4
|
-
const ChatContext = createContext<ChatContextType | undefined>(undefined);
|
|
5
|
-
|
|
6
|
-
export const useChat = () => {
|
|
7
|
-
const context = useContext(ChatContext);
|
|
8
|
-
if (!context) {
|
|
9
|
-
throw new Error("useChat must be used within a ChatProvider");
|
|
10
|
-
}
|
|
11
|
-
return context;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
interface ChatProviderProps {
|
|
15
|
-
children: ReactNode;
|
|
16
|
-
}
|
|
17
|
-
import { globalChatStore } from "../store";
|
|
18
|
-
import { UnionStore, useUnionStore } from "@langgraph-js/sdk";
|
|
19
|
-
import { useStore } from "@nanostores/react";
|
|
20
|
-
export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
21
|
-
const store = useUnionStore(globalChatStore, useStore);
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
store.initClient().then((res) => {
|
|
24
|
-
store.refreshHistoryList();
|
|
25
|
-
});
|
|
26
|
-
}, [store.currentAgent]);
|
|
27
|
-
|
|
28
|
-
return <ChatContext.Provider value={store}>{children}</ChatContext.Provider>;
|
|
29
|
-
};
|
|
1
|
+
import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from "react";
|
|
2
|
+
type ChatContextType = UnionStore<typeof globalChatStore>;
|
|
3
|
+
|
|
4
|
+
const ChatContext = createContext<ChatContextType | undefined>(undefined);
|
|
5
|
+
|
|
6
|
+
export const useChat = () => {
|
|
7
|
+
const context = useContext(ChatContext);
|
|
8
|
+
if (!context) {
|
|
9
|
+
throw new Error("useChat must be used within a ChatProvider");
|
|
10
|
+
}
|
|
11
|
+
return context;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
interface ChatProviderProps {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
import { globalChatStore } from "../store";
|
|
18
|
+
import { UnionStore, useUnionStore } from "@langgraph-js/sdk";
|
|
19
|
+
import { useStore } from "@nanostores/react";
|
|
20
|
+
export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
21
|
+
const store = useUnionStore(globalChatStore, useStore);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
store.initClient().then((res) => {
|
|
24
|
+
store.refreshHistoryList();
|
|
25
|
+
});
|
|
26
|
+
}, [store.currentAgent]);
|
|
27
|
+
|
|
28
|
+
return <ChatContext.Provider value={store}>{children}</ChatContext.Provider>;
|
|
29
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { createContext, useState, useEffect, useContext, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ExtraParamsContextType {
|
|
4
|
+
extraParams: object;
|
|
5
|
+
setExtraParams: (params: object) => void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const ExtraParamsContext = createContext<ExtraParamsContextType | undefined>(undefined);
|
|
9
|
+
|
|
10
|
+
export const ExtraParamsProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
11
|
+
const [extraParams, setExtraParamsState] = useState<object>(() => {
|
|
12
|
+
const savedParams = localStorage.getItem("extraParams");
|
|
13
|
+
try {
|
|
14
|
+
return savedParams ? JSON.parse(savedParams) : {};
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.error("Failed to parse extraParams from localStorage", e);
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
localStorage.setItem("extraParams", JSON.stringify(extraParams));
|
|
23
|
+
}, [extraParams]);
|
|
24
|
+
|
|
25
|
+
const setExtraParams = (params: object) => {
|
|
26
|
+
setExtraParamsState(params);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<ExtraParamsContext.Provider value={{ extraParams, setExtraParams }}>
|
|
31
|
+
{children}
|
|
32
|
+
</ExtraParamsContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const useExtraParams = (): ExtraParamsContextType => {
|
|
37
|
+
const context = useContext(ExtraParamsContext);
|
|
38
|
+
if (context === undefined) {
|
|
39
|
+
throw new Error('useExtraParams must be used within an ExtraParamsProvider');
|
|
40
|
+
}
|
|
41
|
+
return context;
|
|
42
|
+
};
|
package/src/chat/store/index.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import { createChatStore } from "@langgraph-js/sdk";
|
|
2
|
-
const F =
|
|
3
|
-
localStorage.getItem("withCredentials") === "true"
|
|
4
|
-
? (url: string, options: RequestInit) => {
|
|
5
|
-
options.credentials = "include";
|
|
6
|
-
return fetch(url, options);
|
|
7
|
-
}
|
|
8
|
-
: fetch;
|
|
9
|
-
export const globalChatStore = createChatStore(
|
|
10
|
-
"agent",
|
|
11
|
-
{
|
|
12
|
-
apiUrl: localStorage.getItem("apiUrl") || "http://localhost:8123",
|
|
13
|
-
defaultHeaders: JSON.parse(localStorage.getItem("code") || "{}"),
|
|
14
|
-
callerOptions: {
|
|
15
|
-
// 携带 cookie 的写法
|
|
16
|
-
fetch: F,
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
onInit(client) {
|
|
21
|
-
client.tools.bindTools([]);
|
|
22
|
-
},
|
|
23
|
-
}
|
|
24
|
-
);
|
|
1
|
+
import { createChatStore } from "@langgraph-js/sdk";
|
|
2
|
+
const F =
|
|
3
|
+
localStorage.getItem("withCredentials") === "true"
|
|
4
|
+
? (url: string, options: RequestInit) => {
|
|
5
|
+
options.credentials = "include";
|
|
6
|
+
return fetch(url, options);
|
|
7
|
+
}
|
|
8
|
+
: fetch;
|
|
9
|
+
export const globalChatStore = createChatStore(
|
|
10
|
+
"agent",
|
|
11
|
+
{
|
|
12
|
+
apiUrl: localStorage.getItem("apiUrl") || "http://localhost:8123",
|
|
13
|
+
defaultHeaders: JSON.parse(localStorage.getItem("code") || "{}"),
|
|
14
|
+
callerOptions: {
|
|
15
|
+
// 携带 cookie 的写法
|
|
16
|
+
fetch: F,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
onInit(client) {
|
|
21
|
+
client.tools.bindTools([]);
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
);
|
package/src/chat/tools.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { createFETool, ToolManager } from "@langgraph-js/sdk";
|
|
2
|
-
|
|
3
|
-
// 文件操作工具
|
|
4
|
-
export const fileTool = createFETool({
|
|
5
|
-
name: "file_operation",
|
|
6
|
-
description: "执行文件操作,包括读取和写入",
|
|
7
|
-
parameters: [
|
|
8
|
-
{
|
|
9
|
-
name: "filePath",
|
|
10
|
-
type: "string",
|
|
11
|
-
description: "文件的完整路径",
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
returnDirect: true,
|
|
15
|
-
callbackMessage: () => [{ type: "ai", content: "工作完成" }],
|
|
16
|
-
async handler(args) {
|
|
17
|
-
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
18
|
-
return [{ type: "text", text: "执行文件操作 " + args.filePath }];
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export const askUserTool = createFETool({
|
|
23
|
-
name: "ask_user",
|
|
24
|
-
description: "询问用户",
|
|
25
|
-
parameters: [
|
|
26
|
-
{
|
|
27
|
-
name: "question",
|
|
28
|
-
type: "string",
|
|
29
|
-
description: "问题",
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
handler: ToolManager.waitForUIDone,
|
|
33
|
-
});
|
|
1
|
+
import { createFETool, ToolManager } from "@langgraph-js/sdk";
|
|
2
|
+
|
|
3
|
+
// 文件操作工具
|
|
4
|
+
export const fileTool = createFETool({
|
|
5
|
+
name: "file_operation",
|
|
6
|
+
description: "执行文件操作,包括读取和写入",
|
|
7
|
+
parameters: [
|
|
8
|
+
{
|
|
9
|
+
name: "filePath",
|
|
10
|
+
type: "string",
|
|
11
|
+
description: "文件的完整路径",
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
returnDirect: true,
|
|
15
|
+
callbackMessage: () => [{ type: "ai", content: "工作完成" }],
|
|
16
|
+
async handler(args) {
|
|
17
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
18
|
+
return [{ type: "text", text: "执行文件操作 " + args.filePath }];
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const askUserTool = createFETool({
|
|
23
|
+
name: "ask_user",
|
|
24
|
+
description: "询问用户",
|
|
25
|
+
parameters: [
|
|
26
|
+
{
|
|
27
|
+
name: "question",
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "问题",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
handler: ToolManager.waitForUIDone,
|
|
33
|
+
});
|
package/src/chat/types.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export interface Message {
|
|
2
|
-
content: string;
|
|
3
|
-
role: string;
|
|
4
|
-
name?: string;
|
|
5
|
-
metadata?: {
|
|
6
|
-
graph_id?: string;
|
|
7
|
-
};
|
|
8
|
-
thread_id?: string;
|
|
9
|
-
usage_metadata?: {
|
|
10
|
-
input_tokens: number;
|
|
11
|
-
output_tokens: number;
|
|
12
|
-
total_tokens: number;
|
|
13
|
-
};
|
|
14
|
-
spend_time?: number;
|
|
15
|
-
tool_input?: string;
|
|
16
|
-
}
|
|
1
|
+
export interface Message {
|
|
2
|
+
content: string;
|
|
3
|
+
role: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
metadata?: {
|
|
6
|
+
graph_id?: string;
|
|
7
|
+
};
|
|
8
|
+
thread_id?: string;
|
|
9
|
+
usage_metadata?: {
|
|
10
|
+
input_tokens: number;
|
|
11
|
+
output_tokens: number;
|
|
12
|
+
total_tokens: number;
|
|
13
|
+
};
|
|
14
|
+
spend_time?: number;
|
|
15
|
+
tool_input?: string;
|
|
16
|
+
}
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { useState, useEffect } from "react";
|
|
2
|
-
|
|
3
|
-
function useLocalStorage<T>(key: string, initialValue: T) {
|
|
4
|
-
// 从 localStorage 获取初始值
|
|
5
|
-
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
6
|
-
try {
|
|
7
|
-
const item = window.localStorage.getItem(key);
|
|
8
|
-
return item ? JSON.parse(item) : initialValue;
|
|
9
|
-
} catch (error) {
|
|
10
|
-
console.error("Error reading from localStorage:", error);
|
|
11
|
-
return initialValue;
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
// 当值改变时,自动同步到 localStorage
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
try {
|
|
18
|
-
window.localStorage.setItem(key, JSON.stringify(storedValue));
|
|
19
|
-
} catch (error) {
|
|
20
|
-
console.error("Error writing to localStorage:", error);
|
|
21
|
-
}
|
|
22
|
-
}, [key, storedValue]);
|
|
23
|
-
|
|
24
|
-
return [storedValue, setStoredValue] as const;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default useLocalStorage;
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
function useLocalStorage<T>(key: string, initialValue: T) {
|
|
4
|
+
// 从 localStorage 获取初始值
|
|
5
|
+
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
6
|
+
try {
|
|
7
|
+
const item = window.localStorage.getItem(key);
|
|
8
|
+
return item ? JSON.parse(item) : initialValue;
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.error("Error reading from localStorage:", error);
|
|
11
|
+
return initialValue;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// 当值改变时,自动同步到 localStorage
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
try {
|
|
18
|
+
window.localStorage.setItem(key, JSON.stringify(storedValue));
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error("Error writing to localStorage:", error);
|
|
21
|
+
}
|
|
22
|
+
}, [key, storedValue]);
|
|
23
|
+
|
|
24
|
+
return [storedValue, setStoredValue] as const;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default useLocalStorage;
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as Chat } from "./chat/Chat";
|
|
1
|
+
export { default as Chat } from "./chat/Chat";
|