vzcode 1.51.0 → 1.53.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/{index-DkCXnCvn.js → index-BxRNGHcB.js} +111 -111
- package/dist/assets/{index-BXyJ2hMf.css → index-L1AANcDx.css} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/client/CodeEditor/json1PresenceDisplay.ts +40 -7
- package/src/client/VZSidebar/AIChat/ChatInput.tsx +46 -43
- package/src/client/VZSidebar/AIChat/DiffView.scss +0 -26
- package/src/client/VZSidebar/AIChat/DiffView.tsx +1 -67
- package/src/client/VZSidebar/AIChat/Message.tsx +60 -8
- package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +1 -1
- package/src/client/VZSidebar/AIChat/index.tsx +138 -97
- package/src/client/VZSidebar/AIChat/styles.scss +27 -0
- package/src/client/VZSidebar/index.tsx +1 -1
- package/src/client/featureFlags.ts +2 -0
- package/src/server/aiChatHandler/index.ts +3 -3
- package/src/server/aiChatHandler/llmStreaming.ts +15 -12
- package/src/server/generateAIResponse.ts +3 -0
- package/src/server/handleAICopilot.ts +3 -0
- package/src/server/index.ts +7 -7
|
@@ -13,6 +13,8 @@ import './styles.scss';
|
|
|
13
13
|
|
|
14
14
|
const defaultAIChatEndpoint = '/api/ai-chat/';
|
|
15
15
|
|
|
16
|
+
const DEBUG = false;
|
|
17
|
+
|
|
16
18
|
export const AIChat = () => {
|
|
17
19
|
const { aiChatMessage, setAIChatMessage } =
|
|
18
20
|
useContext(VZCodeContext);
|
|
@@ -54,110 +56,149 @@ export const AIChat = () => {
|
|
|
54
56
|
// Check if this is the first time opening the chat (no messages)
|
|
55
57
|
const isEmptyState = rawMessages.length === 0;
|
|
56
58
|
|
|
57
|
-
const handleSendMessage = useCallback(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const handleSendMessage = useCallback(
|
|
60
|
+
async (messageToSend?: string) => {
|
|
61
|
+
DEBUG &&
|
|
62
|
+
console.log(
|
|
63
|
+
'AIChat: handleSendMessage called with:',
|
|
64
|
+
messageToSend,
|
|
65
|
+
'aiChatMessage:',
|
|
66
|
+
aiChatMessage,
|
|
67
|
+
);
|
|
68
|
+
const messageContent = messageToSend || aiChatMessage;
|
|
69
|
+
if (
|
|
70
|
+
!messageContent ||
|
|
71
|
+
typeof messageContent !== 'string' ||
|
|
72
|
+
!messageContent.trim() ||
|
|
73
|
+
isLoading
|
|
74
|
+
)
|
|
75
|
+
return;
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
method: 'POST',
|
|
70
|
-
headers: {
|
|
71
|
-
'Content-Type': 'application/json',
|
|
72
|
-
},
|
|
73
|
-
body: JSON.stringify({
|
|
74
|
-
...aiChatOptions,
|
|
75
|
-
vizId: aiChatOptions.vizId,
|
|
76
|
-
content: currentPrompt,
|
|
77
|
-
chatId: currentChatId,
|
|
78
|
-
mode: aiChatMode,
|
|
79
|
-
}),
|
|
80
|
-
});
|
|
77
|
+
const currentPrompt = aiChatMessage.trim();
|
|
78
|
+
setAIChatMessage('');
|
|
79
|
+
setIsLoading(true);
|
|
80
|
+
setErrorMessage(null); // Clear any previous errors
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
// Call backend endpoint for AI response
|
|
83
|
+
// The server will handle all ShareDB operations including adding the user message
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(aiChatEndpoint, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: {
|
|
88
|
+
'Content-Type': 'application/json',
|
|
89
|
+
},
|
|
90
|
+
body: JSON.stringify({
|
|
91
|
+
...aiChatOptions,
|
|
92
|
+
vizId: aiChatOptions.vizId,
|
|
93
|
+
content: messageContent.trim(),
|
|
94
|
+
chatId: currentChatId,
|
|
95
|
+
mode: aiChatMode,
|
|
96
|
+
}),
|
|
97
|
+
});
|
|
87
98
|
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`HTTP error! status: ${response.status}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
90
104
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
responseData.outcome === 'failure' &&
|
|
94
|
-
responseData.error
|
|
95
|
-
) {
|
|
96
|
-
const errorMessage = responseData.error.message;
|
|
105
|
+
// Parse the response to check for errors
|
|
106
|
+
const responseData = await response.json();
|
|
97
107
|
|
|
98
|
-
// Check if
|
|
108
|
+
// Check if the response contains a VizHub error
|
|
99
109
|
if (
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
responseData.outcome === 'failure' &&
|
|
111
|
+
responseData.error
|
|
102
112
|
) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
const errorMessage = responseData.error.message;
|
|
114
|
+
|
|
115
|
+
// Check if this is the specific permission error that should trigger auto-fork
|
|
116
|
+
if (
|
|
117
|
+
errorMessage ===
|
|
118
|
+
'You do not have permission to use AI chat on this visualization. Only users with edit access can use this feature. Fork the viz to edit it.'
|
|
119
|
+
) {
|
|
120
|
+
// Trigger auto-fork instead of showing error
|
|
121
|
+
try {
|
|
122
|
+
await autoForkAndRetryAI?.(
|
|
123
|
+
currentPrompt,
|
|
124
|
+
aiChatMode,
|
|
125
|
+
);
|
|
126
|
+
// If we reach here, the fork was successful and redirect should happen
|
|
127
|
+
return;
|
|
128
|
+
} catch (forkError) {
|
|
129
|
+
console.error('Auto-fork failed:', forkError);
|
|
130
|
+
setErrorMessage(
|
|
131
|
+
'Failed to fork visualization. Please try forking manually.',
|
|
132
|
+
);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
117
135
|
}
|
|
136
|
+
|
|
137
|
+
// For other errors, show the error message
|
|
138
|
+
setErrorMessage(errorMessage);
|
|
139
|
+
return;
|
|
118
140
|
}
|
|
119
141
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
142
|
+
// The backend handles all ShareDB operations for successful responses
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('Error getting AI response:', error);
|
|
145
|
+
setErrorMessage(
|
|
146
|
+
'Failed to send message. Please try again.',
|
|
147
|
+
);
|
|
148
|
+
} finally {
|
|
149
|
+
setIsLoading(false);
|
|
123
150
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
aiChatMessage,
|
|
136
|
-
isLoading,
|
|
137
|
-
aiChatEndpoint,
|
|
138
|
-
aiChatOptions,
|
|
139
|
-
currentChatId,
|
|
140
|
-
aiChatMode,
|
|
141
|
-
autoForkAndRetryAI,
|
|
142
|
-
]);
|
|
151
|
+
},
|
|
152
|
+
[
|
|
153
|
+
aiChatMessage,
|
|
154
|
+
isLoading,
|
|
155
|
+
aiChatEndpoint,
|
|
156
|
+
aiChatOptions,
|
|
157
|
+
currentChatId,
|
|
158
|
+
aiChatMode,
|
|
159
|
+
autoForkAndRetryAI,
|
|
160
|
+
],
|
|
161
|
+
);
|
|
143
162
|
|
|
144
163
|
// Check for stored AI prompt on component mount (post-fork restoration)
|
|
145
164
|
useEffect(() => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
DEBUG &&
|
|
166
|
+
console.log('AIChat: Checking for stored AI prompt');
|
|
167
|
+
if (getStoredAIPrompt) {
|
|
168
|
+
const storedPrompt = getStoredAIPrompt();
|
|
169
|
+
DEBUG &&
|
|
170
|
+
console.log(
|
|
171
|
+
'AIChat: Stored prompt result:',
|
|
172
|
+
storedPrompt,
|
|
173
|
+
);
|
|
174
|
+
if (storedPrompt) {
|
|
175
|
+
// Restore the prompt and mode
|
|
176
|
+
DEBUG &&
|
|
177
|
+
console.log('AIChat: Restoring prompt and mode');
|
|
178
|
+
setAIChatMessage(storedPrompt.prompt);
|
|
179
|
+
setAIChatMode(
|
|
180
|
+
storedPrompt.modelName === 'ask' ? 'ask' : 'edit',
|
|
181
|
+
);
|
|
153
182
|
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
// Clear the stored prompt
|
|
184
|
+
DEBUG &&
|
|
185
|
+
console.log('AIChat: Clearing stored prompt');
|
|
186
|
+
clearStoredAIPrompt();
|
|
156
187
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
188
|
+
// Auto-submit the restored prompt after a short delay
|
|
189
|
+
DEBUG &&
|
|
190
|
+
console.log('AIChat: Scheduling auto-submit');
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
DEBUG &&
|
|
193
|
+
console.log(
|
|
194
|
+
'AIChat: Auto-submitting restored prompt',
|
|
195
|
+
);
|
|
196
|
+
handleSendMessage(storedPrompt.prompt);
|
|
197
|
+
}, 100);
|
|
198
|
+
} else {
|
|
199
|
+
DEBUG &&
|
|
200
|
+
console.log('AIChat: No stored prompt found');
|
|
201
|
+
}
|
|
161
202
|
}
|
|
162
203
|
}, [
|
|
163
204
|
getStoredAIPrompt,
|
|
@@ -181,7 +222,7 @@ export const AIChat = () => {
|
|
|
181
222
|
<div className="ai-chat-empty">
|
|
182
223
|
<div className="ai-chat-empty-icon">✨</div>
|
|
183
224
|
<h3 className="ai-chat-empty-title">
|
|
184
|
-
Hi
|
|
225
|
+
Hi! I'm here to help you edit with AI.
|
|
185
226
|
</h3>
|
|
186
227
|
<div className="ai-chat-empty-text">
|
|
187
228
|
How can I help you?
|
|
@@ -194,7 +235,7 @@ export const AIChat = () => {
|
|
|
194
235
|
<button
|
|
195
236
|
className="ai-chat-suggested-prompt"
|
|
196
237
|
onClick={() =>
|
|
197
|
-
|
|
238
|
+
handleSendMessage(
|
|
198
239
|
'Explain how this works',
|
|
199
240
|
)
|
|
200
241
|
}
|
|
@@ -204,7 +245,7 @@ export const AIChat = () => {
|
|
|
204
245
|
<button
|
|
205
246
|
className="ai-chat-suggested-prompt"
|
|
206
247
|
onClick={() =>
|
|
207
|
-
|
|
248
|
+
handleSendMessage(
|
|
208
249
|
'How could I change it so that the circles are bigger?',
|
|
209
250
|
)
|
|
210
251
|
}
|
|
@@ -215,7 +256,7 @@ export const AIChat = () => {
|
|
|
215
256
|
<button
|
|
216
257
|
className="ai-chat-suggested-prompt"
|
|
217
258
|
onClick={() =>
|
|
218
|
-
|
|
259
|
+
handleSendMessage(
|
|
219
260
|
'What does this function do?',
|
|
220
261
|
)
|
|
221
262
|
}
|
|
@@ -225,7 +266,7 @@ export const AIChat = () => {
|
|
|
225
266
|
<button
|
|
226
267
|
className="ai-chat-suggested-prompt"
|
|
227
268
|
onClick={() =>
|
|
228
|
-
|
|
269
|
+
handleSendMessage(
|
|
229
270
|
'How can I make this more accessible?',
|
|
230
271
|
)
|
|
231
272
|
}
|
|
@@ -241,7 +282,7 @@ export const AIChat = () => {
|
|
|
241
282
|
<button
|
|
242
283
|
className="ai-chat-suggested-prompt"
|
|
243
284
|
onClick={() =>
|
|
244
|
-
|
|
285
|
+
handleSendMessage(
|
|
245
286
|
'Change the circles to squares',
|
|
246
287
|
)
|
|
247
288
|
}
|
|
@@ -251,7 +292,7 @@ export const AIChat = () => {
|
|
|
251
292
|
<button
|
|
252
293
|
className="ai-chat-suggested-prompt"
|
|
253
294
|
onClick={() =>
|
|
254
|
-
|
|
295
|
+
handleSendMessage(
|
|
255
296
|
'Add a button that toggles the animation',
|
|
256
297
|
)
|
|
257
298
|
}
|
|
@@ -262,7 +303,7 @@ export const AIChat = () => {
|
|
|
262
303
|
<button
|
|
263
304
|
className="ai-chat-suggested-prompt"
|
|
264
305
|
onClick={() =>
|
|
265
|
-
|
|
306
|
+
handleSendMessage(
|
|
266
307
|
'Fix the CSS so the layout is responsive',
|
|
267
308
|
)
|
|
268
309
|
}
|
|
@@ -273,7 +314,7 @@ export const AIChat = () => {
|
|
|
273
314
|
<button
|
|
274
315
|
className="ai-chat-suggested-prompt"
|
|
275
316
|
onClick={() =>
|
|
276
|
-
|
|
317
|
+
handleSendMessage(
|
|
277
318
|
'Refactor this function to use async/await',
|
|
278
319
|
)
|
|
279
320
|
}
|
|
@@ -150,6 +150,33 @@
|
|
|
150
150
|
line-height: 1.4;
|
|
151
151
|
word-wrap: break-word;
|
|
152
152
|
|
|
153
|
+
// Undo button styles (moved from DiffView)
|
|
154
|
+
.undo-button-container {
|
|
155
|
+
display: flex;
|
|
156
|
+
justify-content: flex-end;
|
|
157
|
+
margin-top: 16px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.undo-button {
|
|
161
|
+
background-color: #dc3545;
|
|
162
|
+
color: white;
|
|
163
|
+
border: none;
|
|
164
|
+
border-radius: 4px;
|
|
165
|
+
padding: 4px 8px;
|
|
166
|
+
font-size: 12px;
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
transition: background-color 0.2s;
|
|
169
|
+
|
|
170
|
+
&:hover:not(:disabled) {
|
|
171
|
+
background-color: #c82333;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
&:disabled {
|
|
175
|
+
background-color: #6c757d;
|
|
176
|
+
cursor: not-allowed;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
153
180
|
// Markdown styling for dark backgrounds
|
|
154
181
|
pre {
|
|
155
182
|
background-color: rgba(0, 0, 0, 0.3);
|
|
@@ -21,11 +21,11 @@ const DEBUG = false;
|
|
|
21
21
|
export const handleAIChatMessage =
|
|
22
22
|
({
|
|
23
23
|
shareDBDoc,
|
|
24
|
-
|
|
24
|
+
createAIEditLocalPresence,
|
|
25
25
|
onCreditDeduction,
|
|
26
26
|
}: {
|
|
27
27
|
shareDBDoc: ShareDBDoc<VizContent>;
|
|
28
|
-
|
|
28
|
+
createAIEditLocalPresence: () => any;
|
|
29
29
|
onCreditDeduction?: any;
|
|
30
30
|
}) =>
|
|
31
31
|
async (req: any, res: any) => {
|
|
@@ -58,7 +58,7 @@ export const handleAIChatMessage =
|
|
|
58
58
|
// Create LLM function for streaming
|
|
59
59
|
const llmFunction = createLLMFunction({
|
|
60
60
|
shareDBDoc,
|
|
61
|
-
|
|
61
|
+
createAIEditLocalPresence,
|
|
62
62
|
chatId,
|
|
63
63
|
});
|
|
64
64
|
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
parseMarkdownFiles,
|
|
3
3
|
StreamingMarkdownParser,
|
|
4
4
|
} from 'llm-code-format';
|
|
5
|
-
import { ChatOpenAI } from '@langchain/openai';
|
|
6
5
|
import OpenAI from 'openai';
|
|
7
6
|
import fs from 'fs';
|
|
8
7
|
import { generateRunId } from '@vizhub/viz-utils';
|
|
@@ -44,15 +43,17 @@ const enableStreamingEditing = false;
|
|
|
44
43
|
*/
|
|
45
44
|
export const createLLMFunction = ({
|
|
46
45
|
shareDBDoc,
|
|
47
|
-
|
|
46
|
+
createAIEditLocalPresence,
|
|
48
47
|
chatId,
|
|
49
48
|
}: {
|
|
50
49
|
shareDBDoc: ShareDBDoc<VizContent>;
|
|
51
|
-
|
|
50
|
+
createAIEditLocalPresence: () => any;
|
|
52
51
|
chatId: VizChatId;
|
|
53
52
|
}) => {
|
|
54
53
|
return async (fullPrompt: string) => {
|
|
55
|
-
const localPresence =
|
|
54
|
+
const localPresence = enableStreamingEditing
|
|
55
|
+
? createAIEditLocalPresence()
|
|
56
|
+
: null;
|
|
56
57
|
|
|
57
58
|
// Create OpenRouter client for reasoning token support
|
|
58
59
|
const openRouterClient = new OpenAI({
|
|
@@ -133,13 +134,13 @@ export const createLLMFunction = ({
|
|
|
133
134
|
line,
|
|
134
135
|
);
|
|
135
136
|
|
|
136
|
-
// Update
|
|
137
|
+
// Update AI presence to show cursor at the end of the file
|
|
137
138
|
const currentFile =
|
|
138
139
|
shareDBDoc.data.files[currentEditingFileId];
|
|
139
140
|
if (currentFile && currentFile.text) {
|
|
140
141
|
const textLength = currentFile.text.length;
|
|
141
142
|
const filePresence = {
|
|
142
|
-
username: '
|
|
143
|
+
username: 'AI Editor',
|
|
143
144
|
start: [
|
|
144
145
|
'files',
|
|
145
146
|
currentEditingFileId,
|
|
@@ -157,7 +158,7 @@ export const createLLMFunction = ({
|
|
|
157
158
|
localPresence.submit(filePresence, (error) => {
|
|
158
159
|
if (error) {
|
|
159
160
|
console.warn(
|
|
160
|
-
'
|
|
161
|
+
'AI Editor line presence submission error:',
|
|
161
162
|
error,
|
|
162
163
|
);
|
|
163
164
|
}
|
|
@@ -196,11 +197,13 @@ export const createLLMFunction = ({
|
|
|
196
197
|
)({
|
|
197
198
|
model: modelName,
|
|
198
199
|
messages: [{ role: 'user', content: fullPrompt }],
|
|
199
|
-
max_tokens: 8192,
|
|
200
200
|
reasoning: {
|
|
201
201
|
effort: 'medium',
|
|
202
202
|
exclude: false,
|
|
203
203
|
},
|
|
204
|
+
provider: {
|
|
205
|
+
sort: 'throughput',
|
|
206
|
+
},
|
|
204
207
|
usage: { include: true },
|
|
205
208
|
stream: true,
|
|
206
209
|
});
|
|
@@ -275,16 +278,16 @@ export const createLLMFunction = ({
|
|
|
275
278
|
// Finalize the AI message by clearing temporary fields
|
|
276
279
|
finalizeAIMessage(shareDBDoc, chatId);
|
|
277
280
|
|
|
278
|
-
// Clear
|
|
281
|
+
// Clear AI Editor presence when done
|
|
279
282
|
DEBUG &&
|
|
280
283
|
console.log(
|
|
281
|
-
'AI editing done, clearing
|
|
284
|
+
'AI editing done, clearing AI Editor presence',
|
|
282
285
|
);
|
|
283
286
|
localPresence.submit(null, (error) => {
|
|
284
|
-
DEBUG && console.log('
|
|
287
|
+
DEBUG && console.log('AI Editor presence cleared');
|
|
285
288
|
if (error) {
|
|
286
289
|
console.warn(
|
|
287
|
-
'
|
|
290
|
+
'AI Editor presence cleanup error:',
|
|
288
291
|
error,
|
|
289
292
|
);
|
|
290
293
|
}
|
|
@@ -154,6 +154,9 @@ export const generateAIResponse = async ({
|
|
|
154
154
|
effort: 'medium',
|
|
155
155
|
exclude: false,
|
|
156
156
|
} as any, // Type assertion for OpenRouter-specific reasoning parameter
|
|
157
|
+
provider: {
|
|
158
|
+
sort: 'throughput',
|
|
159
|
+
} as any, // Type assertion for OpenRouter-specific provider parameter
|
|
157
160
|
stream: true,
|
|
158
161
|
});
|
|
159
162
|
|
|
@@ -40,6 +40,9 @@ export const handleAICopilot = () => {
|
|
|
40
40
|
apiKey: VZCODE_AI_COPILOT_API_KEY,
|
|
41
41
|
baseURL: VZCODE_AI_COPILOT_BASE_URL,
|
|
42
42
|
},
|
|
43
|
+
additionalParameters: {
|
|
44
|
+
provider: { sort: 'throughput' },
|
|
45
|
+
},
|
|
43
46
|
streaming: false,
|
|
44
47
|
};
|
|
45
48
|
debug && console.log('chatModel options:', options);
|
package/src/server/index.ts
CHANGED
|
@@ -100,20 +100,20 @@ app.use(express.static(dir));
|
|
|
100
100
|
const shareDBConnection = shareDBBackend.connect();
|
|
101
101
|
const shareDBDoc = shareDBConnection.get('documents', '1');
|
|
102
102
|
|
|
103
|
-
// Set up presence for
|
|
103
|
+
// Set up presence for AI editing following the same pattern as useShareDB.ts
|
|
104
104
|
const docPresence = shareDBConnection.getDocPresence(
|
|
105
105
|
'documents',
|
|
106
106
|
'1',
|
|
107
107
|
);
|
|
108
108
|
|
|
109
|
-
// Create local presence for
|
|
110
|
-
const
|
|
109
|
+
// Create local presence for AI editing with a unique ID
|
|
110
|
+
const generateAIEditId = () => {
|
|
111
111
|
const timestamp = Date.now().toString(36);
|
|
112
|
-
return `
|
|
112
|
+
return `ai-edit-${timestamp}`;
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
const
|
|
116
|
-
docPresence.create(
|
|
115
|
+
const createAIEditLocalPresence = () =>
|
|
116
|
+
docPresence.create(generateAIEditId());
|
|
117
117
|
|
|
118
118
|
shareDBDoc.create(initialDocument, json1Presence.type.uri);
|
|
119
119
|
|
|
@@ -137,7 +137,7 @@ app.post(
|
|
|
137
137
|
bodyParser.json(),
|
|
138
138
|
handleAIChatMessage({
|
|
139
139
|
shareDBDoc,
|
|
140
|
-
|
|
140
|
+
createAIEditLocalPresence,
|
|
141
141
|
onCreditDeduction: undefined,
|
|
142
142
|
}),
|
|
143
143
|
);
|