@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.
@@ -0,0 +1 @@
1
+ @import"https://unpkg.com/github-markdown-css/github-markdown.css";.chat-container{display:flex;height:100vh;width:100%;position:relative}.chat-sidebar{background-color:#f5f5f5;border-right:1px solid #e0e0e0;display:flex;flex-direction:column}.history-header{padding:16px;border-bottom:1px solid #e0e0e0;display:flex;justify-content:space-between;align-items:center}.history-header h3{margin:0;font-size:16px;color:#333}.close-button{background:none;border:none;font-size:20px;cursor:pointer;color:#666}.history-list{flex:none;width:280px;overflow-y:auto;border-right:1px solid #e0e0e0}.history-item{padding:12px;margin-bottom:8px;background-color:#fff;cursor:pointer;transition:background-color .2s}.history-item:hover{background-color:#e8e8e8}.history-item.active{background-color:#e3f2fd;border-left:3px solid #2196f3}.history-title{font-size:14px;color:#333;margin-bottom:4px}.history-time{font-size:12px;color:#666}.chat-main{overflow:hidden;flex:1;display:flex;flex-direction:column}.chat-header{padding:16px;border-bottom:1px solid #e0e0e0;display:flex;justify-content:flex-end;gap:.5rem}.history-button{padding:8px 16px;background-color:#f0f0f0;border:1px solid #e0e0e0;border-radius:4px;cursor:pointer;font-size:14px;color:#333}.history-button:hover{background-color:#e0e0e0}.chat-messages{flex:1;overflow-y:auto;padding:1rem;display:flex;flex-direction:column;gap:1rem}.message{display:flex;max-width:80%}.message.human{margin-left:auto}.message.ai{margin-right:auto}.message-content{padding:.75rem 1rem;border-radius:8px;border:1px solid #e5e7eb;display:flex;flex-direction:column;gap:.5rem;max-width:100%}.message-text{word-break:break-word;line-height:1.5}.message-image{margin:.5rem 0}.message-image img{max-width:100%;border-radius:4px;box-shadow:0 1px 3px #0000001a}.message-audio{margin:.5rem 0}.message-audio audio{width:100%;max-width:300px}.message-meta{display:flex;align-items:center;gap:.75rem;font-size:.75rem;color:#6b7280;margin-top:.25rem}.message-time{font-family:monospace}.token-info{display:flex;gap:.5rem;align-items:center}.token-item{display:flex;align-items:center;gap:.25rem;background-color:#fff;padding:.125rem .375rem;border-radius:4px;font-family:monospace}.token-emoji{font-size:.875rem}.message.human .message-content{background-color:#3b82f6;color:#fff;border-color:#3b82f6}.message.human .message-meta{color:#fffc}.message.ai .message-content{color:#1f2937}.message.tool{width:100%;max-width:100%}.tool-message{width:100%;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden}.tool-header{display:flex;align-items:center;justify-content:space-between;padding:.5rem 1rem;background-color:#f9fafb;border-bottom:1px solid #e5e7eb;cursor:pointer}.tool-header:hover{background-color:#f3f4f6}.tool-title{font-weight:500;color:#374151}.tool-content{padding:1rem}.tool-input{background-color:#f9fafb;padding:.75rem;border-radius:4px;margin-bottom:.5rem;font-family:monospace;white-space:pre-wrap;word-break:break-all}.tool-output{background-color:#fff;padding:.75rem;border-radius:4px;font-family:monospace;border:1px solid #e5e7eb;margin-bottom:.5rem}.chat-input{border-top:1px solid #e5e7eb;padding:.5rem 1rem 1rem;background-color:#fff}.chat-input-header{display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem}.input-container{display:flex;gap:.5rem}.input-textarea{flex:1;padding:.75rem;border:1px solid #e5e7eb;border-radius:8px;resize:none;font-size:.875rem;line-height:1.25rem}.input-textarea:focus{outline:none;border-color:#3b82f6}.send-button{padding:.5rem 1rem;background-color:#3b82f6;color:#fff;border:none;border-radius:8px;cursor:pointer;font-weight:500;transition:all .2s}.send-button:hover{background-color:#2563eb}.send-button:disabled{background-color:#93c5fd;cursor:not-allowed}.send-button.interrupt{background-color:#ef4444}.send-button.interrupt:hover{background-color:#dc2626}.collapsed .tool-content{display:none}.expand-icon{transition:transform .2s}.collapsed .expand-icon{transform:rotate(-90deg)}.loading-indicator{padding:12px 16px;margin:8px 0;background-color:#f5f5f5;border-radius:8px;color:#666;font-size:14px;text-align:center;animation:pulse 1.5s infinite;display:flex;align-items:center;justify-content:center;gap:12px}.interrupt-button{padding:4px 12px;background-color:#ef4444;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px;transition:background-color .2s}.interrupt-button:hover{background-color:#dc2626}@keyframes pulse{0%{opacity:.6}50%{opacity:1}to{opacity:.6}}.error-message{padding:12px 16px;margin:8px 0;background-color:#fee2e2;border:1px solid #fecaca;border-radius:8px;color:#dc2626;font-size:14px;text-align:center}.markdown-body p{text-align:left}.edit-params-button{padding:6px 12px;background-color:#f0f0f0;border:1px solid #e0e0e0;border-radius:4px;cursor:pointer;font-size:.8rem;color:#333;white-space:nowrap}.edit-params-button:hover{background-color:#e0e0e0}.file-list{display:flex;gap:.5rem;border-radius:8px;flex:1}.file-list-header{margin-bottom:1rem}.file-upload-button{display:inline-flex;align-items:center;justify-content:center;width:80px;height:80px;color:#929292;background-color:#ebebeb;border-radius:6px;cursor:pointer;transition:all .2s}.file-upload-button svg{width:32px;height:32px}.file-upload-button.empty{width:32px;height:32px}.file-upload-button.empty svg{width:20px;height:20px}.file-list-content{display:flex;flex-wrap:wrap;gap:.5rem}.file-item{position:relative;width:80px;height:80px;border-radius:6px;overflow:hidden}.file-item img{border:1px solid #e5e7eb;overflow:hidden}.file-preview{width:100%;height:100%;object-fit:cover}.file-info{padding:.75rem}.file-name{display:block;font-size:.875rem;color:#374151;margin-bottom:.5rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-actions{display:flex;gap:.5rem}.upload-button,.remove-button{flex:1;padding:.375rem .75rem;border:none;border-radius:4px;font-size:.75rem;cursor:pointer;transition:all .2s}.upload-button{color:#000}.upload-button:hover{background-color:#2563eb}.remove-button{position:absolute;top:2px;right:2px;width:20px;height:20px;background-color:#00000080;color:#fff;border:none;border-radius:50%;cursor:pointer;font-size:16px;line-height:1;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.remove-button:hover{background-color:#000000b3}.upload-button:disabled,.remove-button:disabled{opacity:.5;cursor:not-allowed}.json-editor-popup-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000}.json-editor-popup-content{background-color:#fff;padding:20px;border-radius:8px;box-shadow:0 2px 10px #0000001a;width:80%;max-width:600px;display:flex;flex-direction:column}.json-editor-popup-content h2{margin-top:0;margin-bottom:15px;font-size:1.5rem;color:#333}.json-editor-popup-content textarea{width:100%;padding:10px;border:1px solid #ccc;border-radius:4px;font-family:monospace;font-size:.9rem;resize:vertical;box-sizing:border-box}.json-editor-popup-content .error-message{color:red;font-size:.8rem;margin-top:5px;margin-bottom:10px}.popup-actions{display:flex;justify-content:flex-end;margin-top:15px;gap:10px}.popup-actions button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer;font-size:.9rem}.popup-actions .save-button{background-color:#3b82f6;color:#fff}.popup-actions .save-button:hover{background-color:#2563eb}.popup-actions .cancel-button{background-color:#e5e7eb;color:#374151}.popup-actions .cancel-button:hover{background-color:#d1d5db}.login-container{max-width:600px;margin:2rem auto;padding:2rem;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.header-group{display:flex;gap:1rem;align-items:flex-start;padding:1rem;background:#f8f9fa;border-radius:4px;position:relative}.form-group{flex:1}.form-group label{display:block;margin-bottom:.5rem;color:#333;font-weight:500}.form-group input{width:100%;padding:.5rem;border:1px solid #ddd;border-radius:4px;font-size:1rem}.form-group input:focus{outline:none;border-color:#007bff;box-shadow:0 0 0 2px #007bff40}.button-group{display:flex;gap:1rem;margin-top:1rem}button{padding:.5rem 1rem;border:none;border-radius:4px;font-size:1rem;cursor:pointer;transition:background-color .2s}button[type=submit]{background-color:#007bff;color:#fff}button[type=submit]:hover{background-color:#0056b3}button[type=button]{background-color:#6c757d;color:#fff}button[type=button]:hover{background-color:#5a6268}.remove-header{background-color:#dc3545;color:#fff;padding:.25rem .5rem;font-size:.875rem}.remove-header:hover{background-color:#c82333}p{margin-bottom:1.5rem;color:#666;text-align:center}
package/dist/index.html CHANGED
@@ -1,9 +1,9 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Vite App</title>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Vite App</title>
7
7
  <style>
8
8
  body,
9
9
  #root,
@@ -13,11 +13,11 @@
13
13
  width: 100%;
14
14
  height: 100%;
15
15
  }
