vzcode 2.21.0 → 2.25.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/README.md +359 -0
- package/dist/assets/{buildWorker-Bi6wsfQk.js → buildWorker-DylWm2VX.js} +64 -72
- package/dist/assets/index-CkFweu5-.js +403 -0
- package/dist/assets/{index-fYq6iaqF.css → index-Dyt9tpmu.css} +1 -5
- package/dist/assets/{worker-BSzbM0Uo.js → worker-VPLhQVba.js} +230 -230
- package/dist/assets/worker-b9bkZkXK.js +116 -0
- package/dist/assets/{worker-Cm3I3tnR.js → worker-k7HZ3t-V.js} +3 -20
- package/dist/index.html +2 -2
- package/dist/{server/aiChatHandler → llm-streaming-server}/aiEditing.js +1 -1
- package/dist/{server/aiChatHandler → llm-streaming-server}/chatOperations.js +21 -2
- package/dist/{server/aiChatHandler → llm-streaming-server}/errorHandling.js +1 -1
- package/dist/llm-streaming-server/index.js +20 -0
- package/dist/{server/aiChatHandler → llm-streaming-server}/llmStreaming.js +19 -14
- package/dist/server/aiChatHandler/index.js +9 -7
- package/package.json +41 -40
- package/src/client/CodeEditor/getOrCreateEditor.tsx +20 -13
- package/src/client/CodeEditor/index.tsx +3 -3
- package/src/client/VZSidebar/FileTypeIcon.tsx +1 -0
- package/src/client/VZSidebar/index.tsx +1 -1
- package/src/llm-streaming-server/README.md +71 -0
- package/src/{server/aiChatHandler → llm-streaming-server}/aiEditing.ts +1 -1
- package/src/{server/aiChatHandler → llm-streaming-server}/chatOperations.ts +30 -3
- package/src/{server/aiChatHandler → llm-streaming-server}/errorHandling.ts +1 -1
- package/src/llm-streaming-server/index.ts +51 -0
- package/src/{server/aiChatHandler → llm-streaming-server}/llmStreaming.ts +26 -16
- package/src/llm-streaming-ui/README.md +103 -0
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/ChatInput.tsx +3 -3
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/DiffView.tsx +3 -3
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/FileEditingIndicator.tsx +1 -1
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/IndividualFileDiff.tsx +1 -1
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/Message.tsx +12 -11
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/MessageList.tsx +7 -2
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/index.tsx +5 -5
- package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/styles.scss +48 -0
- package/src/llm-streaming-ui/index.ts +34 -0
- package/src/server/aiChatHandler/index.ts +11 -5
- package/src/types.ts +1 -0
- package/dist/assets/index-D6-he0oi.js +0 -476
- package/dist/assets/worker-KiuLM-X4.js +0 -136
- /package/dist/{server/aiChatHandler → llm-streaming-server}/validation.js +0 -0
- /package/src/{server/aiChatHandler → llm-streaming-server}/validation.ts +0 -0
- /package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/DiffView.scss +0 -0
- /package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/JumpToLatestButton.tsx +0 -0
- /package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/ThinkingScratchpad.tsx +0 -0
- /package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/TypingIndicator.tsx +0 -0
- /package/src/{client/VZSidebar/AIChat → llm-streaming-ui/components}/useSpeechRecognition.ts +0 -0
package/dist/index.html
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
|
|
21
21
|
rel="stylesheet"
|
|
22
22
|
/>
|
|
23
|
-
<script type="module" crossorigin src="/assets/index-
|
|
24
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-CkFweu5-.js"></script>
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Dyt9tpmu.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
|
27
27
|
<div id="root"></div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assembleFullPrompt, prepareFilesForPrompt, } from 'editcodewithai';
|
|
2
2
|
import { formatMarkdownFiles } from 'llm-code-format';
|
|
3
|
-
import { createFilesSnapshot, generateFilesUnifiedDiff, } from '
|
|
3
|
+
import { createFilesSnapshot, generateFilesUnifiedDiff, } from '../utils/fileDiff.js';
|
|
4
4
|
// Dev flag for waiting 1 second before starting the LLM function.
|
|
5
5
|
// Useful for debugging and testing purposes, e.g. checking the typing indicator.
|
|
6
6
|
const delayStart = false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dateToTimestamp } from '@vizhub/viz-utils';
|
|
2
|
-
import { randomId } from '
|
|
3
|
-
import { diff } from '
|
|
2
|
+
import { randomId } from '../randomId.js';
|
|
3
|
+
import { diff } from '../ot.js';
|
|
4
4
|
/**
|
|
5
5
|
* Ensures the chats object exists in the ShareDB document
|
|
6
6
|
*/
|
|
@@ -402,6 +402,25 @@ export const updateStreamingStatus = (shareDBDoc, chatId, status, isStreaming =
|
|
|
402
402
|
});
|
|
403
403
|
shareDBDoc.submitOp(op);
|
|
404
404
|
};
|
|
405
|
+
/**
|
|
406
|
+
* Sets the model for a chat when AI generation starts
|
|
407
|
+
*/
|
|
408
|
+
export const setChatModel = (shareDBDoc, chatId, model) => {
|
|
409
|
+
DEBUG &&
|
|
410
|
+
console.log(`ChatOperations: Setting model: "${model}" for chatId: ${chatId}`);
|
|
411
|
+
const op = diff(shareDBDoc.data, {
|
|
412
|
+
...shareDBDoc.data,
|
|
413
|
+
chats: {
|
|
414
|
+
...shareDBDoc.data.chats,
|
|
415
|
+
[chatId]: {
|
|
416
|
+
...shareDBDoc.data.chats[chatId],
|
|
417
|
+
model: model,
|
|
418
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
shareDBDoc.submitOp(op);
|
|
423
|
+
};
|
|
405
424
|
/**
|
|
406
425
|
* Finalizes streaming message and clears streaming state
|
|
407
426
|
*/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vizhub/llm-streaming-server
|
|
3
|
+
*
|
|
4
|
+
* Server-side library for streaming LLM responses with ShareDB integration.
|
|
5
|
+
* This module provides functionality for:
|
|
6
|
+
* - Creating and managing LLM streaming functions
|
|
7
|
+
* - Performing AI-assisted code editing
|
|
8
|
+
* - Managing chat operations with ShareDB
|
|
9
|
+
* - Validating requests and handling errors
|
|
10
|
+
*/
|
|
11
|
+
// Core LLM streaming functionality
|
|
12
|
+
export { createLLMFunction } from './llmStreaming.js';
|
|
13
|
+
// AI editing operations
|
|
14
|
+
export { performAIChat, performAIEditing, } from './aiEditing.js';
|
|
15
|
+
// ShareDB chat operations
|
|
16
|
+
export { ensureChatsExist, ensureChatExists, addUserMessage, updateAIStatus, updateAIScratchpad, clearAIScratchpadAndStatus, createAIMessage, updateAIMessageContent, setAIStatus, finalizeAIMessage, addDiffToAIMessage, addAIMessage, updateFiles, resolveFileId, createNewFile, createStreamingAIMessage, addStreamingEvent, updateStreamingStatus, finalizeStreamingMessage, } from './chatOperations.js';
|
|
17
|
+
// Request validation
|
|
18
|
+
export { validateRequest } from './validation.js';
|
|
19
|
+
// Error handling
|
|
20
|
+
export { handleError, handleBackgroundError, } from './errorHandling.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
2
|
import { parseMarkdownFiles, StreamingMarkdownParser, } from 'llm-code-format';
|
|
3
3
|
import { mergeFileChanges } from 'editcodewithai';
|
|
4
|
-
import { updateFiles, updateAIScratchpad, createStreamingAIMessage, addStreamingEvent, updateStreamingStatus, finalizeStreamingMessage, } from './chatOperations.js';
|
|
5
|
-
import { formatFiles } from '../prettier.js';
|
|
4
|
+
import { updateFiles, updateAIScratchpad, createStreamingAIMessage, addStreamingEvent, updateStreamingStatus, finalizeStreamingMessage, setChatModel, } from './chatOperations.js';
|
|
5
|
+
import { formatFiles } from '../server/prettier.js';
|
|
6
6
|
// Verbose logs
|
|
7
7
|
const DEBUG = false;
|
|
8
8
|
// Useful for testing/debugging the streaming behavior
|
|
@@ -22,8 +22,12 @@ export const createLLMFunction = ({ shareDBDoc, chatId,
|
|
|
22
22
|
enableReasoningTokens = false, model, aiRequestOptions, }) => {
|
|
23
23
|
return async (fullPrompt) => {
|
|
24
24
|
// Create OpenRouter client for reasoning token support
|
|
25
|
+
const apiKey = process.env.VZCODE_EDIT_WITH_AI_API_KEY;
|
|
26
|
+
if (!apiKey) {
|
|
27
|
+
console.warn('[LLMStreaming] OpenRouter API Key (VZCODE_EDIT_WITH_AI_API_KEY) not found');
|
|
28
|
+
}
|
|
25
29
|
const openRouterClient = new OpenAI({
|
|
26
|
-
apiKey:
|
|
30
|
+
apiKey: apiKey,
|
|
27
31
|
baseURL: process.env.VZCODE_EDIT_WITH_AI_BASE_URL ||
|
|
28
32
|
'https://openrouter.ai/api/v1',
|
|
29
33
|
defaultHeaders: {
|
|
@@ -36,8 +40,14 @@ enableReasoningTokens = false, model, aiRequestOptions, }) => {
|
|
|
36
40
|
let currentEditingFileName = null;
|
|
37
41
|
let accumulatedTextChunk = '';
|
|
38
42
|
let currentFileContent = '';
|
|
43
|
+
// Stream the response with reasoning tokens
|
|
44
|
+
const modelName = model ||
|
|
45
|
+
process.env.VZCODE_EDIT_WITH_AI_MODEL_NAME ||
|
|
46
|
+
'anthropic/claude-haiku-4.5';
|
|
39
47
|
// Create streaming AI message
|
|
40
48
|
createStreamingAIMessage(shareDBDoc, chatId);
|
|
49
|
+
// Set the model being used for this chat
|
|
50
|
+
setChatModel(shareDBDoc, chatId, modelName);
|
|
41
51
|
// Set initial content generation status
|
|
42
52
|
updateStreamingStatus(shareDBDoc, chatId, 'Formulating a plan...');
|
|
43
53
|
// Helper to get original file content
|
|
@@ -145,25 +155,20 @@ enableReasoningTokens = false, model, aiRequestOptions, }) => {
|
|
|
145
155
|
const parser = new StreamingMarkdownParser(callbacks);
|
|
146
156
|
const chunks = [];
|
|
147
157
|
let reasoningContent = '';
|
|
148
|
-
// Stream the response with reasoning tokens
|
|
149
|
-
const modelName = model ||
|
|
150
|
-
process.env.VZCODE_EDIT_WITH_AI_MODEL_NAME ||
|
|
151
|
-
'anthropic/claude-3.5-sonnet';
|
|
152
158
|
// Configure reasoning tokens based on enableReasoningTokens flag
|
|
153
159
|
const requestConfig = {
|
|
154
160
|
model: modelName,
|
|
155
161
|
messages: [{ role: 'user', content: fullPrompt }],
|
|
156
|
-
usage: { include: true },
|
|
157
162
|
stream: true,
|
|
158
163
|
...aiRequestOptions,
|
|
159
164
|
};
|
|
160
165
|
// Only include reasoning configuration if reasoning tokens are enabled
|
|
161
|
-
if (enableReasoningTokens) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
166
|
+
// if (enableReasoningTokens) {
|
|
167
|
+
// requestConfig.reasoning = {
|
|
168
|
+
// effort: 'low',
|
|
169
|
+
// exclude: false,
|
|
170
|
+
// };
|
|
171
|
+
// }
|
|
167
172
|
const stream = await openRouterClient.chat.completions.create(requestConfig);
|
|
168
173
|
let reasoningStarted = false;
|
|
169
174
|
let contentStarted = false;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { validateRequest } from '
|
|
2
|
-
import { ensureChatsExist, ensureChatExists, addUserMessage, setAIStatus, } from '
|
|
3
|
-
import { createLLMFunction } from '
|
|
4
|
-
import { performAIEditing } from '
|
|
5
|
-
import { handleError, handleBackgroundError, } from '
|
|
1
|
+
import { validateRequest } from '../../llm-streaming-server/validation.js';
|
|
2
|
+
import { ensureChatsExist, ensureChatExists, addUserMessage, setAIStatus, } from '../../llm-streaming-server/chatOperations.js';
|
|
3
|
+
import { createLLMFunction } from '../../llm-streaming-server/llmStreaming.js';
|
|
4
|
+
import { performAIEditing } from '../../llm-streaming-server/aiEditing.js';
|
|
5
|
+
import { handleError, handleBackgroundError, } from '../../llm-streaming-server/errorHandling.js';
|
|
6
6
|
import { createRunCodeFunction } from '../../runCode.js';
|
|
7
7
|
import { createSubmitOperation } from '../../submitOperation.js';
|
|
8
8
|
import { getGenerationMetadata } from 'editcodewithai';
|
|
9
9
|
const DEBUG = false;
|
|
10
|
-
export const handleAIChatMessage = ({ shareDBDoc, onCreditDeduction, model, aiRequestOptions, }) => async (req, res) => {
|
|
10
|
+
export const handleAIChatMessage = ({ shareDBDoc, onCreditDeduction, model, aiRequestOptions, enableReasoningTokens, }) => async (req, res) => {
|
|
11
11
|
const { content, chatId } = req.body;
|
|
12
12
|
if (DEBUG) {
|
|
13
13
|
console.log('[handleAIChatMessage] content:', content, 'chatId:', chatId, 'shareDBDoc:', shareDBDoc);
|
|
@@ -31,6 +31,7 @@ export const handleAIChatMessage = ({ shareDBDoc, onCreditDeduction, model, aiRe
|
|
|
31
31
|
content,
|
|
32
32
|
model,
|
|
33
33
|
aiRequestOptions,
|
|
34
|
+
enableReasoningTokens,
|
|
34
35
|
onCreditDeduction,
|
|
35
36
|
}).catch((error) => {
|
|
36
37
|
console.error('Background AI processing error:', error);
|
|
@@ -45,12 +46,13 @@ export const handleAIChatMessage = ({ shareDBDoc, onCreditDeduction, model, aiRe
|
|
|
45
46
|
/**
|
|
46
47
|
* Processes the AI request asynchronously in the background
|
|
47
48
|
*/
|
|
48
|
-
const processAIRequestAsync = async ({ shareDBDoc, chatId, content, model, aiRequestOptions, onCreditDeduction, }) => {
|
|
49
|
+
const processAIRequestAsync = async ({ shareDBDoc, chatId, content, model, aiRequestOptions, enableReasoningTokens, onCreditDeduction, }) => {
|
|
49
50
|
try {
|
|
50
51
|
// Create LLM function for streaming
|
|
51
52
|
const llmFunction = createLLMFunction({
|
|
52
53
|
shareDBDoc,
|
|
53
54
|
chatId,
|
|
55
|
+
enableReasoningTokens: enableReasoningTokens ?? true,
|
|
54
56
|
model,
|
|
55
57
|
aiRequestOptions,
|
|
56
58
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.25.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
13
|
"test": "vitest run",
|
|
14
|
-
"test-interactive": "cd test/sampleDirectories/playground &&
|
|
14
|
+
"test-interactive": "cd test/sampleDirectories/playground && tsx ../../../src/server/index.ts",
|
|
15
15
|
"prettier": "prettier {*.*,**/*.*} --write",
|
|
16
16
|
"typecheck": "tsc --noEmit",
|
|
17
17
|
"lint": "eslint . --ext .js,.ts,.tsx",
|
|
@@ -110,35 +110,35 @@
|
|
|
110
110
|
},
|
|
111
111
|
"homepage": "https://github.com/vizhub-core/vzcode#readme",
|
|
112
112
|
"dependencies": {
|
|
113
|
-
"@codemirror/autocomplete": "^6.
|
|
113
|
+
"@codemirror/autocomplete": "^6.20.0",
|
|
114
114
|
"@codemirror/lang-css": "^6.3.1",
|
|
115
115
|
"@codemirror/lang-html": "^6.4.11",
|
|
116
116
|
"@codemirror/lang-javascript": "^6.2.4",
|
|
117
117
|
"@codemirror/lang-json": "^6.0.2",
|
|
118
|
-
"@codemirror/lang-markdown": "^6.
|
|
119
|
-
"@codemirror/lint": "^6.9.
|
|
118
|
+
"@codemirror/lang-markdown": "^6.5.0",
|
|
119
|
+
"@codemirror/lint": "^6.9.2",
|
|
120
120
|
"@codemirror/state": "^6.5.2",
|
|
121
121
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
122
|
-
"@codemirror/view": "^6.38.
|
|
123
|
-
"@langchain/core": "^0.
|
|
124
|
-
"@langchain/openai": "^
|
|
125
|
-
"@lezer/highlight": "^1.2.
|
|
126
|
-
"@livekit/components-react": "^2.9.
|
|
122
|
+
"@codemirror/view": "^6.38.8",
|
|
123
|
+
"@langchain/core": "^1.0.6",
|
|
124
|
+
"@langchain/openai": "^1.1.2",
|
|
125
|
+
"@lezer/highlight": "^1.2.3",
|
|
126
|
+
"@livekit/components-react": "^2.9.16",
|
|
127
127
|
"@replit/codemirror-indentation-markers": "^6.5.3",
|
|
128
128
|
"@replit/codemirror-interact": "^6.3.1",
|
|
129
129
|
"@replit/codemirror-lang-svelte": "^6.0.0",
|
|
130
130
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
131
131
|
"@teamwork/websocket-json-stream": "^2.0.0",
|
|
132
|
-
"@typescript/vfs": "^1.6.
|
|
133
|
-
"@uiw/codemirror-theme-abcdef": "^4.25.
|
|
134
|
-
"@uiw/codemirror-theme-dracula": "^4.25.
|
|
135
|
-
"@uiw/codemirror-theme-eclipse": "^4.25.
|
|
136
|
-
"@uiw/codemirror-theme-github": "^4.25.
|
|
137
|
-
"@uiw/codemirror-theme-material": "^4.25.
|
|
138
|
-
"@uiw/codemirror-theme-nord": "^4.25.
|
|
139
|
-
"@uiw/codemirror-theme-okaidia": "^4.25.
|
|
140
|
-
"@uiw/codemirror-theme-xcode": "^4.25.
|
|
141
|
-
"@uiw/codemirror-themes": "^4.25.
|
|
132
|
+
"@typescript/vfs": "^1.6.2",
|
|
133
|
+
"@uiw/codemirror-theme-abcdef": "^4.25.3",
|
|
134
|
+
"@uiw/codemirror-theme-dracula": "^4.25.3",
|
|
135
|
+
"@uiw/codemirror-theme-eclipse": "^4.25.3",
|
|
136
|
+
"@uiw/codemirror-theme-github": "^4.25.3",
|
|
137
|
+
"@uiw/codemirror-theme-material": "^4.25.3",
|
|
138
|
+
"@uiw/codemirror-theme-nord": "^4.25.3",
|
|
139
|
+
"@uiw/codemirror-theme-okaidia": "^4.25.3",
|
|
140
|
+
"@uiw/codemirror-theme-xcode": "^4.25.3",
|
|
141
|
+
"@uiw/codemirror-themes": "^4.25.3",
|
|
142
142
|
"@valtown/codemirror-ts": "^2.3.1",
|
|
143
143
|
"@vizhub/runtime": "^4.5.0",
|
|
144
144
|
"@vizhub/viz-types": "^0.5.0",
|
|
@@ -157,22 +157,22 @@
|
|
|
157
157
|
"diff2html": "^3.4.52",
|
|
158
158
|
"dotenv": "^17.2.3",
|
|
159
159
|
"editcodewithai": "^2.4.0",
|
|
160
|
-
"eslint-linter-browserify": "^9.
|
|
160
|
+
"eslint-linter-browserify": "^9.39.1",
|
|
161
161
|
"express": "^5.1.0",
|
|
162
162
|
"ignore": "^7.0.5",
|
|
163
163
|
"json0-ot-diff": "^1.1.2",
|
|
164
164
|
"jszip": "^3.10.1",
|
|
165
|
-
"livekit-server-sdk": "^2.14.
|
|
165
|
+
"livekit-server-sdk": "^2.14.2",
|
|
166
166
|
"llm-code-format": "^3.1.0",
|
|
167
|
-
"lucide-react": "^0.
|
|
168
|
-
"npm": "^11.6.
|
|
169
|
-
"open": "^
|
|
167
|
+
"lucide-react": "^0.554.0",
|
|
168
|
+
"npm": "^11.6.3",
|
|
169
|
+
"open": "^11.0.0",
|
|
170
170
|
"prettier-plugin-svelte": "^3.4.0",
|
|
171
171
|
"react": "^18",
|
|
172
172
|
"react-bootstrap": "^2.10.10",
|
|
173
173
|
"react-dom": "^18",
|
|
174
174
|
"react-markdown": "^10.1.0",
|
|
175
|
-
"react-router-dom": "^7.9.
|
|
175
|
+
"react-router-dom": "^7.9.6",
|
|
176
176
|
"remark-gfm": "^4.0.1",
|
|
177
177
|
"sharedb": "^5.2.2",
|
|
178
178
|
"sharedb-client-browser": "^5.2.2",
|
|
@@ -181,31 +181,32 @@
|
|
|
181
181
|
"ws": "^8.18.3"
|
|
182
182
|
},
|
|
183
183
|
"devDependencies": {
|
|
184
|
-
"@eslint/js": "^9.
|
|
185
|
-
"@tauri-apps/cli": "^2.
|
|
184
|
+
"@eslint/js": "^9.39.1",
|
|
185
|
+
"@tauri-apps/cli": "^2.9.4",
|
|
186
186
|
"@types/d3-color": "^3.1.3",
|
|
187
187
|
"@types/react": "^18",
|
|
188
188
|
"@types/react-dom": "^18",
|
|
189
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
190
|
-
"@typescript-eslint/parser": "^8.
|
|
191
|
-
"@vitejs/plugin-react": "^5.
|
|
189
|
+
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
|
190
|
+
"@typescript-eslint/parser": "^8.47.0",
|
|
191
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
192
192
|
"concurrently": "^9.2.1",
|
|
193
193
|
"cross-env": "^10.1.0",
|
|
194
|
-
"eslint": "^9.
|
|
194
|
+
"eslint": "^9.39.1",
|
|
195
195
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
196
196
|
"eslint-plugin-react": "^7.37.5",
|
|
197
|
-
"eslint-plugin-react-hooks": "^7.0.
|
|
198
|
-
"globals": "^16.
|
|
199
|
-
"npm-check-updates": "^19.
|
|
197
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
198
|
+
"globals": "^16.5.0",
|
|
199
|
+
"npm-check-updates": "^19.1.2",
|
|
200
200
|
"prettier": "^3.6.2",
|
|
201
|
-
"sass": "^1.
|
|
201
|
+
"sass": "^1.94.2",
|
|
202
202
|
"ts-node": "^10.9.2",
|
|
203
|
+
"tsx": "^4.20.6",
|
|
203
204
|
"typescript": "^5.9.3",
|
|
204
|
-
"vite": "^7.
|
|
205
|
-
"vitest": "^
|
|
205
|
+
"vite": "^7.2.4",
|
|
206
|
+
"vitest": "^4.0.13"
|
|
206
207
|
},
|
|
207
208
|
"optionalDependencies": {
|
|
208
|
-
"@rollup/rollup-darwin-arm64": "^4.
|
|
209
|
-
"@rollup/rollup-win32-x64-msvc": "^4.
|
|
209
|
+
"@rollup/rollup-darwin-arm64": "^4.53.3",
|
|
210
|
+
"@rollup/rollup-win32-x64-msvc": "^4.53.3"
|
|
210
211
|
}
|
|
211
212
|
}
|
|
@@ -70,7 +70,7 @@ import { SparklesSVG } from '../Icons/SparklesSVG';
|
|
|
70
70
|
import { MINIMAL_EXTENSIONS } from '../featureFlags';
|
|
71
71
|
|
|
72
72
|
const DEBUG = false;
|
|
73
|
-
const enableToDoPlugin =
|
|
73
|
+
const enableToDoPlugin = true;
|
|
74
74
|
|
|
75
75
|
// Define a StateField to store the file name.
|
|
76
76
|
// This should be defined at the module level if it's to be imported by other modules.
|
|
@@ -138,6 +138,7 @@ const languageExtensions = {
|
|
|
138
138
|
html: () => html(htmlConfig),
|
|
139
139
|
css,
|
|
140
140
|
md: markdown,
|
|
141
|
+
prompt: markdown,
|
|
141
142
|
svelte,
|
|
142
143
|
};
|
|
143
144
|
|
|
@@ -188,7 +189,7 @@ export const getOrCreateEditor = async ({
|
|
|
188
189
|
esLintSource,
|
|
189
190
|
rainbowBracketsEnabled = true,
|
|
190
191
|
setIsAIChatOpen,
|
|
191
|
-
|
|
192
|
+
setAIChatMessage,
|
|
192
193
|
}: {
|
|
193
194
|
// TODO pass this in from the outside
|
|
194
195
|
paneId?: PaneId;
|
|
@@ -225,7 +226,7 @@ export const getOrCreateEditor = async ({
|
|
|
225
226
|
) => Promise<readonly Diagnostic[]>;
|
|
226
227
|
rainbowBracketsEnabled?: boolean; // New parameter type
|
|
227
228
|
setIsAIChatOpen: (isAIChatOpen: boolean) => void;
|
|
228
|
-
|
|
229
|
+
setAIChatMessage: (message: string) => void;
|
|
229
230
|
}): Promise<ExtendedEditorCacheValue> => {
|
|
230
231
|
// Cache hit
|
|
231
232
|
|
|
@@ -356,7 +357,10 @@ export const getOrCreateEditor = async ({
|
|
|
356
357
|
);
|
|
357
358
|
|
|
358
359
|
// Enable line wrapping for Markdown files
|
|
359
|
-
if (
|
|
360
|
+
if (
|
|
361
|
+
fileExtension === 'md' ||
|
|
362
|
+
fileExtension === 'prompt'
|
|
363
|
+
) {
|
|
360
364
|
extensions.push(EditorView.lineWrapping);
|
|
361
365
|
}
|
|
362
366
|
|
|
@@ -485,22 +489,25 @@ export const getOrCreateEditor = async ({
|
|
|
485
489
|
wrap.style.alignItems = 'center';
|
|
486
490
|
wrap.style.justifyContent = 'center';
|
|
487
491
|
wrap.className = 'icon-button icon-button-dark';
|
|
492
|
+
wrap.style.cursor = 'pointer';
|
|
493
|
+
|
|
494
|
+
// Handle click directly on the wrapper element
|
|
495
|
+
wrap.addEventListener('click', (e) => {
|
|
496
|
+
e.preventDefault();
|
|
497
|
+
e.stopPropagation();
|
|
498
|
+
setAIChatMessage('Implement the TODO');
|
|
499
|
+
setIsAIChatOpen(true);
|
|
500
|
+
});
|
|
501
|
+
|
|
488
502
|
const reactContainer =
|
|
489
503
|
document.createElement('div');
|
|
490
504
|
wrap.appendChild(reactContainer);
|
|
491
505
|
|
|
492
506
|
const root = createRoot(reactContainer);
|
|
493
507
|
root.render(
|
|
494
|
-
<
|
|
495
|
-
onClick={() => {
|
|
496
|
-
setIsAIChatOpen(true);
|
|
497
|
-
// setAIChatMessage('Implement the TODO');
|
|
498
|
-
handleSendMessage('Implement the TODO');
|
|
499
|
-
}}
|
|
500
|
-
>
|
|
501
|
-
<SparklesSVG width={14} height={14} />
|
|
502
|
-
</div>,
|
|
508
|
+
<SparklesSVG width={14} height={14} />,
|
|
503
509
|
);
|
|
510
|
+
|
|
504
511
|
return wrap;
|
|
505
512
|
}
|
|
506
513
|
|
|
@@ -43,7 +43,7 @@ export const CodeEditor = ({
|
|
|
43
43
|
codeEditorRef,
|
|
44
44
|
enableAutoFollow,
|
|
45
45
|
setIsAIChatOpen,
|
|
46
|
-
|
|
46
|
+
setAIChatMessage,
|
|
47
47
|
} = useContext(VZCodeContext);
|
|
48
48
|
|
|
49
49
|
// Set `doc.data.isInteracting` to `true` when the user is interacting
|
|
@@ -117,7 +117,7 @@ export const CodeEditor = ({
|
|
|
117
117
|
aiCopilotEndpoint,
|
|
118
118
|
esLintSource,
|
|
119
119
|
setIsAIChatOpen,
|
|
120
|
-
|
|
120
|
+
setAIChatMessage,
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
if (isMounted) {
|
|
@@ -142,7 +142,7 @@ export const CodeEditor = ({
|
|
|
142
142
|
aiCopilotEndpoint,
|
|
143
143
|
esLintSource,
|
|
144
144
|
setIsAIChatOpen,
|
|
145
|
-
|
|
145
|
+
setAIChatMessage,
|
|
146
146
|
]);
|
|
147
147
|
|
|
148
148
|
// Every time the active file switches from one file to another,
|
|
@@ -32,7 +32,7 @@ import { MicSVG } from '../Icons/MicSVG';
|
|
|
32
32
|
import { sortFileTree } from '../sortFileTree';
|
|
33
33
|
import { SplitPaneResizeContext } from '../SplitPaneResizeContext';
|
|
34
34
|
import { VZCodeContext } from '../VZCodeContext';
|
|
35
|
-
import { AIChat } from '
|
|
35
|
+
import { AIChat } from '../../llm-streaming-ui/components/index.js';
|
|
36
36
|
import { Listing } from './Listing';
|
|
37
37
|
import { Search } from './Search';
|
|
38
38
|
import { DeleteConfirmationModal } from './DeleteConfirmationModal';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# LLM Streaming Server
|
|
2
|
+
|
|
3
|
+
Server-side library for streaming LLM responses with ShareDB integration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module is being extracted from VZCode's AI chat functionality to become a standalone, reusable library. It provides server-side functionality for:
|
|
8
|
+
|
|
9
|
+
- **LLM Streaming**: Creating and managing streaming LLM functions with OpenAI/OpenRouter
|
|
10
|
+
- **AI Code Editing**: Performing AI-assisted code transformations and edits
|
|
11
|
+
- **ShareDB Integration**: Managing chat operations and real-time collaboration
|
|
12
|
+
- **Request Validation**: Validating incoming AI chat requests
|
|
13
|
+
- **Error Handling**: Handling errors gracefully in streaming contexts
|
|
14
|
+
|
|
15
|
+
## Current Status
|
|
16
|
+
|
|
17
|
+
⚠️ **Work in Progress**: This is Phase 1 of the extraction plan. The files have been copied to this directory and imports have been updated, but the code is not yet decoupled from VZCode.
|
|
18
|
+
|
|
19
|
+
## Files
|
|
20
|
+
|
|
21
|
+
- `index.ts` - Main entry point exporting all public APIs
|
|
22
|
+
- `llmStreaming.ts` - Core LLM streaming functionality with OpenAI/OpenRouter
|
|
23
|
+
- `aiEditing.ts` - AI-assisted code editing operations
|
|
24
|
+
- `chatOperations.ts` - ShareDB chat operations (messages, events, streaming)
|
|
25
|
+
- `validation.ts` - Request validation utilities
|
|
26
|
+
- `errorHandling.ts` - Error handling for streaming contexts
|
|
27
|
+
|
|
28
|
+
## Dependencies
|
|
29
|
+
|
|
30
|
+
Currently depends on:
|
|
31
|
+
|
|
32
|
+
- `openai` - OpenAI SDK for LLM interactions
|
|
33
|
+
- `sharedb` - Real-time collaborative editing framework
|
|
34
|
+
- `llm-code-format` - Markdown parsing for code blocks
|
|
35
|
+
- `editcodewithai` - Code editing utilities
|
|
36
|
+
- VZCode utilities: `../types.js`, `../ot.js`, `../randomId.js`, etc.
|
|
37
|
+
|
|
38
|
+
## Future Plans
|
|
39
|
+
|
|
40
|
+
This module will eventually be:
|
|
41
|
+
|
|
42
|
+
1. Decoupled from VZCode-specific types and utilities
|
|
43
|
+
2. Published as `@vizhub/llm-streaming-server` on npm
|
|
44
|
+
3. Made framework-agnostic (can work with Express, Fastify, etc.)
|
|
45
|
+
4. Fully documented with usage examples
|
|
46
|
+
|
|
47
|
+
See the [main issue](https://github.com/vizhub-core/vzcode/issues/XXX) for the full extraction plan.
|
|
48
|
+
|
|
49
|
+
## Usage (Current)
|
|
50
|
+
|
|
51
|
+
Currently, this module is imported by `src/server/aiChatHandler/index.ts`:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { createLLMFunction } from '../../llm-streaming-server/llmStreaming.js';
|
|
55
|
+
import { performAIEditing } from '../../llm-streaming-server/aiEditing.js';
|
|
56
|
+
import {
|
|
57
|
+
ensureChatsExist,
|
|
58
|
+
ensureChatExists,
|
|
59
|
+
addUserMessage,
|
|
60
|
+
setAIStatus,
|
|
61
|
+
} from '../../llm-streaming-server/chatOperations.js';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Contributing
|
|
65
|
+
|
|
66
|
+
As this module is being extracted, please:
|
|
67
|
+
|
|
68
|
+
- Keep changes minimal and focused
|
|
69
|
+
- Maintain backward compatibility with VZCode
|
|
70
|
+
- Update tests when modifying functionality
|
|
71
|
+
- Document any new dependencies or breaking changes
|
|
@@ -6,7 +6,7 @@ import { formatMarkdownFiles } from 'llm-code-format';
|
|
|
6
6
|
import {
|
|
7
7
|
createFilesSnapshot,
|
|
8
8
|
generateFilesUnifiedDiff,
|
|
9
|
-
} from '
|
|
9
|
+
} from '../utils/fileDiff.js';
|
|
10
10
|
|
|
11
11
|
// Dev flag for waiting 1 second before starting the LLM function.
|
|
12
12
|
// Useful for debugging and testing purposes, e.g. checking the typing indicator.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { dateToTimestamp } from '@vizhub/viz-utils';
|
|
2
|
-
import { randomId } from '
|
|
2
|
+
import { randomId } from '../randomId.js';
|
|
3
3
|
import {
|
|
4
4
|
ShareDBDoc,
|
|
5
5
|
StreamingEvent,
|
|
6
6
|
ExtendedVizContent,
|
|
7
|
-
} from '
|
|
8
|
-
import { diff } from '
|
|
7
|
+
} from '../types.js';
|
|
8
|
+
import { diff } from '../ot.js';
|
|
9
9
|
import {
|
|
10
10
|
VizChatId,
|
|
11
11
|
VizContent,
|
|
@@ -543,6 +543,33 @@ export const updateStreamingStatus = (
|
|
|
543
543
|
shareDBDoc.submitOp(op);
|
|
544
544
|
};
|
|
545
545
|
|
|
546
|
+
/**
|
|
547
|
+
* Sets the model for a chat when AI generation starts
|
|
548
|
+
*/
|
|
549
|
+
export const setChatModel = (
|
|
550
|
+
shareDBDoc: ShareDBDoc<ExtendedVizContent>,
|
|
551
|
+
chatId: VizChatId,
|
|
552
|
+
model: string,
|
|
553
|
+
) => {
|
|
554
|
+
DEBUG &&
|
|
555
|
+
console.log(
|
|
556
|
+
`ChatOperations: Setting model: "${model}" for chatId: ${chatId}`,
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
const op = diff(shareDBDoc.data, {
|
|
560
|
+
...shareDBDoc.data,
|
|
561
|
+
chats: {
|
|
562
|
+
...shareDBDoc.data.chats,
|
|
563
|
+
[chatId]: {
|
|
564
|
+
...shareDBDoc.data.chats[chatId],
|
|
565
|
+
model: model,
|
|
566
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
});
|
|
570
|
+
shareDBDoc.submitOp(op);
|
|
571
|
+
};
|
|
572
|
+
|
|
546
573
|
/**
|
|
547
574
|
* Finalizes streaming message and clears streaming state
|
|
548
575
|
*/
|