vzcode 2.0.0 → 2.3.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.
Files changed (218) hide show
  1. package/dist/assets/{buildWorker-Dsx6rOdK.js → buildWorker-wLkQ0yz1.js} +64 -64
  2. package/dist/assets/{index-BM8tDh6M.css → index-BoFeK4GL.css} +1 -1
  3. package/dist/assets/{index-BgqNq-ww.js → index-DuUjtrt6.js} +107 -107
  4. package/dist/assets/{worker-BeBZGG1n.js → worker-DHxSPM7N.js} +78 -78
  5. package/dist/cli.js +113 -0
  6. package/dist/index.html +2 -2
  7. package/{src/ot.ts → dist/ot.js} +2 -14
  8. package/{src/randomId.ts → dist/randomId.js} +5 -6
  9. package/dist/runCode.js +20 -0
  10. package/dist/server/aiChatHandler/aiEditing.js +89 -0
  11. package/dist/server/aiChatHandler/chatOperations.js +360 -0
  12. package/dist/server/aiChatHandler/errorHandling.js +49 -0
  13. package/dist/server/aiChatHandler/index.js +112 -0
  14. package/dist/server/aiChatHandler/llmStreaming.js +284 -0
  15. package/dist/server/aiChatHandler/validation.js +19 -0
  16. package/dist/server/computeInitialDocument.js +146 -0
  17. package/{src/server/config.ts → dist/server/config.js} +4 -7
  18. package/{src/server/featureFlags.ts → dist/server/featureFlags.js} +0 -3
  19. package/dist/server/generateAIResponse.js +129 -0
  20. package/dist/server/handleAIAssist.js +39 -0
  21. package/dist/server/handleAICopilot.js +81 -0
  22. package/dist/server/index.js +297 -0
  23. package/dist/server/livekit.js +17 -0
  24. package/dist/server/prettier.js +90 -0
  25. package/{src/server/setupEnv.ts → dist/server/setupEnv.js} +1 -2
  26. package/dist/submitOperation.js +14 -0
  27. package/dist/types.js +1 -0
  28. package/dist/utils/fileDiff.js +66 -0
  29. package/package.json +23 -22
  30. package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +0 -42
  31. package/src/client/AIAssist/AIAssistWidget/index.tsx +0 -135
  32. package/src/client/AIAssist/AIAssistWidget/style.scss +0 -47
  33. package/src/client/AIAssist/Spinner.jsx +0 -76
  34. package/src/client/AIAssist/formatFile.ts +0 -27
  35. package/src/client/AIAssist/generateFilesContext.ts +0 -25
  36. package/src/client/AIAssist/index.ts +0 -2
  37. package/src/client/AIAssist/startAIAssist.ts +0 -95
  38. package/src/client/App/index.tsx +0 -153
  39. package/src/client/App/style.scss +0 -82
  40. package/src/client/App/usePending.ts +0 -51
  41. package/src/client/App/useShareDB.ts +0 -161
  42. package/src/client/CodeEditor/Copilot/index.ts +0 -74
  43. package/src/client/CodeEditor/InteractiveWidgets/boolToggler.ts +0 -12
  44. package/src/client/CodeEditor/InteractiveWidgets/colorPicker.ts +0 -68
  45. package/src/client/CodeEditor/InteractiveWidgets/colorsInTextPlugin.ts +0 -174
  46. package/src/client/CodeEditor/InteractiveWidgets/highlightWidgets.ts +0 -79
  47. package/src/client/CodeEditor/InteractiveWidgets/index.ts +0 -44
  48. package/src/client/CodeEditor/InteractiveWidgets/markdownCheckboxToggler.ts +0 -12
  49. package/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +0 -20
  50. package/src/client/CodeEditor/InteractiveWidgets/rotateWidget.ts +0 -143
  51. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.test.ts +0 -48
  52. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.ts +0 -12
  53. package/src/client/CodeEditor/InteractiveWidgets/vec2Slider.ts +0 -25
  54. package/src/client/CodeEditor/basicSetup.ts +0 -73
  55. package/src/client/CodeEditor/getOrCreateEditor.tsx +0 -615
  56. package/src/client/CodeEditor/index.tsx +0 -202
  57. package/src/client/CodeEditor/json1PresenceBroadcast.ts +0 -41
  58. package/src/client/CodeEditor/json1PresenceDisplay.ts +0 -350
  59. package/src/client/CodeEditor/rainbowBrackets.ts +0 -122
  60. package/src/client/CodeEditor/style.scss +0 -156
  61. package/src/client/CodeEditor/typescriptExtension/worker.ts +0 -32
  62. package/src/client/CodeErrorOverlay/index.tsx +0 -77
  63. package/src/client/CodeErrorOverlay/style.scss +0 -33
  64. package/src/client/Fonts/fonts.ts +0 -8
  65. package/src/client/Icons/AdjustmentSVG.tsx +0 -19
  66. package/src/client/Icons/BugSVG.tsx +0 -20
  67. package/src/client/Icons/CloseSVG.tsx +0 -19
  68. package/src/client/Icons/CssSVG.tsx +0 -16
  69. package/src/client/Icons/DirectoryArrowSVG.tsx +0 -19
  70. package/src/client/Icons/EditSVG.tsx +0 -19
  71. package/src/client/Icons/FileSVG.tsx +0 -19
  72. package/src/client/Icons/FolderSVG.tsx +0 -20
  73. package/src/client/Icons/GearSVG.tsx +0 -16
  74. package/src/client/Icons/HtmlSVG.tsx +0 -16
  75. package/src/client/Icons/JavaScriptSVG.tsx +0 -16
  76. package/src/client/Icons/JsonSVG.tsx +0 -16
  77. package/src/client/Icons/MarkdownSVG.tsx +0 -16
  78. package/src/client/Icons/MicSVG.tsx +0 -25
  79. package/src/client/Icons/NewSVG.tsx +0 -19
  80. package/src/client/Icons/PinSVG.tsx +0 -14
  81. package/src/client/Icons/PlaySVG.tsx +0 -16
  82. package/src/client/Icons/QuestionMarkSVG.tsx +0 -18
  83. package/src/client/Icons/ReactSVG.tsx +0 -16
  84. package/src/client/Icons/SearchSVG.tsx +0 -20
  85. package/src/client/Icons/SparklesSVG.tsx +0 -23
  86. package/src/client/Icons/SplitEditorSVG.tsx +0 -34
  87. package/src/client/Icons/StopSVG.tsx +0 -15
  88. package/src/client/Icons/SvelteSVG.tsx +0 -16
  89. package/src/client/Icons/TrashSVG.tsx +0 -19
  90. package/src/client/Icons/TypeScriptSVG.tsx +0 -16
  91. package/src/client/Icons/index.tsx +0 -24
  92. package/src/client/ImageViewer/index.tsx +0 -91
  93. package/src/client/ImageViewer/style.scss +0 -41
  94. package/src/client/PresenceNotifications/PresenceNotifications.test.ts +0 -134
  95. package/src/client/PresenceNotifications/index.tsx +0 -158
  96. package/src/client/PresenceNotifications/style.scss +0 -17
  97. package/src/client/RunCodeWidget/index.tsx +0 -117
  98. package/src/client/RunCodeWidget/style.scss +0 -24
  99. package/src/client/SplitPaneResizeContext.tsx +0 -159
  100. package/src/client/TabList/Tab.tsx +0 -73
  101. package/src/client/TabList/index.tsx +0 -35
  102. package/src/client/TabList/style.scss +0 -51
  103. package/src/client/VZCodeContext/index.tsx +0 -29
  104. package/src/client/VZCodeContext/types.ts +0 -249
  105. package/src/client/VZCodeContext/useVZCodeState.ts +0 -584
  106. package/src/client/VZKeyboardShortcutsDoc.tsx +0 -148
  107. package/src/client/VZLeft.tsx +0 -28
  108. package/src/client/VZMiddle.tsx +0 -172
  109. package/src/client/VZResizer/index.tsx +0 -128
  110. package/src/client/VZResizer/styles.scss +0 -33
  111. package/src/client/VZRight.tsx +0 -113
  112. package/src/client/VZSettings.tsx +0 -275
  113. package/src/client/VZSidebar/AIChat/ChatInput.tsx +0 -196
  114. package/src/client/VZSidebar/AIChat/DiffView.scss +0 -136
  115. package/src/client/VZSidebar/AIChat/DiffView.tsx +0 -176
  116. package/src/client/VZSidebar/AIChat/Message.tsx +0 -143
  117. package/src/client/VZSidebar/AIChat/MessageList.tsx +0 -205
  118. package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +0 -35
  119. package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +0 -156
  120. package/src/client/VZSidebar/AIChat/TypingIndicator.tsx +0 -19
  121. package/src/client/VZSidebar/AIChat/index.tsx +0 -273
  122. package/src/client/VZSidebar/AIChat/styles.scss +0 -627
  123. package/src/client/VZSidebar/CreateDirModal.tsx +0 -111
  124. package/src/client/VZSidebar/CreateFileModal.tsx +0 -111
  125. package/src/client/VZSidebar/DeleteConfirmationModal.tsx +0 -76
  126. package/src/client/VZSidebar/DirectoryListing.tsx +0 -89
  127. package/src/client/VZSidebar/EmptyState.tsx +0 -11
  128. package/src/client/VZSidebar/FileListing.tsx +0 -91
  129. package/src/client/VZSidebar/FileTypeIcon.tsx +0 -49
  130. package/src/client/VZSidebar/Item.tsx +0 -237
  131. package/src/client/VZSidebar/Listing.tsx +0 -47
  132. package/src/client/VZSidebar/Search.tsx +0 -527
  133. package/src/client/VZSidebar/VisualEditor.tsx +0 -295
  134. package/src/client/VZSidebar/VoiceChatModal.tsx +0 -121
  135. package/src/client/VZSidebar/aiCopyPaste.ts +0 -199
  136. package/src/client/VZSidebar/index.tsx +0 -728
  137. package/src/client/VZSidebar/styles.scss +0 -612
  138. package/src/client/VZSidebar/useDragAndDrop.tsx +0 -408
  139. package/src/client/bootstrap.ts +0 -26
  140. package/src/client/buildWorker.ts +0 -3
  141. package/src/client/featureFlags.ts +0 -8
  142. package/src/client/generateRequestId.ts +0 -6
  143. package/src/client/getFileTree.ts +0 -53
  144. package/src/client/index.ts +0 -19
  145. package/src/client/main.jsx +0 -10
  146. package/src/client/presenceColor.ts +0 -8
  147. package/src/client/shouldTriggerRun.ts +0 -31
  148. package/src/client/sortFileTree.ts +0 -21
  149. package/src/client/tabsSearchParameters.test.ts +0 -79
  150. package/src/client/tabsSearchParameters.ts +0 -137
  151. package/src/client/themes/index.ts +0 -7
  152. package/src/client/themes/themes.ts +0 -77
  153. package/src/client/themes/uiwImports.ts +0 -32
  154. package/src/client/themes/useDynamicTheme.ts +0 -29
  155. package/src/client/themes/vizhubTheme/colors.ts +0 -80
  156. package/src/client/themes/vizhubTheme/index.ts +0 -193
  157. package/src/client/useActions.ts +0 -288
  158. package/src/client/useESLint/index.ts +0 -72
  159. package/src/client/useESLint/worker.ts +0 -113
  160. package/src/client/useEditorCache.ts +0 -35
  161. package/src/client/useFileCRUD.ts +0 -228
  162. package/src/client/useKeyboardShortcuts.ts +0 -451
  163. package/src/client/useOpenDirectories.ts +0 -99
  164. package/src/client/usePresenceAutoFollow.ts +0 -85
  165. package/src/client/usePrettier/index.ts +0 -178
  166. package/src/client/usePrettier/worker.ts +0 -109
  167. package/src/client/useRunCode.tsx +0 -15
  168. package/src/client/useRuntimeError.ts +0 -32
  169. package/src/client/useSubmitOperation.ts +0 -15
  170. package/src/client/useURLSync.ts +0 -137
  171. package/src/client/usernameLocalStorage.ts +0 -43
  172. package/src/client/utils/fileExtension.ts +0 -10
  173. package/src/client/utils/isImageFile.ts +0 -12
  174. package/src/client/vzReducer/aiChatReducer.ts +0 -44
  175. package/src/client/vzReducer/closeTabReducer.test.ts +0 -205
  176. package/src/client/vzReducer/closeTabsReducer.ts +0 -96
  177. package/src/client/vzReducer/createInitialState.ts +0 -38
  178. package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +0 -9
  179. package/src/client/vzReducer/findPane.ts +0 -19
  180. package/src/client/vzReducer/index.ts +0 -250
  181. package/src/client/vzReducer/openTabReducer.test.ts +0 -127
  182. package/src/client/vzReducer/openTabReducer.ts +0 -74
  183. package/src/client/vzReducer/searchReducer.test.ts +0 -292
  184. package/src/client/vzReducer/searchReducer.ts +0 -181
  185. package/src/client/vzReducer/setActiveFileIdReducer.ts +0 -28
  186. package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +0 -76
  187. package/src/client/vzReducer/setIsDocOpenReducer.ts +0 -9
  188. package/src/client/vzReducer/setIsSettingsOpenReducer.ts +0 -9
  189. package/src/client/vzReducer/setThemeReducer.ts +0 -9
  190. package/src/client/vzReducer/setUsernameReducer.ts +0 -9
  191. package/src/client/vzReducer/splitCurrentPaneReducer.ts +0 -66
  192. package/src/client/vzReducer/toggleAutoFollowReducer.ts +0 -12
  193. package/src/client/vzReducer/updatePane.ts +0 -40
  194. package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +0 -30
  195. package/src/client/vzReducer/visualEditorReducer.ts +0 -9
  196. package/src/reactRouterExports.ts +0 -15
  197. package/src/runCode.ts +0 -27
  198. package/src/server/aiChatHandler/aiEditing.ts +0 -152
  199. package/src/server/aiChatHandler/chatOperations.ts +0 -510
  200. package/src/server/aiChatHandler/errorHandling.ts +0 -66
  201. package/src/server/aiChatHandler/index.ts +0 -200
  202. package/src/server/aiChatHandler/llmStreaming.ts +0 -419
  203. package/src/server/aiChatHandler/undoHandler.ts +0 -97
  204. package/src/server/aiChatHandler/validation.ts +0 -24
  205. package/src/server/computeInitialDocument.ts +0 -220
  206. package/src/server/generateAIResponse.ts +0 -188
  207. package/src/server/handleAIAssist.ts +0 -53
  208. package/src/server/handleAIChatUndo.ts +0 -2
  209. package/src/server/handleAICopilot.ts +0 -97
  210. package/src/server/index.ts +0 -388
  211. package/src/server/livekit.ts +0 -24
  212. package/src/server/prettier.ts +0 -115
  213. package/src/submitOperation.ts +0 -25
  214. package/src/types.ts +0 -205
  215. package/src/utils/fileDiff.ts +0 -117
  216. package/src/vite-env.d.ts +0 -10
  217. /package/{src/server/handleAIChatMessage.ts → dist/server/handleAIChatMessage.js} +0 -0
  218. /package/{src/server/isDirectory.ts → dist/server/isDirectory.js} +0 -0