16
- </style>
17
- <script type="module" crossorigin src="/assets/index-CCaE6qp1.js"></script>
18
- <link rel="stylesheet" crossorigin href="/assets/index-CBfok6qC.css">
19
- </head>
20
- <body>
21
- <div id="root"></div>
22
- </body>
23
- </html>
16
+ </style>
17
+ <script type="module" crossorigin src="/assets/index-BHPbGlnP.js"></script>
18
+ <link rel="stylesheet" crossorigin href="/assets/index-Du4LMUX2.css">
19
+ </head>
20
+ <body>
21
+ <div id="root"></div>
22
+ </body>
23
+ </html>
package/index.html CHANGED
@@ -1,22 +1,22 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Vite App</title>
7
- <style>
8
- body,
9
- #root,
10
- html {
11
- margin: 0;
12
- padding: 0;
13
- width: 100%;
14
- height: 100%;
15
- }
16
- </style>
17
- </head>
18
- <body>
19
- <div id="root"></div>
20
- <script type="module" src="./test/main.tsx"></script>
21
- </body>
22
- </html>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Vite App</title>
7
+ <style>
8
+ body,
9
+ #root,
10
+ html {
11
+ margin: 0;
12
+ padding: 0;
13
+ width: 100%;
14
+ height: 100%;
15
+ }
16
+ </style>
17
+ </head>
18
+ <body>
19
+ <div id="root"></div>
20
+ <script type="module" src="./test/main.tsx"></script>
21
+ </body>
22
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langgraph-js/ui",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -12,20 +12,21 @@
12
12
  "main": "./dist/react/src/index.js",
