@messenger-box/platform-server 10.0.3-alpha.95 → 10.0.3-alpha.97
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/lib/containers/containers.js +1 -4
- package/lib/containers/containers.js.map +1 -1
- package/lib/containers/context-services-from-container.js +1 -2
- package/lib/containers/context-services-from-container.js.map +1 -1
- package/lib/graphql/resolvers/channel.js +2 -10
- package/lib/graphql/resolvers/channel.js.map +1 -1
- package/lib/graphql/resolvers/index.js +1 -1
- package/lib/graphql/resolvers/index.js.map +1 -1
- package/lib/graphql/resolvers/post.js +3 -30
- package/lib/graphql/resolvers/post.js.map +1 -1
- package/lib/graphql/schema/channel.graphql +17 -2
- package/lib/graphql/schema/channel.graphql.js +1 -1
- package/lib/graphql/schema/index.js +2 -2
- package/lib/graphql/schema/index.js.map +1 -1
- package/lib/graphql/schema/post.graphql +16 -2
- package/lib/graphql/schema/post.graphql.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/inngest/factory.js +2 -2
- package/lib/inngest/factory.js.map +1 -1
- package/lib/inngest/functions.d.ts +0 -96
- package/lib/inngest/functions.js +88 -815
- package/lib/inngest/functions.js.map +1 -1
- package/lib/module.d.ts +1 -1
- package/lib/module.js +8 -8
- package/lib/module.js.map +1 -1
- package/lib/plugins/index.d.ts +0 -1
- package/lib/preferences/permissions/inbox-permission-contribution.js +61 -22
- package/lib/preferences/permissions/inbox-permission-contribution.js.map +1 -1
- package/lib/preferences/permissions/inbox-roles-permission-overwrite.js +2 -2
- package/lib/preferences/permissions/inbox-roles-permission-overwrite.js.map +1 -1
- package/lib/services/channel-service.js +2 -2
- package/lib/services/channel-service.js.map +1 -1
- package/lib/services/index.d.ts +0 -1
- package/lib/services/post-service.d.ts +2 -3
- package/lib/services/post-service.js +9 -12
- package/lib/services/post-service.js.map +1 -1
- package/lib/services/proxy-services/index.d.ts +0 -1
- package/lib/store/models/index.d.ts +0 -1
- package/lib/store/repositories/channel-repository.js +1 -1
- package/lib/store/repositories/channel-repository.js.map +1 -1
- package/lib/store/repositories/index.d.ts +0 -1
- package/lib/store/repositories/post-repository.js +1 -1
- package/lib/store/repositories/post-repository.js.map +1 -1
- package/lib/store/repositories/post-thread-repository.js +1 -1
- package/lib/store/repositories/post-thread-repository.js.map +1 -1
- package/lib/store/repositories/reaction-repository.js +1 -1
- package/lib/store/repositories/reaction-repository.js.map +1 -1
- package/lib/templates/constants/SERVER_TYPES.ts.template +0 -3
- package/lib/templates/constants/WorkflowNamespace.ts.template +10 -0
- package/lib/templates/services/PostService.ts.template +8 -8
- package/package.json +6 -7
- package/lib/graphql/resolvers/ai-fragment.d.ts +0 -3
- package/lib/graphql/resolvers/ai-fragment.js +0 -276
- package/lib/graphql/resolvers/ai-fragment.js.map +0 -1
- package/lib/graphql/schema/ai-fragment.graphql +0 -311
- package/lib/graphql/schema/ai-fragment.graphql.js +0 -1
- package/lib/graphql/schema/ai-fragment.graphql.js.map +0 -1
- package/lib/plugins/ai-fragment-moleculer-service.d.ts +0 -29
- package/lib/plugins/ai-fragment-moleculer-service.js +0 -516
- package/lib/plugins/ai-fragment-moleculer-service.js.map +0 -1
- package/lib/services/ai-fragment-service.d.ts +0 -195
- package/lib/services/ai-fragment-service.js +0 -631
- package/lib/services/ai-fragment-service.js.map +0 -1
- package/lib/services/proxy-services/ai-fragment-microservice.d.ts +0 -23
- package/lib/services/proxy-services/ai-fragment-microservice.js +0 -75
- package/lib/services/proxy-services/ai-fragment-microservice.js.map +0 -1
- package/lib/store/models/ai-fragment.d.ts +0 -4
- package/lib/store/models/ai-fragment.js +0 -125
- package/lib/store/models/ai-fragment.js.map +0 -1
- package/lib/store/repositories/ai-fragment-repository.d.ts +0 -15
- package/lib/store/repositories/ai-fragment-repository.js +0 -69
- package/lib/store/repositories/ai-fragment-repository.js.map +0 -1
- package/lib/templates/repositories/AiFragmentRepository.ts.template +0 -4
- package/lib/templates/services/AiFragmentService.ts.template +0 -123
package/lib/inngest/functions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {SERVER_TYPES,RoomType,PostTypeEnum,
|
|
1
|
+
import {WorkflowNamespace,SERVER_TYPES,RoomType,AiAgentMessageRole,PostTypeEnum,AiAgentMessageType}from'common/server';import {TaggedType}from'@common-stack/core';import {createState,createAgent,createTool,createNetwork,gemini,anthropic,openai}from'@inngest/agent-kit';import {Sandbox}from'@e2b/code-interpreter';import {z}from'zod';import {lastAssistantTextMessageContent,getSandbox,parseAgentOutput}from'./utils.js';import {FRAGMENT_TITLE_PROMPT,RESPONSE_PROMPT,VUE_CODING_PROMPT,VITE_REACT_CODING_PROMPT,CODING_PROMPT}from'./prompt.js';import {config}from'../config/env-config.js';// E2B Configuration constants
|
|
2
2
|
const templateID = config.E2B_TEMPLATE_ID; // Next.js template (default)
|
|
3
3
|
const vueTemplateID = config.E2B_VUE_TEMPLATE_ID; // Vue template
|
|
4
4
|
const viteReactTemplateID = config.E2B_VITE_REACT_TEMPLATE_ID; // Vite+React template
|
|
@@ -24,21 +24,6 @@ const getTemplateId = modelConfig => {
|
|
|
24
24
|
}
|
|
25
25
|
return templateID; // Default to Next.js template
|
|
26
26
|
};
|
|
27
|
-
// Helper function to normalize template names for database validation
|
|
28
|
-
const normalizeTemplateName = template => {
|
|
29
|
-
if (!template) return 'NEXTJS';
|
|
30
|
-
switch (template.toLowerCase()) {
|
|
31
|
-
case 'nextjs':
|
|
32
|
-
return 'NEXTJS';
|
|
33
|
-
case 'vue':
|
|
34
|
-
return 'VUE';
|
|
35
|
-
case 'vite-react':
|
|
36
|
-
case 'vitereact':
|
|
37
|
-
return 'VITE_REACT';
|
|
38
|
-
default:
|
|
39
|
-
return 'NEXTJS';
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
27
|
// Helper to create model instance from modelConfig (provider/model/apiKey)
|
|
43
28
|
const createModelInstance = modelConfig => {
|
|
44
29
|
if (!modelConfig || !modelConfig.apiKey) {
|
|
@@ -74,9 +59,9 @@ const createModelInstance = modelConfig => {
|
|
|
74
59
|
}
|
|
75
60
|
};
|
|
76
61
|
const createChannelWithProjectId = (inngest, container) => inngest.createFunction({
|
|
77
|
-
id:
|
|
62
|
+
id: WorkflowNamespace.MESSENGER_PLATFORM_CREATE_CHANNEL_FUNCTION
|
|
78
63
|
}, {
|
|
79
|
-
event:
|
|
64
|
+
event: WorkflowNamespace.MESSENGER_PLATFORM_CREATE_CHANNEL_EVENT
|
|
80
65
|
}, async ({
|
|
81
66
|
event,
|
|
82
67
|
step
|
|
@@ -88,17 +73,16 @@ const createChannelWithProjectId = (inngest, container) => inngest.createFunctio
|
|
|
88
73
|
channelInput
|
|
89
74
|
} = event.data || {};
|
|
90
75
|
const {
|
|
91
|
-
content,
|
|
92
76
|
createdBy,
|
|
77
|
+
type,
|
|
78
|
+
title,
|
|
79
|
+
displayName,
|
|
80
|
+
topic,
|
|
81
|
+
description,
|
|
93
82
|
orgName,
|
|
94
|
-
organization,
|
|
95
83
|
team,
|
|
96
|
-
files,
|
|
97
|
-
notificationParams,
|
|
98
84
|
modelConfig,
|
|
99
|
-
|
|
100
|
-
teamId,
|
|
101
|
-
orgId
|
|
85
|
+
postData
|
|
102
86
|
} = channelInput;
|
|
103
87
|
// Validate that projectId is provided
|
|
104
88
|
if (!projectId) {
|
|
@@ -111,7 +95,7 @@ const createChannelWithProjectId = (inngest, container) => inngest.createFunctio
|
|
|
111
95
|
const existingList = await channelService.getAll({
|
|
112
96
|
criteria: {
|
|
113
97
|
projectId,
|
|
114
|
-
type: RoomType.Aiassistant
|
|
98
|
+
type: type || RoomType.Aiassistant
|
|
115
99
|
}
|
|
116
100
|
});
|
|
117
101
|
const existing = Array.isArray(existingList) ? existingList[0] : null;
|
|
@@ -119,15 +103,16 @@ const createChannelWithProjectId = (inngest, container) => inngest.createFunctio
|
|
|
119
103
|
return existing;
|
|
120
104
|
}
|
|
121
105
|
const newChannel = await channelService.saveChannel({
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
106
|
+
...channelInput,
|
|
107
|
+
type: type || RoomType.Aiassistant,
|
|
108
|
+
orgName,
|
|
109
|
+
organization: orgName,
|
|
110
|
+
creator: createdBy,
|
|
111
|
+
title: title || 'AI Assistant',
|
|
112
|
+
displayName: displayName || 'AI Assistant',
|
|
113
|
+
topic: topic || 'AI Assistant',
|
|
114
|
+
team: team,
|
|
115
|
+
description: description || 'AI Assistant',
|
|
131
116
|
projectId,
|
|
132
117
|
_id: undefined
|
|
133
118
|
});
|
|
@@ -138,28 +123,36 @@ const createChannelWithProjectId = (inngest, container) => inngest.createFunctio
|
|
|
138
123
|
if (!channelId) {
|
|
139
124
|
throw new Error('Failed to resolve channel id for post creation');
|
|
140
125
|
}
|
|
126
|
+
const {
|
|
127
|
+
content,
|
|
128
|
+
files,
|
|
129
|
+
props: propsData
|
|
130
|
+
} = postData;
|
|
131
|
+
const props = propsData || {
|
|
132
|
+
notificationParams: {},
|
|
133
|
+
template: modelConfig?.template || 'vite-react',
|
|
134
|
+
projectId: projectId,
|
|
135
|
+
role: AiAgentMessageRole.User,
|
|
136
|
+
fragment: {},
|
|
137
|
+
sendNotificationWithProjectId: false
|
|
138
|
+
};
|
|
141
139
|
await step.sendEvent('emit-send-message', {
|
|
142
|
-
name:
|
|
140
|
+
name: WorkflowNamespace.MESSENGER_PLATFORM_CREATE_POST_EVENT,
|
|
143
141
|
data: {
|
|
144
142
|
channelId,
|
|
145
143
|
content,
|
|
146
144
|
createdBy,
|
|
147
145
|
files,
|
|
148
|
-
|
|
149
|
-
projectId,
|
|
150
|
-
modelConfig,
|
|
151
|
-
accountId,
|
|
152
|
-
teamId,
|
|
153
|
-
orgId
|
|
146
|
+
props
|
|
154
147
|
}
|
|
155
148
|
});
|
|
156
149
|
}
|
|
157
150
|
return channelData;
|
|
158
151
|
});
|
|
159
152
|
const sendMessageHandler = (inngest, container) => inngest.createFunction({
|
|
160
|
-
id:
|
|
153
|
+
id: WorkflowNamespace.MESSENGER_PLATFORM_CREATE_POST_FUNCTION
|
|
161
154
|
}, {
|
|
162
|
-
event:
|
|
155
|
+
event: WorkflowNamespace.MESSENGER_PLATFORM_CREATE_POST_EVENT
|
|
163
156
|
}, async ({
|
|
164
157
|
event,
|
|
165
158
|
step
|
|
@@ -167,717 +160,38 @@ const sendMessageHandler = (inngest, container) => inngest.createFunction({
|
|
|
167
160
|
console.log('=== Send Message Handler FUNCTION STARTED ===');
|
|
168
161
|
console.log('=== Send Message Handler Event data:', JSON.stringify(event.data, null, 2));
|
|
169
162
|
const {
|
|
170
|
-
|
|
163
|
+
props,
|
|
171
164
|
channelId,
|
|
172
165
|
content,
|
|
173
166
|
createdBy,
|
|
174
|
-
files
|
|
175
|
-
notificationParams,
|
|
176
|
-
modelConfig,
|
|
177
|
-
accountId,
|
|
178
|
-
teamId,
|
|
179
|
-
orgId
|
|
167
|
+
files
|
|
180
168
|
} = event.data || {};
|
|
181
|
-
if (!channelId || !content || !createdBy
|
|
169
|
+
if (!channelId || !content || !createdBy) {
|
|
182
170
|
throw new Error('channelId, content and createdBy are required');
|
|
183
171
|
}
|
|
184
172
|
const messageData = await step.run('persist-message', async () => {
|
|
185
173
|
const postService = container.getNamed(SERVER_TYPES.PostService, TaggedType.MICROSERVICE);
|
|
186
|
-
container.get('PubSub');
|
|
187
174
|
const postDoc = await postService.create({
|
|
188
175
|
channel: channelId,
|
|
189
176
|
message: content,
|
|
190
|
-
editedBy: createdBy
|
|
191
|
-
author: createdBy
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
notificationParams: notificationParams || {},
|
|
195
|
-
template: modelConfig?.template || 'vite-react',
|
|
196
|
-
projectId: projectId,
|
|
197
|
-
role: AiAgentMessageRole.USER,
|
|
198
|
-
fragment: {},
|
|
199
|
-
sendNotificationWithProjectId: false
|
|
200
|
-
},
|
|
201
|
-
type: PostTypeEnum.Simple,
|
|
177
|
+
editedBy: createdBy,
|
|
178
|
+
author: createdBy,
|
|
179
|
+
props: props || {},
|
|
180
|
+
type: PostTypeEnum.Aiassistant,
|
|
202
181
|
files
|
|
203
182
|
});
|
|
204
|
-
// if (postDoc) {
|
|
205
|
-
// pubsub.publish(`POST_CREATED.${projectId}`, postDoc);
|
|
206
|
-
// }
|
|
207
183
|
return await postDoc;
|
|
208
184
|
});
|
|
209
185
|
await step.run('publish-subscription-update', async () => {
|
|
210
186
|
const pubsub = container.get('PubSub');
|
|
211
|
-
pubsub.publish(`POST_CREATED.${projectId}`, messageData);
|
|
187
|
+
pubsub.publish(`POST_CREATED.${props.projectId}`, messageData);
|
|
212
188
|
});
|
|
213
|
-
// Prepare final model config with the template that was actually used
|
|
214
|
-
// const finalModelConfig = await step.run('prepare-final-model-config', async () => {
|
|
215
|
-
// let actualTemplate = modelConfig?.template || 'nextjs';
|
|
216
|
-
// if (modelConfig) {
|
|
217
|
-
// // Use provided modelConfig but ensure template is what was actually stored
|
|
218
|
-
// const finalConfig = {
|
|
219
|
-
// ...modelConfig,
|
|
220
|
-
// template: actualTemplate, // Always use the immutable template from DB
|
|
221
|
-
// };
|
|
222
|
-
// console.log('Final modelConfig for code generation:', finalConfig);
|
|
223
|
-
// return finalConfig;
|
|
224
|
-
// }
|
|
225
|
-
// return {
|
|
226
|
-
// template: actualTemplate,
|
|
227
|
-
// provider: 'gemini',
|
|
228
|
-
// model: 'gemini-1.5-flash',
|
|
229
|
-
// apiKey: '', // Will need to be provided via environment
|
|
230
|
-
// };
|
|
231
|
-
// });
|
|
232
|
-
// Trigger code generation
|
|
233
|
-
// await step.run('trigger-code-generation', async () => {
|
|
234
|
-
// await inngest.send({
|
|
235
|
-
// name: 'code-agent/run-code-creation',
|
|
236
|
-
// data: {
|
|
237
|
-
// value:messageData.message,
|
|
238
|
-
// projectId,
|
|
239
|
-
// messageId: messageData.id,
|
|
240
|
-
// owner: messageData?.author || createdBy || accountId,
|
|
241
|
-
// orgName: orgId,
|
|
242
|
-
// modelConfig: finalModelConfig,
|
|
243
|
-
// accountId,
|
|
244
|
-
// teamId,
|
|
245
|
-
// orgId,
|
|
246
|
-
// channelId
|
|
247
|
-
// },
|
|
248
|
-
// });
|
|
249
|
-
// });
|
|
250
|
-
// Send event after result is available
|
|
251
|
-
// await step.sendEvent('emit-ai-response-generate', {
|
|
252
|
-
// name: 'ai.response.generate',
|
|
253
|
-
// data: {
|
|
254
|
-
// projectId,
|
|
255
|
-
// post: result,
|
|
256
|
-
// modelConfig,
|
|
257
|
-
// },
|
|
258
|
-
// });
|
|
259
189
|
return messageData;
|
|
260
190
|
});
|
|
261
|
-
// Main code agent function - processes queries and generates code
|
|
262
|
-
const codeAgentFunction = (inngest, container) => inngest.createFunction({
|
|
263
|
-
id: 'code-agent'
|
|
264
|
-
}, {
|
|
265
|
-
event: 'code-agent/run-code-creation'
|
|
266
|
-
}, async ({
|
|
267
|
-
event,
|
|
268
|
-
step
|
|
269
|
-
}) => {
|
|
270
|
-
console.log('=== CODE AGENT FUNCTION STARTED ===');
|
|
271
|
-
console.log('Event data:', JSON.stringify(event.data, null, 2));
|
|
272
|
-
const {
|
|
273
|
-
sandboxId,
|
|
274
|
-
sandboxUrl
|
|
275
|
-
} = await step.run('create-sandbox-and-start-monitoring', async () => {
|
|
276
|
-
const selectedTemplateId = getTemplateId(event.data.modelConfig);
|
|
277
|
-
console.log(`Creating sandbox with template: ${selectedTemplateId} (requested: ${event.data.modelConfig?.template || 'nextjs'})`);
|
|
278
|
-
const sandbox = await Sandbox.create(selectedTemplateId, {
|
|
279
|
-
apiKey,
|
|
280
|
-
domain,
|
|
281
|
-
timeoutMs: 60_000 * 20
|
|
282
|
-
});
|
|
283
|
-
const sandboxUrl = `https://3000-${sandbox.sandboxId}.yarntra.ai`;
|
|
284
|
-
// Start error monitoring immediately after sandbox creation
|
|
285
|
-
// const container = await initializeContainer();
|
|
286
|
-
const sandboxErrorService = container.get(SERVER_TYPES.SandboxErrorService);
|
|
287
|
-
console.log(`Starting immediate error monitoring for sandbox ${sandbox.sandboxId}`);
|
|
288
|
-
await sandboxErrorService.startErrorMonitoring(event.data.projectId, sandbox.sandboxId, sandboxUrl);
|
|
289
|
-
return {
|
|
290
|
-
sandboxId: sandbox.sandboxId,
|
|
291
|
-
sandboxUrl
|
|
292
|
-
};
|
|
293
|
-
});
|
|
294
|
-
// Only fetch previous messages if we don't have active fragment files
|
|
295
|
-
const previousMessages = event.data.activeFragmentFiles ? [] // Skip fetching previous messages when we have active fragment files
|
|
296
|
-
: await step.run('get-previous-messages', async () => {
|
|
297
|
-
const postService = container.getNamed(SERVER_TYPES.PostService, TaggedType.MICROSERVICE);
|
|
298
|
-
const result = await postService.getPreviousMessagesByProjectId(event.data.projectId, 5);
|
|
299
|
-
// Handle error case - return empty array if getPreviousMessagesByProjectId returns an Error
|
|
300
|
-
if (result instanceof Error) {
|
|
301
|
-
console.warn('Failed to get previous messages:', result.message);
|
|
302
|
-
return [];
|
|
303
|
-
}
|
|
304
|
-
return result;
|
|
305
|
-
});
|
|
306
|
-
const state = createState({
|
|
307
|
-
summary: '',
|
|
308
|
-
files: event.data.activeFragmentFiles || {},
|
|
309
|
-
// Use active fragment files if provided
|
|
310
|
-
canvasLayers: []
|
|
311
|
-
}, {
|
|
312
|
-
messages: previousMessages
|
|
313
|
-
});
|
|
314
|
-
// Get model configuration from event data
|
|
315
|
-
const {
|
|
316
|
-
modelConfig
|
|
317
|
-
} = event.data;
|
|
318
|
-
console.log('Model configuration received:', JSON.stringify(modelConfig, null, 2));
|
|
319
|
-
// Create a new agent with the template-specific coding prompt
|
|
320
|
-
const selectedCodingPrompt = getCodingPrompt(event.data.modelConfig);
|
|
321
|
-
console.log(`Using coding prompt for template: ${event.data.modelConfig?.template || 'nextjs'}`);
|
|
322
|
-
const codeAgent = createAgent({
|
|
323
|
-
name: 'code-agent',
|
|
324
|
-
description: 'An expert coding agent for page creation',
|
|
325
|
-
system: selectedCodingPrompt,
|
|
326
|
-
model: createModelInstance(modelConfig),
|
|
327
|
-
tools: [createTool({
|
|
328
|
-
name: 'terminal',
|
|
329
|
-
description: 'Use the terminal to run commands',
|
|
330
|
-
parameters: z.object({
|
|
331
|
-
command: z.string()
|
|
332
|
-
}),
|
|
333
|
-
handler: async ({
|
|
334
|
-
command
|
|
335
|
-
}, {
|
|
336
|
-
step
|
|
337
|
-
}) => await step?.run('terminal', async () => {
|
|
338
|
-
try {
|
|
339
|
-
const sandbox = await getSandbox(sandboxId);
|
|
340
|
-
const result = await sandbox.commands.run(command);
|
|
341
|
-
return result.stdout;
|
|
342
|
-
} catch (e) {
|
|
343
|
-
return `Command failed: ${e}`;
|
|
344
|
-
}
|
|
345
|
-
})
|
|
346
|
-
}), createTool({
|
|
347
|
-
name: 'createOrUpdateFiles',
|
|
348
|
-
description: 'Create or update files in the sandbox',
|
|
349
|
-
parameters: z.object({
|
|
350
|
-
files: z.union([z.array(z.object({
|
|
351
|
-
path: z.string(),
|
|
352
|
-
content: z.string()
|
|
353
|
-
})), z.string() // Accept JSON string as fallback
|
|
354
|
-
])
|
|
355
|
-
}),
|
|
356
|
-
// handler: async ({ files }, { step, network }: Tool.Options<AgentState>) => {
|
|
357
|
-
handler: async ({
|
|
358
|
-
files
|
|
359
|
-
}, {
|
|
360
|
-
step,
|
|
361
|
-
network
|
|
362
|
-
}) => {
|
|
363
|
-
const newFiles = await step?.run('createOrUpdateFiles', async () => {
|
|
364
|
-
try {
|
|
365
|
-
console.log('createOrUpdateFiles - Raw files parameter type:', typeof files);
|
|
366
|
-
console.log('createOrUpdateFiles - Raw files parameter:', `${JSON.stringify(files).substring(0, 200)}...`);
|
|
367
|
-
// Parse files if it's a JSON string (fallback for AI model behavior)
|
|
368
|
-
let parsedFiles;
|
|
369
|
-
if (typeof files === 'string') {
|
|
370
|
-
try {
|
|
371
|
-
// First try standard JSON parsing
|
|
372
|
-
parsedFiles = JSON.parse(files);
|
|
373
|
-
console.log('createOrUpdateFiles - Successfully parsed files from JSON string');
|
|
374
|
-
} catch (parseError) {
|
|
375
|
-
console.log('createOrUpdateFiles - Standard JSON parse failed, trying template literal fix');
|
|
376
|
-
try {
|
|
377
|
-
// AI is using template literals in JSON - need more sophisticated parsing
|
|
378
|
-
console.log('createOrUpdateFiles - Attempting to fix template literals in JSON');
|
|
379
|
-
// Try to evaluate the string as JavaScript to handle template literals
|
|
380
|
-
// This is safe because we're in a sandboxed environment and only parsing our own AI's output
|
|
381
|
-
const evalString = `(${files})`;
|
|
382
|
-
console.log('createOrUpdateFiles - Evaluating as JavaScript array');
|
|
383
|
-
parsedFiles = eval(evalString);
|
|
384
|
-
console.log('createOrUpdateFiles - Successfully evaluated template literals');
|
|
385
|
-
} catch (secondError) {
|
|
386
|
-
console.error('createOrUpdateFiles - Both parsing attempts failed:');
|
|
387
|
-
console.error('Original error:', parseError.message);
|
|
388
|
-
console.error('Template literal fix error:', secondError.message);
|
|
389
|
-
console.error('Raw string (first 500 chars):', files.substring(0, 500));
|
|
390
|
-
return `Error: Failed to parse files JSON - ${parseError.message}`;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
} else if (Array.isArray(files)) {
|
|
394
|
-
parsedFiles = files;
|
|
395
|
-
console.log('createOrUpdateFiles - Using files array directly');
|
|
396
|
-
} else {
|
|
397
|
-
console.error('createOrUpdateFiles - Files parameter is neither string nor array:', typeof files);
|
|
398
|
-
return `Error: Files parameter must be an array or JSON string, got ${typeof files}`;
|
|
399
|
-
}
|
|
400
|
-
// Validate that parsedFiles is an array
|
|
401
|
-
if (!Array.isArray(parsedFiles)) {
|
|
402
|
-
console.error('createOrUpdateFiles - Parsed files is not an array:', parsedFiles);
|
|
403
|
-
return `Error: Files must be an array after parsing, got ${typeof parsedFiles}`;
|
|
404
|
-
}
|
|
405
|
-
console.log(`createOrUpdateFiles - Processing ${parsedFiles.length} files`);
|
|
406
|
-
const updatedFiles = network.state.data.files || {};
|
|
407
|
-
const sandbox = await getSandbox(sandboxId);
|
|
408
|
-
for (const file of parsedFiles) {
|
|
409
|
-
if (!file.path || file.content === undefined) {
|
|
410
|
-
console.error('createOrUpdateFiles - Invalid file object (missing path or content):', file);
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
console.log(`createOrUpdateFiles - Writing file: ${file.path}`);
|
|
414
|
-
await sandbox.files.write(file.path, file.content);
|
|
415
|
-
updatedFiles[file.path] = file.content;
|
|
416
|
-
}
|
|
417
|
-
console.log(`createOrUpdateFiles - Successfully updated ${parsedFiles.length} files`);
|
|
418
|
-
return updatedFiles;
|
|
419
|
-
} catch (e) {
|
|
420
|
-
console.error('createOrUpdateFiles - Error:', e);
|
|
421
|
-
console.error('createOrUpdateFiles - Error stack:', e?.stack);
|
|
422
|
-
return `Error: ${e}`;
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
if (typeof newFiles === 'object' && !Array.isArray(newFiles) && typeof newFiles !== 'string') {
|
|
426
|
-
network.state.data.files = newFiles;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}), createTool({
|
|
430
|
-
name: 'readFiles',
|
|
431
|
-
description: 'Read files from the sandbox',
|
|
432
|
-
parameters: z.object({
|
|
433
|
-
files: z.array(z.string())
|
|
434
|
-
}),
|
|
435
|
-
handler: async ({
|
|
436
|
-
files
|
|
437
|
-
}, {
|
|
438
|
-
step
|
|
439
|
-
}) => await step?.run('readFiles', async () => {
|
|
440
|
-
try {
|
|
441
|
-
const sandbox = await getSandbox(sandboxId);
|
|
442
|
-
const contents = [];
|
|
443
|
-
for (const file of files) {
|
|
444
|
-
const content = await sandbox.files.read(file);
|
|
445
|
-
contents.push({
|
|
446
|
-
path: file,
|
|
447
|
-
content
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
return JSON.stringify(contents);
|
|
451
|
-
} catch (e) {
|
|
452
|
-
return `Error: ${e}`;
|
|
453
|
-
}
|
|
454
|
-
})
|
|
455
|
-
})],
|
|
456
|
-
lifecycle: {
|
|
457
|
-
onResponse: async ({
|
|
458
|
-
result,
|
|
459
|
-
network
|
|
460
|
-
}) => {
|
|
461
|
-
const lastAssistantMessageText = lastAssistantTextMessageContent(result);
|
|
462
|
-
if (lastAssistantMessageText && network) {
|
|
463
|
-
if (lastAssistantMessageText.includes('<task_summary>')) {
|
|
464
|
-
network.state.data.summary = lastAssistantMessageText;
|
|
465
|
-
// Extract canvas layers if present
|
|
466
|
-
const canvasLayersMatch = lastAssistantMessageText.match(/<canvas_layers>([\s\S]*?)<\/canvas_layers>/);
|
|
467
|
-
if (canvasLayersMatch) {
|
|
468
|
-
try {
|
|
469
|
-
const layersJson = canvasLayersMatch[1].trim();
|
|
470
|
-
network.state.data.canvasLayers = JSON.parse(layersJson);
|
|
471
|
-
} catch (error) {
|
|
472
|
-
console.error('Error parsing canvas layers:', error);
|
|
473
|
-
network.state.data.canvasLayers = null;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return result;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
const network = createNetwork({
|
|
483
|
-
name: 'coding-agent-network',
|
|
484
|
-
agents: [codeAgent],
|
|
485
|
-
maxIter: 15,
|
|
486
|
-
defaultState: state,
|
|
487
|
-
router: async ({
|
|
488
|
-
network
|
|
489
|
-
}) => {
|
|
490
|
-
const {
|
|
491
|
-
summary
|
|
492
|
-
} = network.state.data;
|
|
493
|
-
if (summary) {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
return codeAgent;
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
// Prepare the enhanced input message that includes activeFragmentFiles context
|
|
500
|
-
let enhancedInput = event.data.value;
|
|
501
|
-
// If we have activeFragmentFiles, include them in the message to the AI
|
|
502
|
-
if (event.data.activeFragmentFiles && Object.keys(event.data.activeFragmentFiles).length > 0) {
|
|
503
|
-
console.log('📂 Including activeFragmentFiles context for AI agent:');
|
|
504
|
-
console.log('- File count:', Object.keys(event.data.activeFragmentFiles).length);
|
|
505
|
-
console.log('- Files:', Object.keys(event.data.activeFragmentFiles));
|
|
506
|
-
// Check if ViteVisualEditor exists and warn if it's missing for vite-react template
|
|
507
|
-
const hasViteVisualEditor = 'src/components/ViteVisualEditor.tsx' in event.data.activeFragmentFiles;
|
|
508
|
-
const isViteReact = event.data.modelConfig?.template === 'vite-react';
|
|
509
|
-
if (isViteReact && !hasViteVisualEditor) {
|
|
510
|
-
console.warn('⚠️ ViteVisualEditor.tsx missing from activeFragmentFiles for vite-react template');
|
|
511
|
-
console.warn('AI may create stub component instead of importing existing one');
|
|
512
|
-
}
|
|
513
|
-
enhancedInput = `${event.data.value}
|
|
514
|
-
|
|
515
|
-
ACTIVE FRAGMENT FILES CONTEXT:
|
|
516
|
-
The current sandbox contains the following files that you MUST include in your createOrUpdateFiles call:
|
|
517
|
-
|
|
518
|
-
${Object.entries(event.data.activeFragmentFiles).map(([path, content]) => `FILE: ${path}\n${typeof content === 'string' ? content.substring(0, 500) + (content.length > 500 ? '...' : '') : '[Non-string content]'}`).join('\n\n')}
|
|
519
|
-
|
|
520
|
-
CRITICAL INSTRUCTIONS FOR VISUAL CHANGES:
|
|
521
|
-
- You MUST include ALL ${Object.keys(event.data.activeFragmentFiles).length} files from the active fragment files in your createOrUpdateFiles call
|
|
522
|
-
- Apply the visual changes described above to the appropriate files
|
|
523
|
-
- Preserve ALL existing files and their content except for the specific changes requested
|
|
524
|
-
- Use the complete file context provided above as your current codebase state
|
|
525
|
-
- DO NOT output only the changed file - output ALL files to rebuild the complete sandbox${isViteReact && !hasViteVisualEditor ? '\n- IMPORTANT: For vite-react template, you MUST import ViteVisualEditor from "./components/ViteVisualEditor" (it exists in the template), do NOT create a new component' : ''}`;
|
|
526
|
-
}
|
|
527
|
-
console.log('🤖 Running AI agent with enhanced input length:', enhancedInput.length);
|
|
528
|
-
const result = await network.run(enhancedInput, {
|
|
529
|
-
state
|
|
530
|
-
});
|
|
531
|
-
// Enhanced file validation with logging
|
|
532
|
-
const hasFiles = result.state.data.files && Object.keys(result.state.data.files).length > 0;
|
|
533
|
-
const hasSummary = result.state.data.summary && result.state.data.summary.trim().length > 0;
|
|
534
|
-
const outputFileCount = Object.keys(result.state.data.files || {}).length;
|
|
535
|
-
const inputFileCount = event.data.activeFragmentFiles ? Object.keys(event.data.activeFragmentFiles).length : 0;
|
|
536
|
-
console.log('Agent result validation:', {
|
|
537
|
-
hasFiles,
|
|
538
|
-
outputFileCount,
|
|
539
|
-
inputFileCount,
|
|
540
|
-
fileCountMatch: inputFileCount > 0 ? outputFileCount === inputFileCount : true,
|
|
541
|
-
hasSummary,
|
|
542
|
-
summaryLength: result.state.data.summary?.length || 0,
|
|
543
|
-
outputFiles: Object.keys(result.state.data.files || {}),
|
|
544
|
-
inputFiles: event.data.activeFragmentFiles ? Object.keys(event.data.activeFragmentFiles) : []
|
|
545
|
-
});
|
|
546
|
-
// Warning if file counts don't match for visual changes
|
|
547
|
-
if (event.data.activeFragmentFiles && inputFileCount > 0 && outputFileCount !== inputFileCount) {
|
|
548
|
-
console.warn(`⚠️ File count mismatch! Expected ${inputFileCount} files, got ${outputFileCount} files`);
|
|
549
|
-
console.warn('Missing files may cause incomplete sandbox rebuild');
|
|
550
|
-
}
|
|
551
|
-
const fragmentTitleGenerator = createAgent({
|
|
552
|
-
name: 'fragment-title-generator',
|
|
553
|
-
description: 'A fragment title generator',
|
|
554
|
-
system: FRAGMENT_TITLE_PROMPT,
|
|
555
|
-
model: createModelInstance(modelConfig)
|
|
556
|
-
});
|
|
557
|
-
const responseGenerator = createAgent({
|
|
558
|
-
name: 'response-generator',
|
|
559
|
-
description: 'A response generator',
|
|
560
|
-
system: RESPONSE_PROMPT,
|
|
561
|
-
model: createModelInstance(modelConfig)
|
|
562
|
-
});
|
|
563
|
-
// Run generators in parallel outside of steps (like lovable-clone)
|
|
564
|
-
const {
|
|
565
|
-
output: fragmentTitleOutput
|
|
566
|
-
} = await fragmentTitleGenerator.run(result.state.data.summary || 'Generated Page');
|
|
567
|
-
const {
|
|
568
|
-
output: responseOutput
|
|
569
|
-
} = await responseGenerator.run(result.state.data.summary || 'Code generated successfully');
|
|
570
|
-
const agentError = !result.state.data.summary || Object.keys(result.state.data.files || {}).length === 0;
|
|
571
|
-
// sandboxUrl is already available from the previous step
|
|
572
|
-
// Save to database using handler
|
|
573
|
-
const messageData = await step.run('save-result', async () => {
|
|
574
|
-
const postService = container.getNamed(SERVER_TYPES.PostService, TaggedType.MICROSERVICE);
|
|
575
|
-
// Normalize template name for database validation
|
|
576
|
-
const normalizedModelConfig = {
|
|
577
|
-
...event.data.modelConfig,
|
|
578
|
-
template: normalizeTemplateName(event.data.modelConfig?.template)
|
|
579
|
-
};
|
|
580
|
-
return await postService.saveCodeAgentResult({
|
|
581
|
-
value: event.data.value,
|
|
582
|
-
projectId: event.data.projectId,
|
|
583
|
-
messageId: event.data.messageId,
|
|
584
|
-
owner: event.data.owner,
|
|
585
|
-
orgName: event.data.orgName,
|
|
586
|
-
modelConfig: normalizedModelConfig
|
|
587
|
-
}, sandboxUrl, parseAgentOutput(fragmentTitleOutput), parseAgentOutput(responseOutput), result.state.data.files || {}, result.state.data.summary || '', agentError, result.state.data.canvasLayers);
|
|
588
|
-
});
|
|
589
|
-
// Publish subscription update for real-time client updates
|
|
590
|
-
await step.run('publish-subscription-update', async () => {
|
|
591
|
-
// const responseData = {
|
|
592
|
-
// projectId: event.data.projectId,
|
|
593
|
-
// sandboxId: sandboxId,
|
|
594
|
-
// sandboxUrl: sandboxUrl,
|
|
595
|
-
// url: sandboxUrl, // Keep both for compatibility
|
|
596
|
-
// title: parseAgentOutput(fragmentTitleOutput) || 'Generated Page',
|
|
597
|
-
// files: result.state.data.files,
|
|
598
|
-
// summary: result.state.data.summary,
|
|
599
|
-
// isError: agentError,
|
|
600
|
-
// messageId: agentError ? null : (messageData as any).id,
|
|
601
|
-
// canvasLayers: result.state.data.canvasLayers,
|
|
602
|
-
// template: normalizeTemplateName(event.data.modelConfig?.template),
|
|
603
|
-
// timestamp: new Date().toISOString(),
|
|
604
|
-
// };
|
|
605
|
-
const pubsub = container.get('PubSub');
|
|
606
|
-
// const post = event.data.post;
|
|
607
|
-
// const postData = {
|
|
608
|
-
// ...messageData,
|
|
609
|
-
// props: {
|
|
610
|
-
// ...messageData.props,
|
|
611
|
-
// fragment: responseData,
|
|
612
|
-
// },
|
|
613
|
-
// };
|
|
614
|
-
console.log('=== Publish to subscription Post Data:', JSON.stringify(messageData, null, 2));
|
|
615
|
-
pubsub.publish(`POST_CREATED.${event.data.projectId}`, messageData);
|
|
616
|
-
});
|
|
617
|
-
// Error monitoring was already started immediately after sandbox creation
|
|
618
|
-
// No need to start it again here
|
|
619
|
-
return {
|
|
620
|
-
projectId: event.data.projectId,
|
|
621
|
-
sandboxId: sandboxId,
|
|
622
|
-
sandboxUrl: sandboxUrl,
|
|
623
|
-
url: sandboxUrl,
|
|
624
|
-
// Keep both for compatibility
|
|
625
|
-
title: parseAgentOutput(fragmentTitleOutput) || 'Generated Page',
|
|
626
|
-
files: result.state.data.files,
|
|
627
|
-
summary: result.state.data.summary,
|
|
628
|
-
isError: agentError,
|
|
629
|
-
canvasLayers: result.state.data.canvasLayers,
|
|
630
|
-
template: normalizeTemplateName(event.data.modelConfig?.template),
|
|
631
|
-
timestamp: new Date().toISOString()
|
|
632
|
-
};
|
|
633
|
-
});
|
|
634
|
-
const generateAIResponseWithSandbox = (inngest, container) => inngest.createFunction({
|
|
635
|
-
id: 'generate-ai-response-with-sandbox'
|
|
636
|
-
}, {
|
|
637
|
-
event: 'ai.response.generate'
|
|
638
|
-
}, async ({
|
|
639
|
-
event,
|
|
640
|
-
step
|
|
641
|
-
}) => {
|
|
642
|
-
console.log('=== AI Response Generation with Sandbox FUNCTION STARTED ===');
|
|
643
|
-
console.log('Event data:', JSON.stringify(event.data, null, 2));
|
|
644
|
-
const {
|
|
645
|
-
projectId,
|
|
646
|
-
post,
|
|
647
|
-
modelConfig
|
|
648
|
-
} = event.data || {};
|
|
649
|
-
const message = post.message;
|
|
650
|
-
// Validate required parameters
|
|
651
|
-
if (!projectId || !message) {
|
|
652
|
-
throw new Error('projectId and message are required');
|
|
653
|
-
}
|
|
654
|
-
// Create e2b sandbox
|
|
655
|
-
const sandbox = await step.run('create-sandbox', async () => {
|
|
656
|
-
try {
|
|
657
|
-
const selectedTemplateId = getTemplateId(modelConfig);
|
|
658
|
-
console.log(`Creating sandbox with template: ${selectedTemplateId} (requested: ${modelConfig?.template || 'vite-react'})`);
|
|
659
|
-
const sandbox = await Sandbox.create(selectedTemplateId, {
|
|
660
|
-
apiKey,
|
|
661
|
-
domain,
|
|
662
|
-
timeoutMs: 60_000 * 20 // 20 minutes timeout
|
|
663
|
-
});
|
|
664
|
-
return sandbox;
|
|
665
|
-
} catch (error) {
|
|
666
|
-
console.error('Failed to create sandbox:', error);
|
|
667
|
-
throw new Error(`Failed to create sandbox: ${error.message}`);
|
|
668
|
-
}
|
|
669
|
-
});
|
|
670
|
-
// Create the appropriate coding prompt based on framework
|
|
671
|
-
let codingPrompt = CODING_PROMPT;
|
|
672
|
-
if (modelConfig?.template === 'vue') {
|
|
673
|
-
codingPrompt = VUE_CODING_PROMPT;
|
|
674
|
-
} else if (modelConfig?.template === 'vite-react') {
|
|
675
|
-
codingPrompt = VITE_REACT_CODING_PROMPT;
|
|
676
|
-
}
|
|
677
|
-
// Create agent with sandbox tools (use modelConfig model if provided)
|
|
678
|
-
const agent = createAgent({
|
|
679
|
-
name: 'CodeGenerator',
|
|
680
|
-
system: codingPrompt,
|
|
681
|
-
model: createModelInstance(modelConfig?.modelConfig || modelConfig),
|
|
682
|
-
tools: [createTool({
|
|
683
|
-
name: 'createOrUpdateFiles',
|
|
684
|
-
description: 'Create or update files in the sandbox',
|
|
685
|
-
parameters: z.object({
|
|
686
|
-
files: z.array(z.object({
|
|
687
|
-
path: z.string(),
|
|
688
|
-
content: z.string()
|
|
689
|
-
}))
|
|
690
|
-
}),
|
|
691
|
-
handler: async ({
|
|
692
|
-
files
|
|
693
|
-
}) => {
|
|
694
|
-
try {
|
|
695
|
-
await sandbox.files.write(files);
|
|
696
|
-
return {
|
|
697
|
-
success: true,
|
|
698
|
-
message: `Updated ${files.length} files`
|
|
699
|
-
};
|
|
700
|
-
} catch (error) {
|
|
701
|
-
return {
|
|
702
|
-
success: false,
|
|
703
|
-
error: error.message
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}), createTool({
|
|
708
|
-
name: 'readFiles',
|
|
709
|
-
description: 'Read files from the sandbox',
|
|
710
|
-
parameters: z.object({
|
|
711
|
-
paths: z.array(z.string())
|
|
712
|
-
}),
|
|
713
|
-
handler: async ({
|
|
714
|
-
paths
|
|
715
|
-
}) => {
|
|
716
|
-
try {
|
|
717
|
-
const files = await Promise.all(paths.map(async path => {
|
|
718
|
-
try {
|
|
719
|
-
const content = await sandbox.files.read(path);
|
|
720
|
-
return {
|
|
721
|
-
path,
|
|
722
|
-
content,
|
|
723
|
-
exists: true
|
|
724
|
-
};
|
|
725
|
-
} catch (error) {
|
|
726
|
-
return {
|
|
727
|
-
path,
|
|
728
|
-
content: '',
|
|
729
|
-
exists: false,
|
|
730
|
-
error: error.message
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
}));
|
|
734
|
-
return {
|
|
735
|
-
success: true,
|
|
736
|
-
files
|
|
737
|
-
};
|
|
738
|
-
} catch (error) {
|
|
739
|
-
return {
|
|
740
|
-
success: false,
|
|
741
|
-
error: error.message
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
}), createTool({
|
|
746
|
-
name: 'terminal',
|
|
747
|
-
description: 'Execute terminal commands in the sandbox',
|
|
748
|
-
parameters: z.object({
|
|
749
|
-
command: z.string()
|
|
750
|
-
}),
|
|
751
|
-
handler: async ({
|
|
752
|
-
command
|
|
753
|
-
}) => {
|
|
754
|
-
try {
|
|
755
|
-
const result = await sandbox.process.start({
|
|
756
|
-
cmd: command
|
|
757
|
-
});
|
|
758
|
-
const output = await result.finished;
|
|
759
|
-
return {
|
|
760
|
-
success: true,
|
|
761
|
-
output: output.stdout || output.stderr,
|
|
762
|
-
exitCode: output.exitCode
|
|
763
|
-
};
|
|
764
|
-
} catch (error) {
|
|
765
|
-
return {
|
|
766
|
-
success: false,
|
|
767
|
-
error: error.message
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
})]
|
|
772
|
-
});
|
|
773
|
-
// Generate AI response using agent-kit (completely outside of step.run to avoid nesting)
|
|
774
|
-
let aiResponse;
|
|
775
|
-
try {
|
|
776
|
-
console.log('=== Starting AI response generation ===');
|
|
777
|
-
console.log('Message:', message);
|
|
778
|
-
// Generate response
|
|
779
|
-
const result = await agent.run(message);
|
|
780
|
-
// Parse the response to extract task summary and canvas layers
|
|
781
|
-
const responseContent = lastAssistantTextMessageContent(result);
|
|
782
|
-
console.log('=== AI Response Content ===');
|
|
783
|
-
console.log('Response Content:', responseContent);
|
|
784
|
-
const taskSummary = responseContent?.match(/<task_summary>([\s\S]*?)<\/task_summary>/)?.[1]?.trim();
|
|
785
|
-
const canvasLayersMatch = responseContent?.match(/<canvas_layers>([\s\S]*?)<\/canvas_layers>/)?.[1];
|
|
786
|
-
let canvasLayers = null;
|
|
787
|
-
if (canvasLayersMatch) {
|
|
788
|
-
try {
|
|
789
|
-
canvasLayers = JSON.parse(canvasLayersMatch.trim());
|
|
790
|
-
} catch (error) {
|
|
791
|
-
console.error('Failed to parse canvas layers:', error);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
aiResponse = {
|
|
795
|
-
success: true,
|
|
796
|
-
response: responseContent,
|
|
797
|
-
taskSummary,
|
|
798
|
-
canvasLayers,
|
|
799
|
-
agentResult: result
|
|
800
|
-
};
|
|
801
|
-
console.log('=== AI Response Object ===');
|
|
802
|
-
console.log('AI Response:', JSON.stringify(aiResponse, null, 2));
|
|
803
|
-
} catch (error) {
|
|
804
|
-
console.error('AI response generation failed:', error);
|
|
805
|
-
aiResponse = {
|
|
806
|
-
success: false,
|
|
807
|
-
error: error.message,
|
|
808
|
-
response: null,
|
|
809
|
-
taskSummary: null,
|
|
810
|
-
canvasLayers: null
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
// Generate sandbox URL
|
|
814
|
-
const sandboxUrl = await step.run('generate-sandbox-url', async () => {
|
|
815
|
-
try {
|
|
816
|
-
// Your domain pattern: https://3000-<sandboxId>.yarntra.ai
|
|
817
|
-
const url = `https://3000-${sandbox.sandboxId}.yarntra.ai`;
|
|
818
|
-
console.log(`Generated sandbox URL: ${url}`);
|
|
819
|
-
return url;
|
|
820
|
-
} catch (error) {
|
|
821
|
-
console.error('Failed to generate sandbox URL:', error);
|
|
822
|
-
throw new Error(`Failed to generate sandbox URL: ${error.message}`);
|
|
823
|
-
}
|
|
824
|
-
});
|
|
825
|
-
console.log('sandboxUrl', sandboxUrl);
|
|
826
|
-
// Publish to subscription
|
|
827
|
-
await step.run('publish-to-subscription', async () => {
|
|
828
|
-
try {
|
|
829
|
-
const pubsub = container.get('PubSub');
|
|
830
|
-
const responseData = {
|
|
831
|
-
projectId,
|
|
832
|
-
sandboxId: sandbox.sandboxId,
|
|
833
|
-
sandboxUrl,
|
|
834
|
-
url: sandboxUrl,
|
|
835
|
-
// Keep both for compatibility
|
|
836
|
-
aiResponse: aiResponse.success ? aiResponse.response : null,
|
|
837
|
-
taskSummary: aiResponse.success ? aiResponse.taskSummary : null,
|
|
838
|
-
canvasLayers: aiResponse.success ? aiResponse.canvasLayers : null,
|
|
839
|
-
error: aiResponse.success ? null : aiResponse.error,
|
|
840
|
-
template: normalizeTemplateName(modelConfig?.template || 'vite-react'),
|
|
841
|
-
timestamp: new Date().toISOString()
|
|
842
|
-
};
|
|
843
|
-
console.log('=== Response Data ===');
|
|
844
|
-
console.log('Response Data:', JSON.stringify(responseData, null, 2));
|
|
845
|
-
console.log('AI Response Success:', aiResponse.success);
|
|
846
|
-
// console.log('AI Response Content:', aiResponse.response);
|
|
847
|
-
// pubsub.publish(`AI_RESPONSE_GENERATED.${projectId}`, responseData);
|
|
848
|
-
const postData = {
|
|
849
|
-
...post,
|
|
850
|
-
props: {
|
|
851
|
-
...post.props,
|
|
852
|
-
generatedResponse: responseData
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
console.log('=== Publish to subscription Post Data:', JSON.stringify(postData, null, 2));
|
|
856
|
-
pubsub.publish(`POST_CREATED.${projectId}`, postData);
|
|
857
|
-
return responseData;
|
|
858
|
-
} catch (error) {
|
|
859
|
-
console.error('Failed to publish to subscription:', error);
|
|
860
|
-
throw new Error(`Failed to publish to subscription: ${error.message}`);
|
|
861
|
-
}
|
|
862
|
-
});
|
|
863
|
-
return {
|
|
864
|
-
projectId,
|
|
865
|
-
sandboxId: sandbox.sandboxId,
|
|
866
|
-
sandboxUrl,
|
|
867
|
-
url: sandboxUrl,
|
|
868
|
-
// Keep both for compatibility
|
|
869
|
-
aiResponse: aiResponse.success ? aiResponse.response : null,
|
|
870
|
-
taskSummary: aiResponse.success ? aiResponse.taskSummary : null,
|
|
871
|
-
canvasLayers: aiResponse.success ? aiResponse.canvasLayers : null,
|
|
872
|
-
error: aiResponse.success ? null : aiResponse.error,
|
|
873
|
-
template: normalizeTemplateName(modelConfig?.template || 'vite-react'),
|
|
874
|
-
timestamp: new Date().toISOString()
|
|
875
|
-
};
|
|
876
|
-
});
|
|
877
191
|
const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
878
|
-
id:
|
|
192
|
+
id: WorkflowNamespace.MESSENGER_PLATFORM_GENERATE_AI_CODE_FUNCTION
|
|
879
193
|
}, {
|
|
880
|
-
event:
|
|
194
|
+
event: WorkflowNamespace.MESSENGER_PLATFORM_GENERATE_AI_CODE_EVENT
|
|
881
195
|
}, async ({
|
|
882
196
|
event,
|
|
883
197
|
step
|
|
@@ -890,7 +204,6 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
890
204
|
} = event.data || {};
|
|
891
205
|
const postService = container.getNamed(SERVER_TYPES.PostService, TaggedType.MICROSERVICE);
|
|
892
206
|
const post = await postService.get(messageId);
|
|
893
|
-
console.log('ai-event-post', JSON.stringify(post, null, 2));
|
|
894
207
|
if (!post) {
|
|
895
208
|
throw new Error('Post not found');
|
|
896
209
|
}
|
|
@@ -919,7 +232,6 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
919
232
|
const previousMessages = event.data.activeFragmentFiles ? [] // Skip fetching previous messages when we have active fragment files
|
|
920
233
|
: await step.run('get-previous-messages', async () => {
|
|
921
234
|
const result = await postService.getPreviousMessagesByProjectId(post.props.projectId, 5);
|
|
922
|
-
console.log('previousMessages', JSON.stringify(result, null, 2));
|
|
923
235
|
// Handle error case - return empty array if getPreviousMessagesByProjectId returns an Error
|
|
924
236
|
if (result instanceof Error) {
|
|
925
237
|
console.warn('Failed to get previous messages:', result.message);
|
|
@@ -1119,21 +431,45 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
1119
431
|
// If we have activeFragmentFiles, include them in the message to the AI
|
|
1120
432
|
if (event.data.activeFragmentFiles && Object.keys(event.data.activeFragmentFiles).length > 0) {
|
|
1121
433
|
// Check if ViteVisualEditor exists and warn if it's missing for vite-react template
|
|
1122
|
-
|
|
1123
|
-
|
|
434
|
+
'src/components/ViteVisualEditor.tsx' in event.data.activeFragmentFiles;
|
|
435
|
+
modelConfig?.template === 'vite-react';
|
|
1124
436
|
enhancedInput = `${post.message}
|
|
1125
437
|
ACTIVE FRAGMENT FILES CONTEXT:
|
|
1126
438
|
The current sandbox contains the following files that you MUST include in your createOrUpdateFiles call:
|
|
1127
439
|
|
|
1128
|
-
${Object.entries(event.data.activeFragmentFiles).map(([path, content]) => `FILE: ${path}\n${typeof content === 'string' ? content.substring(0, 500) + (content.length > 500 ? '...' : '') : '[Non-string content]'}`).join('\n\n')}
|
|
1129
|
-
|
|
1130
|
-
CRITICAL INSTRUCTIONS FOR VISUAL CHANGES:
|
|
1131
|
-
- You MUST include ALL ${Object.keys(event.data.activeFragmentFiles).length} files from the active fragment files in your createOrUpdateFiles call
|
|
1132
|
-
- Apply the visual changes described above to the appropriate files
|
|
1133
|
-
- Preserve ALL existing files and their content except for the specific changes requested
|
|
1134
|
-
- Use the complete file context provided above as your current codebase state
|
|
1135
|
-
- DO NOT output only the changed file - output ALL files to rebuild the complete sandbox${isViteReact && !hasViteVisualEditor ? '\n- IMPORTANT: For vite-react template, you MUST import ViteVisualEditor from "./components/ViteVisualEditor" (it exists in the template), do NOT create a new component' : ''}`;
|
|
440
|
+
${Object.entries(event.data.activeFragmentFiles).map(([path, content]) => `FILE: ${path}\n${typeof content === 'string' ? content.substring(0, 500) + (content.length > 500 ? '...' : '') : '[Non-string content]'}`).join('\n\n')}`;
|
|
1136
441
|
}
|
|
442
|
+
// // If we have activeFragmentFiles, include them in the message to the AI
|
|
443
|
+
// if (event.data.activeFragmentFiles && Object.keys(event.data.activeFragmentFiles).length > 0) {
|
|
444
|
+
// // Check if ViteVisualEditor exists and warn if it's missing for vite-react template
|
|
445
|
+
// const hasViteVisualEditor = 'src/components/ViteVisualEditor.tsx' in event.data.activeFragmentFiles;
|
|
446
|
+
// const isViteReact = modelConfig?.template === 'vite-react';
|
|
447
|
+
// enhancedInput = `${post.message}
|
|
448
|
+
// ACTIVE FRAGMENT FILES CONTEXT:
|
|
449
|
+
// The current sandbox contains the following files that you MUST include in your createOrUpdateFiles call:
|
|
450
|
+
// ${Object.entries(event.data.activeFragmentFiles)
|
|
451
|
+
// .map(
|
|
452
|
+
// ([path, content]) =>
|
|
453
|
+
// `FILE: ${path}\n${
|
|
454
|
+
// typeof content === 'string'
|
|
455
|
+
// ? content.substring(0, 500) + (content.length > 500 ? '...' : '')
|
|
456
|
+
// : '[Non-string content]'
|
|
457
|
+
// }`,
|
|
458
|
+
// )
|
|
459
|
+
// .join('\n\n')}
|
|
460
|
+
// CRITICAL INSTRUCTIONS FOR VISUAL CHANGES:
|
|
461
|
+
// - You MUST include ALL ${
|
|
462
|
+
// Object.keys(event.data.activeFragmentFiles).length
|
|
463
|
+
// } files from the active fragment files in your createOrUpdateFiles call
|
|
464
|
+
// - Apply the visual changes described above to the appropriate files
|
|
465
|
+
// - Preserve ALL existing files and their content except for the specific changes requested
|
|
466
|
+
// - Use the complete file context provided above as your current codebase state
|
|
467
|
+
// - DO NOT output only the changed file - output ALL files to rebuild the complete sandbox${
|
|
468
|
+
// isViteReact && !hasViteVisualEditor
|
|
469
|
+
// ? '\n- IMPORTANT: For vite-react template, you MUST import ViteVisualEditor from "./components/ViteVisualEditor" (it exists in the template), do NOT create a new component'
|
|
470
|
+
// : ''
|
|
471
|
+
// }`;
|
|
472
|
+
// }
|
|
1137
473
|
const result = await network.run(enhancedInput, {
|
|
1138
474
|
state
|
|
1139
475
|
});
|
|
@@ -1188,7 +524,7 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
1188
524
|
author: post.editedBy,
|
|
1189
525
|
files: [],
|
|
1190
526
|
props: {
|
|
1191
|
-
role: AiAgentMessageRole.
|
|
527
|
+
role: AiAgentMessageRole.Assistant,
|
|
1192
528
|
projectId: post.props.projectId,
|
|
1193
529
|
template: modelConfig?.template || 'vite-react',
|
|
1194
530
|
sendNotificationWithProjectId: true,
|
|
@@ -1198,7 +534,7 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
1198
534
|
files: result.state.data.files || [],
|
|
1199
535
|
summary: result.state.data.summary || '',
|
|
1200
536
|
canvasLayers: result.state.data.canvasLayers || [],
|
|
1201
|
-
type: AiAgentMessageType.
|
|
537
|
+
type: AiAgentMessageType.Error,
|
|
1202
538
|
messageId: messageId,
|
|
1203
539
|
isError: agentError
|
|
1204
540
|
}
|
|
@@ -1213,7 +549,7 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
1213
549
|
author: post.editedBy,
|
|
1214
550
|
files: [],
|
|
1215
551
|
props: {
|
|
1216
|
-
role: AiAgentMessageRole.
|
|
552
|
+
role: AiAgentMessageRole.Assistant,
|
|
1217
553
|
projectId: post.props.projectId,
|
|
1218
554
|
template: modelConfig?.template || 'vite-react',
|
|
1219
555
|
sendNotificationWithProjectId: true,
|
|
@@ -1223,7 +559,7 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
1223
559
|
files: result.state.data.files || [],
|
|
1224
560
|
summary: result.state.data.summary || '',
|
|
1225
561
|
canvasLayers: result.state.data.canvasLayers || [],
|
|
1226
|
-
type: AiAgentMessageType.
|
|
562
|
+
type: AiAgentMessageType.Result,
|
|
1227
563
|
messageId: messageId,
|
|
1228
564
|
isError: agentError
|
|
1229
565
|
}
|
|
@@ -1242,9 +578,9 @@ const generateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
|
1242
578
|
});
|
|
1243
579
|
// Sandbox recreation function - recreates sandbox with existing files from fragment
|
|
1244
580
|
const recreateSandboxUrlFunction = (inngest, container) => inngest.createFunction({
|
|
1245
|
-
id:
|
|
581
|
+
id: WorkflowNamespace.MESSENGER_PLATFORM_RECREATE_SANDBOX_FUNCTION
|
|
1246
582
|
}, {
|
|
1247
|
-
event:
|
|
583
|
+
event: WorkflowNamespace.MESSENGER_PLATFORM_RECREATE_SANDBOX_EVENT
|
|
1248
584
|
}, async ({
|
|
1249
585
|
event,
|
|
1250
586
|
step
|
|
@@ -1319,67 +655,4 @@ const recreateSandboxUrlFunction = (inngest, container) => inngest.createFunctio
|
|
|
1319
655
|
newSandboxUrl: sandboxUrl,
|
|
1320
656
|
newSandboxId: sandboxId
|
|
1321
657
|
};
|
|
1322
|
-
});
|
|
1323
|
-
const regenerateAiCodeFunction = (inngest, container) => inngest.createFunction({
|
|
1324
|
-
id: 'regenerate-ai-code-agent'
|
|
1325
|
-
}, {
|
|
1326
|
-
event: 'regenerate-ai-code'
|
|
1327
|
-
}, async ({
|
|
1328
|
-
event,
|
|
1329
|
-
step
|
|
1330
|
-
}) => {
|
|
1331
|
-
console.log('=== REGENERATE AI CODE AGENT FUNCTION STARTED ===');
|
|
1332
|
-
console.log('Event data:', JSON.stringify(event.data, null, 2));
|
|
1333
|
-
const {
|
|
1334
|
-
messageId,
|
|
1335
|
-
modelConfig
|
|
1336
|
-
} = event.data || {};
|
|
1337
|
-
const postService = container.getNamed(SERVER_TYPES.PostService, TaggedType.MICROSERVICE);
|
|
1338
|
-
const post = await postService.get(messageId);
|
|
1339
|
-
if (!post) {
|
|
1340
|
-
throw new Error('Post not found');
|
|
1341
|
-
}
|
|
1342
|
-
const {
|
|
1343
|
-
sandboxId,
|
|
1344
|
-
sandboxUrl
|
|
1345
|
-
} = await step.run('create-sandbox-and-start-monitoring', async () => {
|
|
1346
|
-
const selectedTemplateId = getTemplateId(modelConfig);
|
|
1347
|
-
const sandbox = await Sandbox.create(selectedTemplateId, {
|
|
1348
|
-
apiKey,
|
|
1349
|
-
domain,
|
|
1350
|
-
timeoutMs: 60_000 * 20
|
|
1351
|
-
});
|
|
1352
|
-
const sandboxUrl = `https://3000-${sandbox.sandboxId}.yarntra.ai`;
|
|
1353
|
-
// Start error monitoring immediately after sandbox creation
|
|
1354
|
-
// const container = await initializeContainer();
|
|
1355
|
-
// const sandboxErrorService = container.get<ISandboxErrorService>(SERVER_TYPES.SandboxErrorService);
|
|
1356
|
-
// console.log(`Starting immediate error monitoring for sandbox ${sandbox.sandboxId}`);
|
|
1357
|
-
// await sandboxErrorService.startErrorMonitoring(post.props.projectId, sandbox.sandboxId, sandboxUrl,messageId);
|
|
1358
|
-
return {
|
|
1359
|
-
sandboxId: sandbox.sandboxId,
|
|
1360
|
-
sandboxUrl
|
|
1361
|
-
};
|
|
1362
|
-
});
|
|
1363
|
-
// Save to database using handler
|
|
1364
|
-
const messageData = await step.run('save-ai-code-result', async () => {
|
|
1365
|
-
return await postService.update(messageId, {
|
|
1366
|
-
props: {
|
|
1367
|
-
...post.props,
|
|
1368
|
-
sendNotificationWithProjectId: false,
|
|
1369
|
-
fragment: {
|
|
1370
|
-
sandboxUrl: sandboxUrl
|
|
1371
|
-
// files: result.state.data.files || [],
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
});
|
|
1375
|
-
});
|
|
1376
|
-
// Publish subscription update for real-time client updates
|
|
1377
|
-
await step.run('publish-subscription-ai-code-result', async () => {
|
|
1378
|
-
const pubsub = container.get('PubSub');
|
|
1379
|
-
console.log('=== Publish to subscription Post Data:', JSON.stringify(messageData, null, 2));
|
|
1380
|
-
pubsub.publish(`POST_CREATED.${post.props.projectId}`, messageData);
|
|
1381
|
-
});
|
|
1382
|
-
// Error monitoring was already started immediately after sandbox creation
|
|
1383
|
-
// No need to start it again here
|
|
1384
|
-
return messageData;
|
|
1385
|
-
});export{codeAgentFunction,createChannelWithProjectId,generateAIResponseWithSandbox,generateAiCodeFunction,recreateSandboxUrlFunction,regenerateAiCodeFunction,sendMessageHandler};//# sourceMappingURL=functions.js.map
|
|
658
|
+
});export{createChannelWithProjectId,generateAiCodeFunction,recreateSandboxUrlFunction,sendMessageHandler};//# sourceMappingURL=functions.js.map
|