@@ -1,24 +0,0 @@
1
- /**
2
- * Validates incoming request data for AI chat messages
3
- */
4
- export const validateRequest = (req, res) => {
5
- const { content, chatId } = req.body;
6
-
7
- if (!content || typeof content !== 'string') {
8
- res.status(400).json({
9
- error:
10
- 'Invalid request: content is required and must be a string',
11
- });
12
- return false;
13
- }
14
-
15
- if (!chatId || typeof chatId !== 'string') {
16
- res.status(400).json({
17
- error:
18
- 'Invalid request: chatId is required and must be a string',
19
- });
20
- return false;
21
- }
22
-
23
- return true;
24
- };
@@ -1,220 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { randomId } from '../randomId.js';
4
- import {
5
- enableDirectories,
6
- debugDirectories,
7
- debugIgnore,
8
- } from './featureFlags.js';
9
- import createIgnore from 'ignore';
10
- import { ignoreFilePattern, baseIgnore } from './config.js';
11
- import { isDirectory } from './isDirectory.js';
12
-
13
- // Import the image file utility
14
- const isImageFile = (fileName) => {
15
- return (
16
- fileName.match(
17
- /\.(png|jpg|jpeg|gif|bmp|svg|webp)$/i,
18
- ) !== null
19
- );
20
- };
21
-
22
- /**
23
- * @param {string} fullPath - absolut path of the workspace root
24
- * @param {string} currentDirectoryPath - path where the ignore file is found, relative to fullPath
25
- * @param {string} fileName - name of the ignore file
26
- * @returns {string[]} parsed lines
27
- */
28
- const parseIgnoreFile = (
29
- fullPath,
30
- currentDirectory,
31
- fileName,
32
- ) => {
33
- const filePath = path.join(
34
- fullPath,
35
- currentDirectory,
36
- fileName,
37
- );
38
- const content = fs.readFileSync(filePath, 'utf8');
39
- const globs = content
40
- .split(/[\n\r]+/)
41
- .filter(
42
- // remove blank line and comments
43
- (line) => line.length > 0 && !line.startsWith('#'),
44
- )
45
- .map((line) => {
46
- const { bang, slash, glob } = line.match(
47
- /^(?<bang>!?)(?<slash>\/?)(?<glob>.*)$/,
48
- ).groups;
49
- const hasSlash =
50
- Boolean(slash) || /\/.*\S/.test(glob);
51
- const relativeGlob = path.posix.join(
52
- currentDirectory.replace(
53
- // escape characters with special meaning in glob expressions
54
- /[*?!# \[\]\\]/g,
55
- (char) => '\\' + char,
56
- ),
57
- // a pattern that doesn't include a slash (not counting a trailing one) matches files in any descendant directory od the current one
58
- hasSlash ? '' : '**',
59
- glob,
60
- );
61
- // preserve leading `!` and `/` characters
62
- return bang + slash + relativeGlob;
63
- });
64
- if (debugIgnore) {
65
- console.debug(
66
- 'at',
67
- currentDirectory,
68
- 'parsing',
69
- fileName,
70
- 'obtained globs',
71
- globs,
72
- );
73
- }
74
- return globs;
75
- };
76
-
77
- // Lists files from the file system,
78
- // converts them into the VZCode internal
79
- // ShareDB-compatible data structure.
80
- export const computeInitialDocument = ({ fullPath }) => {
81
- // Isolate files, not directories.
82
- // Inspired by https://stackoverflow.com/questions/41472161/fs-readdir-ignore-directories
83
-
84
- // Initialize the document using our data structure for representing files.
85
- const initialDocument = {
86
- // `files`
87
- // * Keys are file ids, which are random numbers.
88
- // * Values are objects with properties:
89
- // * text - the text content of the file
90
- // * name - the file name
91
- files: {},
92
-
93
- // `isInteracting`
94
- // * Whether the user is currently interacting with the document
95
- // using an interactive code widget such as number dragger.
96
- // * When true, the auto-save to the file system is changed to be
97
- // more frequent (throttled not debounced).
98
- // isInteracting: false,
99
- // This is `undefined` initially.
100
- };
101
-
102
- /**
103
- * Stack for recursively traversing directories.
104
- * @type {string[]}
105
- */
106
- let files = [];
107
-
108
- const ignoreFileMatcher = createIgnore().add(
109
- ignoreFilePattern,
110
- );
111
- const isIgnoreFile = (fileName) =>
112
- ignoreFileMatcher.ignores(fileName);
113
-
114
- const unsearchedDirectories = [
115
- {
116
- currentDirectory: '.',
117
- ignore: createIgnore().add(baseIgnore),
118
- },
119
- ];
120
-
121
- while (unsearchedDirectories.length !== 0) {
122
- const { currentDirectory, ignore: parentIgnore } =
123
- unsearchedDirectories.pop();
124
- const currentDirectoryPath = path.join(
125
- fullPath,
126
- currentDirectory,
127
- );
128
- const dirEntries = fs
129
- .readdirSync(currentDirectoryPath, {
130
- withFileTypes: true,
131
- })
132
- .filter((dirent) =>
133
- enableDirectories ? true : dirent.isFile(),
134
- );
135
- // find .ignore or .gitignore files in the current directory
136
- const ignoreFiles = dirEntries
137
- .filter(
138
- (dirent) =>
139
- dirent.isFile() && isIgnoreFile(dirent.name),
140
- )
141
- .map((file) => file.name);
142
- let ignore = parentIgnore;
143
- if (ignoreFiles.length > 0) {
144
- const globs = ignoreFiles.flatMap((fileName) =>
145
- parseIgnoreFile(
146
- fullPath,
147
- currentDirectory,
148
- fileName,
149
- ),
150
- );
151
- ignore = createIgnore().add(parentIgnore).add(globs);
152
- }
153
- const newFiles = dirEntries
154
- .filter((dirent) => {
155
- const relativePath =
156
- path.posix.join(currentDirectory, dirent.name) +
157
- (dirent.isDirectory() ? '/' : '');
158
- const keep = !ignore.ignores(relativePath);
159
- if (debugIgnore && !keep) {
160
- console.debug(
161
- 'at',
162
- currentDirectory,
163
- 'ignoring',
164
- relativePath,
165
- );
166
- }
167
- return keep;
168
- })
169
- // Add a trailing slash for directories
170
- .map((dirent) => {
171
- const relativePath = path.posix.join(
172
- currentDirectory,
173
- dirent.name,
174
- );
175
-
176
- if (!dirent.isDirectory()) {
177
- return relativePath;
178
- }
179
- unsearchedDirectories.push({
180
- currentDirectory: relativePath,
181
- ignore,
182
- });
183
- return relativePath + '/';
184
- });
185
- // console.log(currentDirectory);
186
-
187
- files.push(...newFiles);
188
- }
189
-
190
- files.forEach((file) => {
191
- const id = randomId();
192
- let text = null;
193
-
194
- if (!isDirectory(file)) {
195
- const filePath = path.join(fullPath, file);
196
-
197
- if (isImageFile(file)) {
198
- // Read image files as binary and convert to base64
199
- const buffer = fs.readFileSync(filePath);
200
- text = buffer.toString('base64');
201
- } else {
202
- // Read non-image files as UTF-8 text
203
- text = fs.readFileSync(filePath, 'utf-8');
204
- }
205
- }
206
-
207
- initialDocument.files[id] = {
208
- text,
209
- name: file,
210
- };
211
- });
212
-
213
- if (debugDirectories) {
214
- console.log('files:');
215
- console.log(files);
216
- console.log('initialDocument:');
217
- console.log(JSON.stringify(initialDocument, null, 2));
218
- }
219
- return initialDocument;
220
- };
@@ -1,188 +0,0 @@
1
- import OpenAI from 'openai';
2
- import { json1Presence } from '../ot.js';
3
-
4
- // This module implements the generation of the AI response,
5
- // using the OpenAI client.
6
-
7
- // These environment variables are used to configure the OpenAI client.
8
- // See `.env.sample` for the expected values.
9
- const { VZCODE_AI_API_KEY, VZCODE_AI_BASE_URL } =
10
- process.env;
11
-
12
- // If neither of these are not set, the OpenAI client will not be initialized,
13
- // e.g. for local development where no AI is needed.
14
- const isAIEnabled =
15
- VZCODE_AI_API_KEY !== undefined &&
16
- VZCODE_AI_BASE_URL !== undefined;
17
-
18
- const { editOp, type } = json1Presence;
19
-
20
- // Debug flag to log more information during development.
21
- const debug = false;
22
-
23
- // Feature flag to slow down AI for development/testing.
24
- // Particularly relevant for debugging ShareDB and CodeMirror sync issues.
25
- // This allows you to test the case of editing the document at the
26
- // same time as the AI generation is happening.
27
- const slowdown = false;
28
-
29
- // The options passed into the OpenAI client
30
- // new OpenAI(openAIOptions)
31
- const openAIOptions: { apiKey?: string; baseURL?: string } =
32
- {};
33
-
34
- // Support specifying the API key via an environment variable
35
- // If VZCODE_AI_API_KEY is not set, note that the OpenAI client
36
- // will look for the OPENAI_API_KEY environment variable instead.
37
- if (process.env.VZCODE_AI_API_KEY !== undefined) {
38
- openAIOptions.apiKey = process.env.VZCODE_AI_API_KEY;
39
- }
40
-
41
- // Support for local AI server
42
- if (process.env.VZCODE_AI_BASE_URL !== undefined) {
43
- // The OpenAI client errors if the API key is not set,
44
- // so in the case where we don't need an API key,
45
- // e.g. for testing with a local AI server like LM Studio or LocalAI,
46
- // we populate the option with a fake API key, so it doesn't error.
47
- if (!openAIOptions.apiKey) {
48
- openAIOptions.apiKey = 'Fake API Key';
49
- }
50
-
51
- openAIOptions.baseURL = process.env.VZCODE_AI_BASE_URL;
52
- }
53
-
54
- debug &&
55
- console.log(
56
- 'openAIOptions: ' +
57
- JSON.stringify(openAIOptions, null, 2),
58
- );
59
- let openai;
60
-
61
- if (isAIEnabled) {
62
- openai = new OpenAI(openAIOptions);
63
- }
64
-
65
- // The name of the source that the AI responses
66
- // will be attributed to in ShareDB operations.
67
- const AIShareDBSourceName = 'AIAssist';
68
-
69
- // Returns trie if the operation comes from AI.
70
- const opComesFromAIAssist = (ops, source) =>
71
- source === AIShareDBSourceName;
72
-
73
- // Keeps track of the currently ongoing AI streams.
74
- // There could be many streams at the same time.
75
- export const streams: Record<string, any> = {};
76
-
77
- export const generateAIResponse = async ({
78
- inputText,
79
- insertionCursor,
80
- fileId,
81
- streamId,
82
- shareDBDoc,
83
- }) => {
84
- if (!isAIEnabled) {
85
- console.log(
86
- '[generateAIResponse] AI is not enabled. Skipping AI generation.',
87
- );
88
- console.log(
89
- '[generateAIResponse] To enable AI, see .env.sample for the required environment variables.',
90
- );
91
- return;
92
- }
93
-
94
- if (debug) {
95
- console.log(
96
- '[generateAIResponse] inputText:',
97
- inputText,
98
- );
99
- console.log(
100
- '[generateAIResponse] insertionCursor:',
101
- insertionCursor,
102
- );
103
- console.log('[generateAIResponse] fileId:', fileId);
104
- console.log('[generateAIResponse] streamId:', streamId);
105
- console.log(
106
- '[generateAIResponse] shareDBDoc:',
107
- shareDBDoc,
108
- );
109
- }
110
-
111
- // Handle the case that a user edits the text in the document
112
- // that comes becofore the insertion cursor.
113
- const accomodateDocChanges = (op, source) => {
114
- if (!opComesFromAIAssist(op, source)) {
115
- if (op !== null) {
116
- insertionCursor = type
117
- .transformPosition(
118
- ['files', fileId, 'text', insertionCursor],
119
- op,
120
- )
121
- .slice(-1)[0];
122
- }
123
- }
124
- };
125
- shareDBDoc.on('op', accomodateDocChanges);
126
-
127
- // The prompt!
128
- const messages = [
129
- {
130
- role: 'system',
131
- content: [
132
- 'You are an expert programmer.',
133
- 'Your task is to output ONLY the code that replaces <FILL_ME> correctly.',
134
- 'Do not add any markdown around.',
135
- 'Do not duplicate the code before or after <FILL_ME>.',
136
- 'Do not make any changes outside of <FILL_ME>.',
137
- 'Do not enclose the output with backticks.',
138
- 'If any additional instructions are required, they will be provided in comments.',
139
- ].join(' '),
140
- },
141
- { role: 'user', content: inputText },
142
- ];
143
-
144
- if (debug) {
145
- console.log('[generateAIResponse] messages:');
146
- console.log(JSON.stringify(messages, null, 2));
147
- }
148
-
149
- streams[streamId] = await openai.chat.completions.create({
150
- // model: 'gpt-3.5-turbo',
151
- model: 'gpt-4o',
152
- messages,
153
- reasoning: {
154
- effort: 'medium',
155
- exclude: false,
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
160
- stream: true,
161
- });
162
-
163
- for await (const part of streams[streamId]) {
164
- const op = editOp(
165
- ['files', fileId, 'text'],
166
- 'text-unicode',
167
- [
168
- insertionCursor,
169
- part.choices[0]?.delta?.content || '',
170
- ],
171
- );
172
-
173
- shareDBDoc.submitOp(op, {
174
- source: AIShareDBSourceName,
175
- });
176
-
177
- if (slowdown) {
178
- await new Promise((resolve) => {
179
- setTimeout(resolve, 2000);
180
- });
181
- }
182
-
183
- insertionCursor += (
184
- part.choices[0]?.delta?.content || ''
185
- ).length;
186
- }
187
- shareDBDoc.off('op', accomodateDocChanges);
188
- };
@@ -1,53 +0,0 @@
1
- import {
2
- generateAIResponse,
3
- streams,
4
- } from './generateAIResponse.js';
5
-
6
- const debug = false;
7
-
8
- export const handleAIAssist =
9
- (shareDBDoc: any) => async (req: any, res: any) => {
10
- const { inputText, insertionCursor, fileId } = req.body;
11
-
12
- if (debug) {
13
- console.log('[handleAIAssist] inputText:', inputText);
14
- console.log(
15
- '[handleAIAssist] insertionCursor:',
16
- insertionCursor,
17
- );
18
- console.log('[handleAIAssist] fileId:', fileId);
19
- }
20
-
21
- try {
22
- // Generate a unique streamId for this request
23
- const streamId = `stream_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
24
-
25
- await generateAIResponse({
26
- inputText,
27
- insertionCursor,
28
- fileId,
29
- streamId,
30
- shareDBDoc,
31
- });
32
-
33
- res
34
- .status(200)
35
- .send({ message: 'Operation successful!' });
36
- } catch (error: any) {
37
- console.error('handleAIAssist error:', error);
38
- res.status(500).send({
39
- message: 'Internal Server Error',
40
- error: error.message,
41
- });
42
- }
43
- };
44
-
45
- export function haltGeneration(streamId: string) {
46
- const stream = streams[streamId];
47
-
48
- // Stream can be undefined here if the user
49
- // clicks start and stop very quickly.
50
- if (stream) {
51
- stream.controller.abort();
52
- }
53
- }
@@ -1,2 +0,0 @@
1
- // Re-export the handleAIChatUndo from the new modular structure
2
- export { handleAIChatUndo } from './aiChatHandler/undoHandler.js';
@@ -1,97 +0,0 @@
1
- import { ChatOpenAI } from '@langchain/openai';
2
- import { StringOutputParser } from '@langchain/core/output_parsers';
3
-
4
- // This module implements the generation of the copilot response.
5
-
6
- // Debug flag to log more information during development.
7
- const debug = false;
8
-
9
- export const handleAICopilot = () => {
10
- // These environment variables are used to configure the OpenAI client.
11
- // See `.env.sample` for the expected values.
12
- const {
13
- VZCODE_AI_COPILOT_API_KEY,
14
- VZCODE_AI_COPILOT_BASE_URL,
15
- VZCODE_AI_COPILOT_MODEL,
16
- } = process.env;
17
-
18
- // If neither of these are not set, the OpenAI client will not be initialized,
19
- // e.g. for local development where no AI is needed.
20
- const isCopilotEnabled =
21
- VZCODE_AI_COPILOT_API_KEY !== undefined &&
22
- VZCODE_AI_COPILOT_BASE_URL !== undefined &&
23
- VZCODE_AI_COPILOT_MODEL !== undefined;
24
-
25
- debug &&
26
- console.log('isCopilotEnabled: ' + isCopilotEnabled);
27
-
28
- debug &&
29
- console.log({
30
- VZCODE_AI_COPILOT_API_KEY,
31
- VZCODE_AI_COPILOT_BASE_URL,
32
- VZCODE_AI_COPILOT_MODEL,
33
- });
34
-
35
- let chatModel;
36
- if (isCopilotEnabled) {
37
- const options = {
38
- modelName: VZCODE_AI_COPILOT_MODEL,
39
- configuration: {
40
- apiKey: VZCODE_AI_COPILOT_API_KEY,
41
- baseURL: VZCODE_AI_COPILOT_BASE_URL,
42
- },
43
- additionalParameters: {
44
- provider: { sort: 'throughput' },
45
- },
46
- streaming: false,
47
- };
48
- debug && console.log('chatModel options:', options);
49
- chatModel = new ChatOpenAI(options);
50
- }
51
-
52
- return async (req, res) => {
53
- // Don't break if the AI is not enabled.
54
- // This is useful for local development.
55
- if (!isCopilotEnabled) {
56
- res.status(200).send({ text: '' });
57
- return;
58
- }
59
- const { prefix, suffix } = req.body;
60
-
61
- const messages = [
62
- {
63
- role: 'user',
64
- content: [
65
- 'Complete the missing part of the text below:\n\n',
66
- '\`\`\`\n',
67
- prefix,
68
- '<MISSING_PART>',
69
- suffix,
70
- '\`\`\`\n\n',
71
- 'ONLY output the text that replaces <MISSING_PART>.\n\n',
72
- 'NEVER include \`\`\`javascript or similar code blocks.\n\n',
73
- ].join(''),
74
- },
75
- ];
76
- if (debug) {
77
- console.log(
78
- '[generateAIResponse] prompt:\n```\n' +
79
- messages[0].content +
80
- '\n```\n',
81
- );
82
- }
83
- try {
84
- const result = await chatModel.invoke(messages);
85
- const parser = new StringOutputParser();
86
- const text = await parser.invoke(result);
87
-
88
- res.status(200).send({ text });
89
- } catch (error) {
90
- console.error('handleAICopilot error:', error);
91
- res.status(500).send({
92
- message: 'Internal Server Error',
93
- error: error.message,
94
- });
95
- }
96
- };
97
- };