13
13
  "type": "module",
14
14
  "dependencies": {
15
+ "@langgraph-js/langgraph-pg": "^1.1.2",
15
16
  "@nanostores/react": "^1.0.0",
16
17
  "@vitejs/plugin-basic-ssl": "^2.0.0",
17
- "@vitejs/plugin-react": "^4.3.4",
18
+ "@vitejs/plugin-react": "^4.4.1",
18
19
  "nanostores": "^1.0.1",
19
- "react": "^19.0.0",
20
- "react-dom": "^19.0.0",
21
- "vite": "^6.2.0",
20
+ "react": "^19.1.0",
21
+ "react-dom": "^19.1.0",
22
22
  "react-markdown": "^10.1.0",
23
23
  "remark-gfm": "^4.0.1",
24
- "@langgraph-js/sdk": "1.1.6"
24
+ "vite": "^6.3.5",
25
+ "@langgraph-js/sdk": "1.1.7"
25
26
  },
26
27
  "devDependencies": {
27
- "@types/react": "^19.0.10",
28
- "@types/react-dom": "^19.0.4"
28
+ "@types/react": "^19.1.3",
29
+ "@types/react-dom": "^19.1.3"
29
30
  },
30
31
  "scripts": {
31
32
  "dev": "vite",
package/src/chat/Chat.tsx CHANGED
@@ -1,150 +1,164 @@
1
- import React, { useState } from "react";
2
- import "./chat.css";
3
- import MessageHuman from "./components/MessageHuman";
4
- import MessageAI from "./components/MessageAI";
5
- import MessageTool from "./components/MessageTool";
6
- import HistoryList from "./components/HistoryList";
7
- import { ChatProvider, useChat } from "./context/ChatContext";
8
- import { UsageMetadata } from "./components/UsageMetadata";
9
- import { formatTime, formatTokens, getMessageContent, Message } from "@langgraph-js/sdk";
10
- import FileList from "./components/FileList";
11
-
12
- const ChatMessages: React.FC = () => {
13
- const { renderMessages, loading, inChatError, client, collapsedTools, toggleToolCollapse } = useChat();
14
-
15
- return (
16
- <div className="chat-messages">
17
- {renderMessages.map((message) =>
18
- message.type === "human" ? (
19
- <MessageHuman content={message.content} key={message.unique_id} />
20
- ) : message.type === "tool" ? (
21
- <MessageTool
22
- key={message.unique_id}
23
- message={message}
24
- client={client!}
25
- getMessageContent={getMessageContent}
26
- formatTokens={formatTokens}
27
- isCollapsed={collapsedTools.includes(message.id!)}
28
- onToggleCollapse={() => toggleToolCollapse(message.id!)}
29
- />
30
- ) : (
31
- <MessageAI key={message.unique_id} message={message} />
32
- )
33
- )}
34
- {loading && <div className="loading-indicator">正在思考中...</div>}
35
- {inChatError && <div className="error-message">{inChatError}</div>}
36
- </div>
37
- );
38
- };
39
-
40
- const ChatInput: React.FC = () => {
41
- const { userInput, setUserInput, loading, sendMessage, stopGeneration, currentAgent, setCurrentAgent, client } = useChat();
42
- const [extraParams, setExtraParams] = useState({});
43
- const [imageUrls, setImageUrls] = useState<string[]>([]);
44
-
45
- const handleFileUploaded = (url: string) => {
46
- setImageUrls((prev) => [...prev, url]);
47
- };
48
-
49
- const sendMultiModalMessage = () => {
50
- const content: Message[] = [
51
- {
52
- type: "human",
53
- content: [
54
- {
55
- type: "text",
56
- text: userInput,
57
- },
58
- ...imageUrls.map((url) => ({
59
- type: "image_url" as const,
60
- image_url: { url },
61
- })),
62
- ],
63
- },
64
- ];
65
-
66
- sendMessage(content, {
67
- extraParams,
68
- });
69
-
70
- // 清空图片列表
71
- setImageUrls([]);
72
- };
73
-
74
- const handleKeyPress = (event: React.KeyboardEvent) => {
75
- if (event.key === "Enter" && !event.shiftKey) {
76
- event.preventDefault();
77
- sendMultiModalMessage();
78
- }
79
- };
80
-
81
- return (
82
- <div className="chat-input">
83
- <FileList onFileUploaded={handleFileUploaded} />
84
- <div className="chat-input-header">
85
- <select value={currentAgent} onChange={(e) => setCurrentAgent(e.target.value)}>
86
- {client?.availableAssistants.map((i) => {
87
- return <option value={i.graph_id}>{i.name}</option>;
88
- })}
89
- </select>
90
- <UsageMetadata usage_metadata={client?.tokenCounter || {}} />
91
- </div>
92
- <div className="input-container">
93
- <textarea
94
- className="input-textarea"
95
- rows={2}
96
- value={userInput}
97
- onChange={(e) => setUserInput(e.target.value)}
98
- onKeyDown={handleKeyPress}
99
- placeholder="输入消息..."
100
- disabled={loading}
101
- />
102
- <button
103
- className={`send-button ${loading ? "interrupt" : ""}`}
104
- onClick={() => (loading ? stopGeneration() : sendMultiModalMessage())}
105
- disabled={!loading && !userInput.trim() && imageUrls.length === 0}
106
- >
107
- {loading ? "中断" : "发送"}
108
- </button>
109
- </div>
110
- </div>
111
- );
112
- };
113
-
114
- const Chat: React.FC = () => {
115
- const { showHistory, toggleHistoryVisible } = useChat();
116
-
117
- return (
118
- <div className="chat-container">
119
- {showHistory && <HistoryList onClose={() => toggleHistoryVisible()} formatTime={formatTime} />}
120
- <div className="chat-main">
121
- <div className="chat-header">
122
- <button className="history-button" onClick={() => toggleHistoryVisible()}>
123
- 历史记录
124
- </button>
125
- <button
126
- className="history-button"
127
- onClick={() => {
128
- localStorage.setItem("code", "");
129
- location.reload();
130
- }}
131
- >
132
- 退出登陆
133
- </button>
134
- </div>
135
- <ChatMessages />
136
- <ChatInput />
137
- </div>
138
- </div>
139
- );
140
- };
141
-
142
- const ChatWrapper: React.FC = () => {
143
- return (
144
- <ChatProvider>
145
- <Chat />
146
- </ChatProvider>
147
- );
148
- };
149
-
150
- export default ChatWrapper;
1
+ import React, { useState, useEffect } from "react";
2
+ import "./chat.css";
3
+ import MessageHuman from "./components/MessageHuman";
4
+ import MessageAI from "./components/MessageAI";
5
+ import MessageTool from "./components/MessageTool";
6
+ import HistoryList from "./components/HistoryList";
7
+ import { ChatProvider, useChat } from "./context/ChatContext";
8
+ import { ExtraParamsProvider, useExtraParams } from "./context/ExtraParamsContext";
9
+ import { UsageMetadata } from "./components/UsageMetadata";
10
+ import { formatTime, formatTokens, getMessageContent, Message } from "@langgraph-js/sdk";
11
+ import FileList from "./components/FileList";
12
+ import JsonEditorPopup from "./components/JsonEditorPopup";
13
+
14
+ const ChatMessages: React.FC = () => {
15
+ const { renderMessages, loading, inChatError, client, collapsedTools, toggleToolCollapse } = useChat();
16
+
17
+ return (
18
+ <div className="chat-messages">
19
+ {renderMessages.map((message) =>
20
+ message.type === "human" ? (
21
+ <MessageHuman content={message.content} key={message.unique_id} />
22
+ ) : message.type === "tool" ? (
23
+ <MessageTool
24
+ key={message.unique_id}
25
+ message={message}
26
+ client={client!}
27
+ getMessageContent={getMessageContent}
28
+ formatTokens={formatTokens}
29
+ isCollapsed={collapsedTools.includes(message.id!)}
30
+ onToggleCollapse={() => toggleToolCollapse(message.id!)}
31
+ />
32
+ ) : (
33
+ <MessageAI key={message.unique_id} message={message} />
34
+ )
35
+ )}
36
+ {loading && <div className="loading-indicator">正在思考中...</div>}
37
+ {inChatError && <div className="error-message">{inChatError}</div>}
38
+ </div>
39
+ );
40
+ };
41
+
42
+ const ChatInput: React.FC = () => {
43
+ const { userInput, setUserInput, loading, sendMessage, stopGeneration, currentAgent, setCurrentAgent, client } = useChat();
44
+ const { extraParams } = useExtraParams();
45
+ const [imageUrls, setImageUrls] = useState<string[]>([]);
46
+
47
+ const handleFileUploaded = (url: string) => {
48
+ setImageUrls((prev) => [...prev, url]);
49
+ };
50
+
51
+ const sendMultiModalMessage = () => {
52
+ const content: Message[] = [
53
+ {
54
+ type: "human",
55
+ content: [
56
+ {
57
+ type: "text",
58
+ text: userInput,
59
+ },
60
+ ...imageUrls.map((url) => ({
61
+ type: "image_url" as const,
62
+ image_url: { url },
63
+ })),
64
+ ],
65
+ },
66
+ ];
67
+
68
+ sendMessage(content, {
69
+ extraParams,
70
+ });
71
+
72
+ // 清空图片列表
73
+ setImageUrls([]);
74
+ };
75
+
76
+ const handleKeyPress = (event: React.KeyboardEvent) => {
77
+ if (event.key === "Enter" && !event.shiftKey) {
78
+ event.preventDefault();
79
+ sendMultiModalMessage();
80
+ }
81
+ };
82
+
83
+ return (
84
+ <div className="chat-input">
85
+ <div className="chat-input-header">
86
+ <FileList onFileUploaded={handleFileUploaded} />
87
+ <UsageMetadata usage_metadata={client?.tokenCounter || {}} />
88
+ <select value={currentAgent} onChange={(e) => setCurrentAgent(e.target.value)}>
89
+ {client?.availableAssistants.map((i) => {
90
+ return (
91
+ <option value={i.graph_id} key={i.graph_id}>
92
+ {i.name}
93
+ </option>
94
+ );
95
+ })}
96
+ </select>
97
+ </div>
98
+ <div className="input-container">
99
+ <textarea
100
+ className="input-textarea"
101
+ rows={2}
102
+ value={userInput}
103
+ onChange={(e) => setUserInput(e.target.value)}
104
+ onKeyDown={handleKeyPress}
105
+ placeholder="输入消息..."
106
+ disabled={loading}
107
+ />
108
+ <button
109
+ className={`send-button ${loading ? "interrupt" : ""}`}
110
+ onClick={() => (loading ? stopGeneration() : sendMultiModalMessage())}
111
+ disabled={!loading && !userInput.trim() && imageUrls.length === 0}
112
+ >
113
+ {loading ? "中断" : "发送"}
114
+ </button>
115
+ </div>
116
+ </div>
117
+ );
118
+ };
119
+
120
+ const Chat: React.FC = () => {
121
+ const [isPopupOpen, setIsPopupOpen] = useState(false);
122
+ const { showHistory, toggleHistoryVisible } = useChat();
123
+ const { extraParams, setExtraParams } = useExtraParams();
124
+
125
+ return (
126
+ <div className="chat-container">
127
+ {showHistory && <HistoryList onClose={() => toggleHistoryVisible()} formatTime={formatTime} />}
128
+ <div className="chat-main">
129
+ <div className="chat-header">
130
+ <button onClick={() => setIsPopupOpen(true)} className="edit-params-button">
131
+ 编辑参数
132
+ </button>
133
+ <button className="history-button" onClick={() => toggleHistoryVisible()}>
134
+ 历史记录
135
+ </button>
136
+ <button
137
+ className="history-button"
138
+ onClick={() => {
139
+ localStorage.setItem("code", "");
140
+ location.reload();
141
+ }}
142
+ >
143
+ 退出登陆
144
+ </button>
145
+ </div>
146
+ <ChatMessages />
147
+ <ChatInput />
148
+ <JsonEditorPopup isOpen={isPopupOpen} initialJson={extraParams} onClose={() => setIsPopupOpen(false)} onSave={setExtraParams} />
149
+ </div>
150
+ </div>
151
+ );
152
+ };
153
+
154
+ const ChatWrapper: React.FC = () => {
155
+ return (
156
+ <ChatProvider>
157
+ <ExtraParamsProvider>
158
+ <Chat />
159
+ </ExtraParamsProvider>
160
+ </ChatProvider>
161
+ );
162
+ };
163
+
164
+ export default ChatWrapper;