@xuda.io/ai_module 1.1.5617 → 1.1.5619
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/.env +1 -0
- package/.sitework/boaz-wiki/assets/boaz-avrahami.png +0 -0
- package/.sitework/boaz-wiki/assets/favicon-180.png +0 -0
- package/.sitework/boaz-wiki/assets/favicon-32.png +0 -0
- package/.sitework/boaz-wiki/assets/favicon.ico +0 -0
- package/.sitework/boaz-wiki/assets/site.js +47 -0
- package/.sitework/boaz-wiki/assets/styles.css +563 -0
- package/.sitework/boaz-wiki/index.html +847 -0
- package/.tmp_boaz_site/assets/boaz-avrahami.png +0 -0
- package/.tmp_boaz_site/assets/site.js +47 -0
- package/.tmp_boaz_site/assets/styles.css +278 -0
- package/.tmp_boaz_site/index.html +845 -0
- package/build_boaz_wiki.py +601 -0
- package/globals.mjs +6 -0
- package/gpt.mjs +155 -0
- package/index copy 2.js +2129 -0
- package/index copy.js +1455 -0
- package/index old.mjs +423 -0
- package/index.js +3751 -0
- package/index_ms.mjs +8 -8
- package/index_msa.mjs +12 -8
- package/package.json +1 -1
- package/test.mjs +2 -0
- package/un-used.mjs +1437 -0
package/index.js
ADDED
|
@@ -0,0 +1,3751 @@
|
|
|
1
|
+
console.log('AI Module loaded...');
|
|
2
|
+
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
7
|
+
import sharp from 'sharp';
|
|
8
|
+
|
|
9
|
+
import puppeteer from 'puppeteer-extra';
|
|
10
|
+
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
|
11
|
+
import youtubedl from 'youtube-dl-exec';
|
|
12
|
+
|
|
13
|
+
import crypto from 'node:crypto';
|
|
14
|
+
|
|
15
|
+
// import { VM } from 'vm2';
|
|
16
|
+
import { NodeVM, VMScript } from 'vm2';
|
|
17
|
+
|
|
18
|
+
puppeteer.use(StealthPlugin());
|
|
19
|
+
import process from 'process';
|
|
20
|
+
import { File } from 'node:buffer'; // Node 20+
|
|
21
|
+
const { readFile, unlink, writeFile } = fs.promises;
|
|
22
|
+
|
|
23
|
+
// global._common = JSON.parse(await fs.readFile(path.join(process.env.XUDA_HOME, 'common', 'xuda_node_common.js'), 'utf-8'));
|
|
24
|
+
global._conf = JSON.parse(await readFile(path.join(process.env.XUDA_HOME, process.env.XUDA_CONFIG), 'utf-8'));
|
|
25
|
+
|
|
26
|
+
const module_path = path.join(process.env.XUDA_HOME, 'cpi') + (!_conf.is_debug ? '/node_modules/@xuda.io' : '');
|
|
27
|
+
const runtime_modules_path = path.join(process.env.XUDA_HOME, 'dist', 'runtime', 'js', 'modules');
|
|
28
|
+
|
|
29
|
+
import { Agent, Runner, tool, setDefaultOpenAIKey, RunContext, fileSearchTool, webSearchTool, hostedMcpTool } from '@openai/agents';
|
|
30
|
+
import OpenAI, { toFile } from 'openai';
|
|
31
|
+
|
|
32
|
+
setDefaultOpenAIKey(_conf.OPENAI_API_KEY);
|
|
33
|
+
|
|
34
|
+
import _ from 'lodash';
|
|
35
|
+
import JSON5 from 'json5';
|
|
36
|
+
import UglifyJS from 'uglify-js';
|
|
37
|
+
|
|
38
|
+
global._ = _;
|
|
39
|
+
|
|
40
|
+
const { get_xuda_couch } = await import(`${module_path}/db_module/index.es.mjs`);
|
|
41
|
+
const { createDoc, valid_menuType } = await import(`${runtime_modules_path}/xuda-studio-doc-utils.mjs`);
|
|
42
|
+
const { xudaPrase } = await import(`${runtime_modules_path}/xuda-cli-plugin-html-parser-module.esm.mjs`);
|
|
43
|
+
const { cast } = await import(`${runtime_modules_path}/xuda-get-cast-util-module.mjs`);
|
|
44
|
+
|
|
45
|
+
const { init, check, check_structure, get_zod_schema } = await import(`${runtime_modules_path}/xuda-studio-checker.mjs`);
|
|
46
|
+
|
|
47
|
+
let client;
|
|
48
|
+
try {
|
|
49
|
+
client = new OpenAI({
|
|
50
|
+
apiKey: _conf.OPENAI_API_KEY,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
console.log('chat-gpt client initiated..OK');
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.error(err);
|
|
56
|
+
}
|
|
57
|
+
const model = 'gpt-5-nano'; //'gpt-5'; // 'ft:gpt-4o-2024-08-06:xuda-io:xuda:CC4aHCsz';
|
|
58
|
+
|
|
59
|
+
var studio_units = {};
|
|
60
|
+
|
|
61
|
+
const init_studio_units = async function () {
|
|
62
|
+
try {
|
|
63
|
+
const db = get_xuda_couch('xuda_website');
|
|
64
|
+
|
|
65
|
+
const ret = await db.find({ selector: { type: 'app-builder', docType: 'feature' } });
|
|
66
|
+
for (const doc of ret.docs) {
|
|
67
|
+
studio_units[doc._id.replace('-', '_')] = doc;
|
|
68
|
+
}
|
|
69
|
+
// console.log(studio_units);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error(err);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
await init_studio_units();
|
|
75
|
+
|
|
76
|
+
const get_studio_table_info_tool = tool({
|
|
77
|
+
name: 'Get studio table info',
|
|
78
|
+
description: 'Get the table doc by table_name',
|
|
79
|
+
parameters: z.object({
|
|
80
|
+
table: z.string(),
|
|
81
|
+
}),
|
|
82
|
+
async execute(e, RunContext) {
|
|
83
|
+
const { table } = e;
|
|
84
|
+
const { app_id, uid } = RunContext.context;
|
|
85
|
+
debugger;
|
|
86
|
+
if (!table) throw new Error('got null in the table parameter');
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const project_db = await get_project_db(app_id);
|
|
90
|
+
let tables = await project_db.view('xuda', 'studio_docs_by_type', { key: 'table' });
|
|
91
|
+
let response = '';
|
|
92
|
+
|
|
93
|
+
for (const doc of tables.rows) {
|
|
94
|
+
const _menuName = doc?.value?.properties?.menuName?.toLowerCase();
|
|
95
|
+
if (_menuName && _menuName === table.toLowerCase()) {
|
|
96
|
+
try {
|
|
97
|
+
response = await project_db.get(doc.id);
|
|
98
|
+
break;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new Error(`table doc ${doc.id} not found`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (_.isEmpty(response)) {
|
|
106
|
+
for (const doc of tables.rows) {
|
|
107
|
+
const _menuName = doc?.value?.properties?.menuName?.toLowerCase();
|
|
108
|
+
if (_menuName && (_menuName.includes(table.toLowerCase()) || _menuName.includes(table.replace('_', ' ').toLowerCase()))) {
|
|
109
|
+
response += `could not found ${table} but found ${_menuName} instead. `;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (_.isEmpty(response)) {
|
|
115
|
+
throw new Error(`table ${table} not found`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return response;
|
|
119
|
+
} catch (err) {
|
|
120
|
+
return err.message;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const save_doc_tool = tool({
|
|
126
|
+
name: 'Save Doc',
|
|
127
|
+
description: 'Save doc (such as table,component,set_data,get_data,alert,batch,globals,route,database) to the project to db',
|
|
128
|
+
parameters: z.object({
|
|
129
|
+
// app_id: z.string(),
|
|
130
|
+
doc: z.string(), // z.object({}), // _id
|
|
131
|
+
}),
|
|
132
|
+
async execute(e, RunContext) {
|
|
133
|
+
const { doc } = e;
|
|
134
|
+
const { app_id, uid } = RunContext.context;
|
|
135
|
+
try {
|
|
136
|
+
const doc_obj = JSON5.parse(doc);
|
|
137
|
+
const project_db = await get_project_db(app_id);
|
|
138
|
+
const result = await project_db.insert(doc_obj);
|
|
139
|
+
console.log('save_doc_tool >>>> ', result);
|
|
140
|
+
return result;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error(err);
|
|
143
|
+
return err.message;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const delete_doc_tool = tool({
|
|
149
|
+
name: 'Delete Doc',
|
|
150
|
+
description: 'Delete doc using _id',
|
|
151
|
+
parameters: z.object({
|
|
152
|
+
_id: z.string(),
|
|
153
|
+
}),
|
|
154
|
+
async execute(e, RunContext) {
|
|
155
|
+
const { _id } = e;
|
|
156
|
+
const { app_id, uid } = RunContext.context;
|
|
157
|
+
try {
|
|
158
|
+
const project_db = await get_project_db(app_id);
|
|
159
|
+
let doc = await project_db.get(_id);
|
|
160
|
+
doc.stat = 4;
|
|
161
|
+
doc.ts = Date.now();
|
|
162
|
+
const result = await project_db.insert(doc);
|
|
163
|
+
console.log('delete doc >>>> ', result);
|
|
164
|
+
return result;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.error(err);
|
|
167
|
+
return err.message;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const fetch_data_tool = tool({
|
|
173
|
+
name: 'Fetch Table Data',
|
|
174
|
+
description: 'this tool perform query find base on couchdb mango syntax',
|
|
175
|
+
parameters: z.object({
|
|
176
|
+
query: z.string(),
|
|
177
|
+
}),
|
|
178
|
+
async execute(e, RunContext) {
|
|
179
|
+
const { query } = e;
|
|
180
|
+
const { app_id, uid, table_id } = RunContext.context;
|
|
181
|
+
try {
|
|
182
|
+
const query_obj = JSON5.parse(query);
|
|
183
|
+
console.log('query_obj', query_obj);
|
|
184
|
+
const project_db = await get_project_db(app_id);
|
|
185
|
+
let table_doc;
|
|
186
|
+
try {
|
|
187
|
+
table_doc = await project_db.get(table_id);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
throw new Error(`table doc ${table_id} not found`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function validateQueryAgainstTable(query, tableFields) {
|
|
193
|
+
const errors = [];
|
|
194
|
+
const validFieldIds = new Set((tableFields || []).map((f) => f.data?.field_id).filter(Boolean));
|
|
195
|
+
|
|
196
|
+
const selector = query.selector || {};
|
|
197
|
+
if (selector.docType !== 'database') errors.push('selector.docType must be "database"');
|
|
198
|
+
if (selector.stat !== 3) errors.push('selector.stat must be 3');
|
|
199
|
+
|
|
200
|
+
const validate = (path, ctx) => {
|
|
201
|
+
if (!path.startsWith('udfData.data.')) {
|
|
202
|
+
errors.push(`Invalid field path in ${ctx}: "${path}" — must start with "udfData.data."`);
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const id = path.slice(13);
|
|
206
|
+
if (!validFieldIds.has(id)) {
|
|
207
|
+
errors.push(`Field "${path}" not defined in table`);
|
|
208
|
+
}
|
|
209
|
+
return id;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const walk = (obj, p = '') => {
|
|
213
|
+
if (!obj || typeof obj !== 'object') return;
|
|
214
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
215
|
+
const fp = p ? `${p}.${k}` : k;
|
|
216
|
+
if (!k.startsWith('$') && fp.startsWith('udfData.data.')) validate(fp, 'selector');
|
|
217
|
+
if (v && typeof v === 'object') Array.isArray(v) ? v.forEach((i) => walk(i, fp)) : walk(v, fp);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
walk(selector);
|
|
222
|
+
(query.fields || []).forEach((f) => validate(f, 'fields'));
|
|
223
|
+
(query.sort || []).forEach((s) => Object.entries(s).forEach(([f]) => validate(f, 'sort')));
|
|
224
|
+
|
|
225
|
+
return { isValid: errors.length === 0, errors };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const validateQuery_result = validateQueryAgainstTable(query_obj, table_doc.tableFields);
|
|
229
|
+
debugger;
|
|
230
|
+
if (!validateQuery_result.isValid) {
|
|
231
|
+
// console.error('Validation failed:', validateQuery_result.errors);
|
|
232
|
+
throw new Error(`Validation failed: ${JSON.stringify(validateQuery_result.errors)}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const result = await project_db.find(query_obj);
|
|
236
|
+
console.log('Fetch Table Data >>>> ', result);
|
|
237
|
+
return result;
|
|
238
|
+
} catch (err) {
|
|
239
|
+
// console.error(err);
|
|
240
|
+
return err.message;
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const create_table_management_tool = tool({
|
|
246
|
+
name: 'Create Table Management Program',
|
|
247
|
+
description: 'this tool create table management program',
|
|
248
|
+
parameters: z.object({}),
|
|
249
|
+
async execute(e, RunContext) {
|
|
250
|
+
const { app_id, uid, table_id, userName } = RunContext.context;
|
|
251
|
+
try {
|
|
252
|
+
const project_db = await get_project_db(app_id);
|
|
253
|
+
let table_doc;
|
|
254
|
+
try {
|
|
255
|
+
table_doc = await project_db.get(table_id);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
throw new Error(`table doc ${table_id} not found`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const menuType = 'component';
|
|
261
|
+
const parentId = 'programs';
|
|
262
|
+
const checkedInUserName = userName;
|
|
263
|
+
const name = `${table_doc.properties.menuName} ${table_doc.properties.menuName.includes('table') ? '' : 'table '}Management Program`;
|
|
264
|
+
const doc = createDoc({ uid, properties: { menuType, menuName: name, menuTitle: name }, checkedInUserName, app_id, parentId });
|
|
265
|
+
|
|
266
|
+
const progUi = [
|
|
267
|
+
{
|
|
268
|
+
id: 'root',
|
|
269
|
+
type: 'element',
|
|
270
|
+
tagName: 'xu-multi-view',
|
|
271
|
+
attributes: {},
|
|
272
|
+
children: [
|
|
273
|
+
{
|
|
274
|
+
type: 'element',
|
|
275
|
+
tagName: 'div',
|
|
276
|
+
attributes: {},
|
|
277
|
+
children: [
|
|
278
|
+
{
|
|
279
|
+
type: 'element',
|
|
280
|
+
tagName: 'xu-widget',
|
|
281
|
+
attributes: {
|
|
282
|
+
'xu-widget': '@xuda.io/xuda-widget-plugin-ag-grid',
|
|
283
|
+
'xu-method': '_default',
|
|
284
|
+
},
|
|
285
|
+
children: [],
|
|
286
|
+
id: `node-${crypto.randomUUID()}`,
|
|
287
|
+
path: [0, 0, 0],
|
|
288
|
+
$folded: false,
|
|
289
|
+
code: '<xu-widget xu-widget="@xuda.io/xuda-widget-plugin-ag-grid" xu-method="_default"></xu-widget>',
|
|
290
|
+
editCode: false,
|
|
291
|
+
$childrenClass: 'active',
|
|
292
|
+
$nodeBackClass: 'selected-tree rounded-lg',
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
id: `node-${crypto.randomUUID()}`,
|
|
296
|
+
path: [0, 0],
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
path: [0],
|
|
300
|
+
$folded: false,
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
|
|
304
|
+
const progDataSource = {
|
|
305
|
+
dataSourceType: 'table',
|
|
306
|
+
dataSourceFilterModelType: 'index',
|
|
307
|
+
dataSourceSort: 'asc',
|
|
308
|
+
dataSourceTableId: table_id,
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
doc.studio_meta.miniApp = true;
|
|
312
|
+
doc.progUi = progUi;
|
|
313
|
+
doc.progDataSource = progDataSource;
|
|
314
|
+
doc.progFields = [];
|
|
315
|
+
|
|
316
|
+
for (const field of table_doc.tableFields || []) {
|
|
317
|
+
const field_id = field.data.field_id;
|
|
318
|
+
const fieldType = field.props.fieldType;
|
|
319
|
+
|
|
320
|
+
doc.progFields.push({
|
|
321
|
+
id: Date.now(),
|
|
322
|
+
data: {
|
|
323
|
+
type: 'table',
|
|
324
|
+
field_id,
|
|
325
|
+
},
|
|
326
|
+
props: {
|
|
327
|
+
fieldType,
|
|
328
|
+
propExpressions: {},
|
|
329
|
+
},
|
|
330
|
+
workflow: [],
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const result = await project_db.insert(doc);
|
|
335
|
+
|
|
336
|
+
update_thumbnail('mini_app', doc, uid);
|
|
337
|
+
|
|
338
|
+
console.log('Create Table Management Component >>>> ', result);
|
|
339
|
+
return result;
|
|
340
|
+
} catch (err) {
|
|
341
|
+
// console.error(err);
|
|
342
|
+
return err.message;
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const create_landing_page_tool = tool({
|
|
348
|
+
name: 'Create Landing Page Program',
|
|
349
|
+
description: 'this tool create landing page by subject',
|
|
350
|
+
parameters: z.object({ subject: z.string().describe('describe the landing page in short') }),
|
|
351
|
+
async execute(e, RunContext) {
|
|
352
|
+
const { subject } = e;
|
|
353
|
+
const { app_id, uid, table_id, userName, app_obj, account_info } = RunContext.context;
|
|
354
|
+
try {
|
|
355
|
+
const project_db = await get_project_db(app_id);
|
|
356
|
+
let table_doc;
|
|
357
|
+
try {
|
|
358
|
+
table_doc = await project_db.get(table_id);
|
|
359
|
+
} catch (error) {
|
|
360
|
+
throw new Error(`table doc ${table_id} not found`);
|
|
361
|
+
}
|
|
362
|
+
debugger;
|
|
363
|
+
const tableIndexObj = table_doc?.tableIndexes?.[0];
|
|
364
|
+
|
|
365
|
+
if (_.isEmpty(tableIndexObj)) {
|
|
366
|
+
throw new Error(`table have at least one index`);
|
|
367
|
+
}
|
|
368
|
+
const indexSegmentKey = tableIndexObj?.data?.keys?.[0];
|
|
369
|
+
|
|
370
|
+
if (!indexSegmentKey) {
|
|
371
|
+
throw new Error(`key segment is empty for the main index`);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const dataSourceIndexId = tableIndexObj?.id;
|
|
375
|
+
|
|
376
|
+
if (!dataSourceIndexId) {
|
|
377
|
+
throw new Error(`table have at least one index id`);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const title_ret = await submit_chat_gpt_prompt({
|
|
381
|
+
prompt: `create title for ${subject} return 5 words result maximum text only without options `,
|
|
382
|
+
model: 'gpt-5-nano',
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const menuType = 'component';
|
|
386
|
+
const parentId = 'programs';
|
|
387
|
+
const checkedInUserName = userName;
|
|
388
|
+
const name = `${title_ret.data} Landing Page`;
|
|
389
|
+
const properties = { menuType, menuName: name, menuTitle: name, rwMode: 'U', uiFramework: '@xuda.io/xuda-framework-plugin-tailwind', renderType: 'form', allowCreate: true };
|
|
390
|
+
const doc = createDoc({ uid, properties, checkedInUserName, app_id, parentId });
|
|
391
|
+
|
|
392
|
+
const progDataSource = {
|
|
393
|
+
dataSourceType: 'table',
|
|
394
|
+
dataSourceFilterModelType: 'index',
|
|
395
|
+
dataSourceSort: 'asc',
|
|
396
|
+
dataSourceTableId: table_id,
|
|
397
|
+
dataSourceIndexId,
|
|
398
|
+
dataSourceIndexesObj: {
|
|
399
|
+
[dataSourceIndexId]: {
|
|
400
|
+
[indexSegmentKey]: {
|
|
401
|
+
from: 'Date.now()',
|
|
402
|
+
locate_from: '',
|
|
403
|
+
locate_to: '',
|
|
404
|
+
to: 'Date.now()',
|
|
405
|
+
key: indexSegmentKey,
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
doc.studio_meta.miniApp = true;
|
|
412
|
+
|
|
413
|
+
doc.progDataSource = progDataSource;
|
|
414
|
+
doc.progFields = [];
|
|
415
|
+
|
|
416
|
+
let contactFormFields = [];
|
|
417
|
+
|
|
418
|
+
for (const field of table_doc.tableFields || []) {
|
|
419
|
+
const field_id = field.data.field_id;
|
|
420
|
+
const fieldType = field.props.fieldType;
|
|
421
|
+
contactFormFields.push({ field_id, fieldType });
|
|
422
|
+
doc.progFields.push({
|
|
423
|
+
id: Date.now(),
|
|
424
|
+
data: {
|
|
425
|
+
type: 'table',
|
|
426
|
+
field_id,
|
|
427
|
+
},
|
|
428
|
+
props: {
|
|
429
|
+
fieldType,
|
|
430
|
+
propExpressions: {},
|
|
431
|
+
},
|
|
432
|
+
workflow: [],
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const html_ret = await submit_chat_gpt_prompt({
|
|
437
|
+
prompt: `Create a single-page landing layout with a Tailwind-styled contact form (fields: ${JSON.stringify(contactFormFields)}, subject: ${subject}). No navigation. Return only the <body> content. contact info:${JSON.stringify(account_info)} ${JSON.stringify(app_obj.app_general_prop)}`,
|
|
438
|
+
model: 'gpt-5-nano',
|
|
439
|
+
});
|
|
440
|
+
debugger;
|
|
441
|
+
const progUi = xudaPrase(html_ret.data);
|
|
442
|
+
doc.progUi = progUi;
|
|
443
|
+
doc.html = html_ret.data;
|
|
444
|
+
|
|
445
|
+
const result = await project_db.insert(doc);
|
|
446
|
+
|
|
447
|
+
update_thumbnail('mini_app', doc, uid);
|
|
448
|
+
|
|
449
|
+
console.log('Create Landing Page >>>> ', result);
|
|
450
|
+
return result;
|
|
451
|
+
} catch (err) {
|
|
452
|
+
// console.error(err);
|
|
453
|
+
return err.message;
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
const create_index_tool = tool({
|
|
459
|
+
name: 'Create Index',
|
|
460
|
+
description: 'this tool creating index',
|
|
461
|
+
parameters: z.object({
|
|
462
|
+
index: z.string(),
|
|
463
|
+
}),
|
|
464
|
+
async execute(e, RunContext) {
|
|
465
|
+
const { index } = e;
|
|
466
|
+
const { app_id, uid } = RunContext.context;
|
|
467
|
+
try {
|
|
468
|
+
debugger;
|
|
469
|
+
const index_obj = JSON5.parse(index);
|
|
470
|
+
console.log('Create Index', index_obj);
|
|
471
|
+
const project_db = await get_project_db(app_id);
|
|
472
|
+
console.log('Create Index >>>> ', index);
|
|
473
|
+
const result = await project_db.createIndex(index_obj);
|
|
474
|
+
|
|
475
|
+
return result;
|
|
476
|
+
} catch (err) {
|
|
477
|
+
console.error(err);
|
|
478
|
+
return err.message;
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
const get_studio_table_info_by_table_id_tool = tool({
|
|
484
|
+
name: 'Get Studio Table Info',
|
|
485
|
+
description: 'Get the table doc by table_id',
|
|
486
|
+
parameters: z.object({
|
|
487
|
+
// table_id: z.string(),
|
|
488
|
+
}),
|
|
489
|
+
async execute(e, RunContext) {
|
|
490
|
+
const {} = e;
|
|
491
|
+
const { app_id, uid, table_id } = RunContext.context;
|
|
492
|
+
|
|
493
|
+
console.log('Get studio table info by table id:', table_id);
|
|
494
|
+
if (!table_id) throw new Error('got null in the table_id parameter');
|
|
495
|
+
|
|
496
|
+
try {
|
|
497
|
+
const project_db = await get_project_db(app_id);
|
|
498
|
+
|
|
499
|
+
let response = '';
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
response = await project_db.get(table_id);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
throw new Error(`table doc ${table_id} not found`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (_.isEmpty(response)) {
|
|
508
|
+
throw new Error(`table with table_id ${table_id} not found`);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return { tableFields: response.tableFields, tableIndexes: response.tableIndexes };
|
|
512
|
+
} catch (err) {
|
|
513
|
+
console.error('Get studio table info by table id:', table_id, err.message);
|
|
514
|
+
return err.message;
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
const get_database_doc_template_tool = tool({
|
|
520
|
+
name: 'Get Database Doc Template',
|
|
521
|
+
description: 'Get the table doc by table_id',
|
|
522
|
+
parameters: z.object({}),
|
|
523
|
+
async execute(e, RunContext) {
|
|
524
|
+
const {} = e;
|
|
525
|
+
const { app_id, uid, table_id } = RunContext.context;
|
|
526
|
+
|
|
527
|
+
console.log('Get Database Doc Template by table id:', table_id);
|
|
528
|
+
if (!table_id) throw new Error('got null in the table_id parameter');
|
|
529
|
+
|
|
530
|
+
try {
|
|
531
|
+
const project_db = await get_project_db(app_id);
|
|
532
|
+
|
|
533
|
+
let response = '';
|
|
534
|
+
|
|
535
|
+
try {
|
|
536
|
+
response = await project_db.get(table_id);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
throw new Error(`table doc ${table_id} not found`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (_.isEmpty(response)) {
|
|
542
|
+
throw new Error(`table with table_id ${table_id} not found`);
|
|
543
|
+
}
|
|
544
|
+
const t = Date.now();
|
|
545
|
+
|
|
546
|
+
const doc = `
|
|
547
|
+
|
|
548
|
+
{
|
|
549
|
+
"_id": "dbs-${crypto.randomUUID()}",
|
|
550
|
+
"docType": "database",
|
|
551
|
+
"stat": 3,
|
|
552
|
+
"user": "${uid}",
|
|
553
|
+
"app_id": "${app_id}",
|
|
554
|
+
"udfData": {
|
|
555
|
+
"udffileid": "${table_id}",
|
|
556
|
+
"data": {
|
|
557
|
+
"PG_Id": "1675955407146",
|
|
558
|
+
"PG_BK_Id": "1629987722344",
|
|
559
|
+
"PG_Page_Number": 5,
|
|
560
|
+
"PG_Chapter": "",
|
|
561
|
+
"PG_Base_Url": "",
|
|
562
|
+
"PG_PR_Id": 1675955409275,
|
|
563
|
+
"PG_Chapter_Name": ""
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
"ts": ${t},
|
|
567
|
+
"udfIndex": [
|
|
568
|
+
{
|
|
569
|
+
"fileId": "${table_id}",
|
|
570
|
+
"indexId": "1629098020301",
|
|
571
|
+
"keySegments": [ "PG_BK_Id","PG_Page_Number"],
|
|
572
|
+
"keyValue": [
|
|
573
|
+
"1629987722344",
|
|
574
|
+
5
|
|
575
|
+
]
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"fileId": "${table_id}",
|
|
579
|
+
"indexId": "1629968420532",
|
|
580
|
+
"keySegments": ["PG_Id"],
|
|
581
|
+
"keyValue": [
|
|
582
|
+
"1675955407146"
|
|
583
|
+
]
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
"fileId": "${table_id}",
|
|
587
|
+
"indexId": "1629969152311",
|
|
588
|
+
"keySegments": ["PG_BK_Id"],
|
|
589
|
+
"keyValue": [
|
|
590
|
+
"1629987722344"
|
|
591
|
+
]
|
|
592
|
+
}
|
|
593
|
+
]
|
|
594
|
+
}
|
|
595
|
+
`;
|
|
596
|
+
|
|
597
|
+
return doc;
|
|
598
|
+
} catch (err) {
|
|
599
|
+
console.error('Get studio table info by table id:', table_id, err.message);
|
|
600
|
+
return err.message;
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// const add_data_to_table_tool = tool({
|
|
606
|
+
// name: 'Add data to table tool',
|
|
607
|
+
// description: 'Add data to a table by receiving a table_id and a data object containing key–value pairs representing the fields and their values.',
|
|
608
|
+
// parameters: z.object({
|
|
609
|
+
// table_id: z.string(),
|
|
610
|
+
// }),
|
|
611
|
+
// async execute(e, RunContext) {
|
|
612
|
+
// const { table_id } = e;
|
|
613
|
+
// const { app_id, uid } = RunContext.context;
|
|
614
|
+
|
|
615
|
+
// if (!table_id) throw new Error('got null in the table_id parameter');
|
|
616
|
+
|
|
617
|
+
// try {
|
|
618
|
+
// const project_db = await get_project_db(app_id);
|
|
619
|
+
|
|
620
|
+
// let response = '';
|
|
621
|
+
|
|
622
|
+
// try {
|
|
623
|
+
// response = await project_db.get(table_id);
|
|
624
|
+
// } catch (error) {
|
|
625
|
+
// throw new Error(`table doc ${table_id} not found`);
|
|
626
|
+
// }
|
|
627
|
+
|
|
628
|
+
// if (_.isEmpty(response)) {
|
|
629
|
+
// throw new Error(`table with table_id ${table_id} not found`);
|
|
630
|
+
// }
|
|
631
|
+
|
|
632
|
+
// return response;
|
|
633
|
+
// } catch (err) {
|
|
634
|
+
// return err.message;
|
|
635
|
+
// }
|
|
636
|
+
// },
|
|
637
|
+
// });
|
|
638
|
+
|
|
639
|
+
// let table_tools = {};
|
|
640
|
+
// const build_studio_table_tools = async function (app_id) {
|
|
641
|
+
// try {
|
|
642
|
+
// if (!table_tools[app_id]) {
|
|
643
|
+
// table_tools[app_id] = {};
|
|
644
|
+
// }
|
|
645
|
+
|
|
646
|
+
// const project_db = await get_project_db(app_id);
|
|
647
|
+
// let tables = await project_db.view('xuda', 'studio_docs_by_type', { key: 'table' });
|
|
648
|
+
|
|
649
|
+
// for (const table_header_doc of tables.rows) {
|
|
650
|
+
// try {
|
|
651
|
+
// const doc = await project_db.get(table_header_doc.id);
|
|
652
|
+
|
|
653
|
+
// let data_parameters = {};
|
|
654
|
+
|
|
655
|
+
// for (const field of doc.tableFields || []) {
|
|
656
|
+
// const fieldId = field.data.field_id;
|
|
657
|
+
// const fieldType = field.props.fieldType;
|
|
658
|
+
// const mandatory = field.props.mandatory;
|
|
659
|
+
// const comment = field.props.fieldComment;
|
|
660
|
+
|
|
661
|
+
// let zodType;
|
|
662
|
+
// switch (fieldType) {
|
|
663
|
+
// case 'string':
|
|
664
|
+
// zodType = z.string();
|
|
665
|
+
// break;
|
|
666
|
+
// case 'number':
|
|
667
|
+
// zodType = z.number();
|
|
668
|
+
// break;
|
|
669
|
+
// case 'array':
|
|
670
|
+
// zodType = z.array(z.any());
|
|
671
|
+
// break;
|
|
672
|
+
// case 'object':
|
|
673
|
+
// zodType = z.object({});
|
|
674
|
+
// break;
|
|
675
|
+
// default:
|
|
676
|
+
// zodType = z.any();
|
|
677
|
+
// }
|
|
678
|
+
|
|
679
|
+
// // Add description if comment exists
|
|
680
|
+
// if (comment) {
|
|
681
|
+
// zodType = zodType.describe(comment);
|
|
682
|
+
// }
|
|
683
|
+
|
|
684
|
+
// // Make optional if not mandatory
|
|
685
|
+
// if (!mandatory) {
|
|
686
|
+
// zodType = zodType.optional();
|
|
687
|
+
// }
|
|
688
|
+
|
|
689
|
+
// data_parameters[fieldId] = zodType;
|
|
690
|
+
// }
|
|
691
|
+
|
|
692
|
+
// const _tool = {
|
|
693
|
+
// name: `add data to table tool`,
|
|
694
|
+
// description: `Add data to ${doc.properties.menuName} table by receiving key–value pairs representing the fields and their values.`,
|
|
695
|
+
// parameters: z.object(data_parameters),
|
|
696
|
+
// // parameters: z.object({ ...{ table_id: z.string() }, ...data_parameters }),
|
|
697
|
+
// // parameters: z.object({
|
|
698
|
+
// // table_id: z.string(),
|
|
699
|
+
// // data: z.object(data_parameters),
|
|
700
|
+
// // }),
|
|
701
|
+
// async execute(e, RunContext) {
|
|
702
|
+
// debugger;
|
|
703
|
+
// const {} = e;
|
|
704
|
+
// const { app_id, uid, table_id } = RunContext.context;
|
|
705
|
+
// let response = '';
|
|
706
|
+
// try {
|
|
707
|
+
// if (!table_id) throw new Error('got null in the table_id parameter');
|
|
708
|
+
|
|
709
|
+
// const FlexibleDatabaseSchema = z.object({
|
|
710
|
+
// _id: z.string(),
|
|
711
|
+
// _rev: z.string(),
|
|
712
|
+
// docType: z.literal('database'),
|
|
713
|
+
// stat: z.number(),
|
|
714
|
+
// udfData: z.object({
|
|
715
|
+
// udffileid: z.string(),
|
|
716
|
+
// data: z.record(z.union([z.string(), z.number()])), // Any key-value pairs
|
|
717
|
+
// error: z.object({}).optional(),
|
|
718
|
+
// }),
|
|
719
|
+
// ts: z.number(),
|
|
720
|
+
// date: z.number(),
|
|
721
|
+
// udfIndex: z.array(
|
|
722
|
+
// z.object({
|
|
723
|
+
// fileId: z.string(),
|
|
724
|
+
// indexId: z.string(),
|
|
725
|
+
// keySegments: z.array(z.any()),
|
|
726
|
+
// keyValue: z.array(z.any()),
|
|
727
|
+
// }),
|
|
728
|
+
// ),
|
|
729
|
+
// });
|
|
730
|
+
|
|
731
|
+
// // for (const [key, val] of Object.entities(data || {})) {
|
|
732
|
+
// // }
|
|
733
|
+
|
|
734
|
+
// let udfIndex = [];
|
|
735
|
+
|
|
736
|
+
// for (const [key, val] of Object.entities(doc.tableIndexes || [])) {
|
|
737
|
+
// let keyValue = [];
|
|
738
|
+
// for (const key_val of val?.data?.keys || []) {
|
|
739
|
+
// keyValue.push(data[key_val]);
|
|
740
|
+
// }
|
|
741
|
+
|
|
742
|
+
// udfIndex.push({
|
|
743
|
+
// fileId: table_id,
|
|
744
|
+
// indexId: val.id,
|
|
745
|
+
// keySegments: val?.data?.keys || [],
|
|
746
|
+
// keyValue,
|
|
747
|
+
// });
|
|
748
|
+
// }
|
|
749
|
+
// const t = Date.now();
|
|
750
|
+
// let table_obj = {
|
|
751
|
+
// _id: 'dbs-' + crypto.randomUUID(),
|
|
752
|
+
|
|
753
|
+
// docType: 'database',
|
|
754
|
+
// stat: 3,
|
|
755
|
+
// udfData: {
|
|
756
|
+
// udffileid: table_id,
|
|
757
|
+
// data: e,
|
|
758
|
+
// error: {},
|
|
759
|
+
// },
|
|
760
|
+
// ts: t,
|
|
761
|
+
// date: t,
|
|
762
|
+
// udfIndex,
|
|
763
|
+
// };
|
|
764
|
+
|
|
765
|
+
// return response;
|
|
766
|
+
// } catch (err) {
|
|
767
|
+
// return err.message;
|
|
768
|
+
// }
|
|
769
|
+
// },
|
|
770
|
+
// };
|
|
771
|
+
|
|
772
|
+
// // const jsonSchema = zodToJsonSchema(_tool.parameters, 'UserSchema');
|
|
773
|
+
|
|
774
|
+
// // console.log(JSON.stringify(jsonSchema, null, 2));
|
|
775
|
+
|
|
776
|
+
// table_tools[app_id][doc._id] = tool(_tool);
|
|
777
|
+
// } catch (error) {
|
|
778
|
+
// throw new Error(`table doc ${doc.id} not found`);
|
|
779
|
+
// }
|
|
780
|
+
// }
|
|
781
|
+
// // console.log(studio_units);
|
|
782
|
+
// } catch (err) {
|
|
783
|
+
// console.error(err);
|
|
784
|
+
// }
|
|
785
|
+
|
|
786
|
+
// return table_tools[app_id];
|
|
787
|
+
// };
|
|
788
|
+
|
|
789
|
+
const get_mock_data_generator_agent = function (app_id, table_id) {
|
|
790
|
+
return new Agent({
|
|
791
|
+
name: `Mock Data Generator Agent`,
|
|
792
|
+
instructions: `You are a Mock Data Generator Assistant for table id ${table_id}. Help the user populate data for table id ${table_id} with sample data. First, retrieve the table field definitions using get_studio_table_info_by_table_id_tool. Then generate five realistic mock data samples based on the table structure and insert each one individually using add data to table tool.`,
|
|
793
|
+
// instructions: `You are a Mock Data Generator Assistant.
|
|
794
|
+
// 1. First, get table info using the get_studio_table_info_by_table_id tool with table_id: ${table_id}
|
|
795
|
+
// 2. Analyze the table structure and field types
|
|
796
|
+
// 3. Generate 5 realistic mock data samples based on the fields
|
|
797
|
+
// 4. Return the generated data in a structured format`,
|
|
798
|
+
model,
|
|
799
|
+
tools: [get_studio_table_info_by_table_id_tool, table_tools[app_id][table_id]], //, table_tools[app_id][table_id]
|
|
800
|
+
// modelSettings: { toolChoice: 'required' },
|
|
801
|
+
});
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
// const get_add_data_to_table_agent = function (app_id, table_id) {
|
|
805
|
+
// return new Agent({
|
|
806
|
+
// name: `Add Data to Table Agent`,
|
|
807
|
+
// instructions: `You are an Add Data Assistant for table ID ${table_id}. Your job is to help the user provide valid data for this table. First, retrieve the table field definitions using get_studio_table_info_by_table_id_tool. Then prepare the data as key–value pairs (e.g., { first_name: "John", last_name: "Smith" }) and submit it using the add_data_to_table_tool. Generate UUID values for required unique fields when necessary. Ignore extra fields from the user prompt, and skip optional fields if not provided.`,
|
|
808
|
+
// model,
|
|
809
|
+
// tools: [get_studio_table_info_by_table_id_tool, table_tools[app_id][table_id]],
|
|
810
|
+
// modelSettings: { toolChoice: 'required' },
|
|
811
|
+
// });
|
|
812
|
+
// };
|
|
813
|
+
|
|
814
|
+
const validate_table_data_doc = tool({
|
|
815
|
+
name: 'Validate Table Data Doc',
|
|
816
|
+
description: 'Validate the table doc structure',
|
|
817
|
+
parameters: z.object({
|
|
818
|
+
doc: z.string(),
|
|
819
|
+
}),
|
|
820
|
+
async execute(e, RunContext) {
|
|
821
|
+
const { doc } = e;
|
|
822
|
+
const { app_id, uid, table_id } = RunContext.context;
|
|
823
|
+
|
|
824
|
+
console.log('validate_table_data_doc', doc);
|
|
825
|
+
if (!table_id) throw new Error('got null in the table_id parameter');
|
|
826
|
+
|
|
827
|
+
try {
|
|
828
|
+
const project_db = await get_project_db(app_id);
|
|
829
|
+
|
|
830
|
+
let response = '';
|
|
831
|
+
|
|
832
|
+
try {
|
|
833
|
+
response = await project_db.get(table_id);
|
|
834
|
+
} catch (error) {
|
|
835
|
+
throw new Error(`table doc ${table_id} not found`);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
const dataDocSchema = z.object({
|
|
839
|
+
_id: z.string(),
|
|
840
|
+
// _rev: z.string(),
|
|
841
|
+
docType: z.literal('database'),
|
|
842
|
+
stat: z.number(),
|
|
843
|
+
udfData: z.object({
|
|
844
|
+
udffileid: z.string(),
|
|
845
|
+
data: z.record(z.union([z.string(), z.number()])), // Any key-value pairs
|
|
846
|
+
error: z.object({}).optional(),
|
|
847
|
+
}),
|
|
848
|
+
ts: z.number(),
|
|
849
|
+
date: z.number(),
|
|
850
|
+
udfIndex: z.array(
|
|
851
|
+
z
|
|
852
|
+
.object({
|
|
853
|
+
fileId: z.string(),
|
|
854
|
+
indexId: z.string(),
|
|
855
|
+
keySegments: z.array(z.any()),
|
|
856
|
+
keyValue: z.array(z.any()),
|
|
857
|
+
})
|
|
858
|
+
.optional(),
|
|
859
|
+
),
|
|
860
|
+
});
|
|
861
|
+
const doc_obj = JSON5.parse(doc);
|
|
862
|
+
try {
|
|
863
|
+
const data_doc = dataDocSchema.parse(doc_obj);
|
|
864
|
+
} catch (err) {
|
|
865
|
+
if (err instanceof z.ZodError) {
|
|
866
|
+
console.log('Validation failed:', err.errors);
|
|
867
|
+
// return { success: false, errors: err.errors };
|
|
868
|
+
throw new Error(err.errors);
|
|
869
|
+
}
|
|
870
|
+
throw new Error(err.message);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
return 'table structure ok';
|
|
874
|
+
} catch (err) {
|
|
875
|
+
console.error('validate_table_data_doc', table_id, err.message);
|
|
876
|
+
return err.message;
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
const delete_table_doc = new Agent({
|
|
882
|
+
name: `Delete Table Doc`,
|
|
883
|
+
instructions: `
|
|
884
|
+
|
|
885
|
+
You are an Delete Table Doc Assistant responsible for deleting doc in a table.
|
|
886
|
+
don't prompt for any questions,table is known with context, just do your job using tools.
|
|
887
|
+
When the user provides a prompt, follow this workflow:
|
|
888
|
+
|
|
889
|
+
1. Retrieve the table’s field definitions using the **Get Studio Table Info** tool.
|
|
890
|
+
2. Build a Mango query object using only fields that exist in the table schema (e.g., 'udfData.data.field_name'), and always include "docType": "database" (see **Query Example**)
|
|
891
|
+
3. Submit the constructed query to the **Fetch Table Data** tool.
|
|
892
|
+
4. If query errors fix and re-submit the query to the **Fetch Table Data** tool.
|
|
893
|
+
5. If the query return 1 doc then continue otherwise show the user the results and ask to be more specific
|
|
894
|
+
6. Send the _id to the **Delete Doc By _id** tool to complete the process
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
## Output Example:
|
|
898
|
+
|
|
899
|
+
#|Name |Email |Role
|
|
900
|
+
-+--------+------------+----------
|
|
901
|
+
1| John |j@gmail.com | Manager
|
|
902
|
+
|
|
903
|
+
## Query Example
|
|
904
|
+
|
|
905
|
+
{
|
|
906
|
+
"selector": {
|
|
907
|
+
"docType": "database", <--- must be in every query
|
|
908
|
+
"stat": 3, <--- must be in every query
|
|
909
|
+
"udfData.data.UID": "12345",
|
|
910
|
+
"limit": 25,
|
|
911
|
+
"skip": 0
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
every table field must begin with prefix: udfData.data.
|
|
915
|
+
|
|
916
|
+
const query = {
|
|
917
|
+
selector: {
|
|
918
|
+
docType: "database",
|
|
919
|
+
stat: 3,
|
|
920
|
+
"$and": [
|
|
921
|
+
{ "STU_Username": "Boaz Avrahami" } // ← WRONG: no prefix
|
|
922
|
+
]
|
|
923
|
+
},
|
|
924
|
+
fields: ["STU_Uuid", "STU_Username", ...], // ← WRONG
|
|
925
|
+
limit: 25,
|
|
926
|
+
skip: 0
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
`,
|
|
931
|
+
model,
|
|
932
|
+
tools: [get_studio_table_info_by_table_id_tool, fetch_data_tool, delete_doc_tool],
|
|
933
|
+
modelSettings: { toolChoice: 'required' },
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
const add_data_to_table_agent = new Agent({
|
|
937
|
+
name: `Add Data to Table Agent`,
|
|
938
|
+
instructions: `
|
|
939
|
+
|
|
940
|
+
You are an Add Data Assistant responsible for creating new records in a table.
|
|
941
|
+
don't prompt for any questions,table is known with context, just do your job using tools.
|
|
942
|
+
When the user provides a prompt, follow this workflow:
|
|
943
|
+
|
|
944
|
+
1. Retrieve the table’s field and index definitions using the **Get Studio Table Info** tool.
|
|
945
|
+
2. Retrieve the base data template using the **Get Database Doc Template** tool.
|
|
946
|
+
3. Replace the template’s 'udfData.data' section with the values extracted from the prompt.
|
|
947
|
+
If some fields are not provided, insert them with empty values based on the table’s field definitions.
|
|
948
|
+
4. Replace the 'udfIndex' accordingly.
|
|
949
|
+
5. Validate the document against ##Validate Table Data Doc## tool
|
|
950
|
+
6. If validation return error then retry otherwise continue to step 7
|
|
951
|
+
7. Save the completed document using the **Save Doc** tool.
|
|
952
|
+
`,
|
|
953
|
+
model,
|
|
954
|
+
tools: [get_studio_table_info_by_table_id_tool, get_database_doc_template_tool, validate_table_data_doc, save_doc_tool],
|
|
955
|
+
modelSettings: { toolChoice: 'required' },
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
const fetch_table_data_agent = new Agent({
|
|
959
|
+
name: `Fetch Table Data Agent`,
|
|
960
|
+
instructions: `
|
|
961
|
+
|
|
962
|
+
You are a Fetch Table Data Assistant responsible for retrieving records from a table,
|
|
963
|
+
don't prompt for any questions, just fetch with defaults using tools.
|
|
964
|
+
When the user provides a prompt, follow this workflow:
|
|
965
|
+
|
|
966
|
+
1. Retrieve the table’s field definitions using the **Get Studio Table Info** tool.
|
|
967
|
+
2. Build a Mango query object using only fields that exist in the table schema (e.g., 'udfData.data.field_name'), and always include "docType": "database" (see **Query Example**).
|
|
968
|
+
3. Submit the constructed query to the **Fetch Table Data** tool.
|
|
969
|
+
4. If query errors fix and re-submit the query to the **Fetch Table Data** tool.
|
|
970
|
+
5. If a 'no_usable_index' error is returned, create the required index object (see **Index Example**) using the **Create Index** tool, wait 20 seconds, and then run the query again.
|
|
971
|
+
6. output with organized table grid style columns and rows ## Output Example:
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
## Output Example:
|
|
975
|
+
|
|
976
|
+
#|Name |Email |Role
|
|
977
|
+
-+--------+------------+----------
|
|
978
|
+
1| John |j@gmail.com | Manager
|
|
979
|
+
|
|
980
|
+
## Query Example
|
|
981
|
+
|
|
982
|
+
{
|
|
983
|
+
"selector": {
|
|
984
|
+
"docType": "database", <--- must be in every query
|
|
985
|
+
"stat": 3, <--- must be in every query
|
|
986
|
+
"$and": [
|
|
987
|
+
{
|
|
988
|
+
"udfData.data.type": "product",
|
|
989
|
+
"udfData.data.category": {"$in": ["electronics", "books"]},
|
|
990
|
+
"udfData.data.price": {
|
|
991
|
+
"$gte": 10,
|
|
992
|
+
"$lte": 1000
|
|
993
|
+
},
|
|
994
|
+
"udfData.data.stock": {"$gt": 0},
|
|
995
|
+
"udfData.data.tags": {"$all": ["featured", "new"]},
|
|
996
|
+
"udfData.data.rating": {"$exists": true}
|
|
997
|
+
}
|
|
998
|
+
],
|
|
999
|
+
"$or": [
|
|
1000
|
+
{"udfData.data.featured": true},
|
|
1001
|
+
{"udfData.data.promotion": true}
|
|
1002
|
+
],
|
|
1003
|
+
"$not": {
|
|
1004
|
+
"udfData.data.status": "discontinued"
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
"fields": [
|
|
1008
|
+
"udfData.data._id",
|
|
1009
|
+
"udfData.data.name",
|
|
1010
|
+
"udfData.data.price",
|
|
1011
|
+
"udfData.data.category",
|
|
1012
|
+
"udfData.data.rating",
|
|
1013
|
+
"udfData.data.image"
|
|
1014
|
+
],
|
|
1015
|
+
"sort": [
|
|
1016
|
+
{"udfData.data.rating": "desc"},
|
|
1017
|
+
{"udfData.data.price": "asc"},
|
|
1018
|
+
{"udfData.data.name": "asc"}
|
|
1019
|
+
],
|
|
1020
|
+
"limit": 25,
|
|
1021
|
+
"skip": 0
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
every table field must begin with prefix: udfData.data.
|
|
1026
|
+
|
|
1027
|
+
const query = {
|
|
1028
|
+
selector: {
|
|
1029
|
+
docType: "database",
|
|
1030
|
+
stat: 3,
|
|
1031
|
+
"$and": [
|
|
1032
|
+
{ "STU_Username": "Boaz Avrahami" } // ← WRONG: no prefix
|
|
1033
|
+
]
|
|
1034
|
+
},
|
|
1035
|
+
fields: ["STU_Uuid", "STU_Username", ...], // ← WRONG
|
|
1036
|
+
limit: 25,
|
|
1037
|
+
skip: 0
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
## Index Example
|
|
1041
|
+
|
|
1042
|
+
{
|
|
1043
|
+
"index": {
|
|
1044
|
+
"fields": ["udfData.data.docType", "udfData.data.stat", "udfData.data.type"]
|
|
1045
|
+
},
|
|
1046
|
+
"name": "basic_query_index",
|
|
1047
|
+
"ddoc": "agent_indexes",
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
`,
|
|
1051
|
+
model,
|
|
1052
|
+
tools: [get_studio_table_info_by_table_id_tool, fetch_data_tool, create_index_tool],
|
|
1053
|
+
modelSettings: { toolChoice: 'required' },
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
// const add_data_to_table = tool({
|
|
1057
|
+
// name: 'Create table agent',
|
|
1058
|
+
// description: 'crete agent that handles table tools by providing table_id',
|
|
1059
|
+
// parameters: z.object({
|
|
1060
|
+
// table_id: z.string(),
|
|
1061
|
+
// }),
|
|
1062
|
+
// async execute(e, RunContext) {
|
|
1063
|
+
// const { table_id } = e;
|
|
1064
|
+
// const { app_id, uid } = RunContext.context;
|
|
1065
|
+
// let response = '';
|
|
1066
|
+
// try {
|
|
1067
|
+
// if (!table_id) throw new Error('got null in the table_id parameter');
|
|
1068
|
+
// let table_doc = {};
|
|
1069
|
+
// const project_db = await get_project_db(app_id);
|
|
1070
|
+
// try {
|
|
1071
|
+
// table_doc = await project_db.get(table_id);
|
|
1072
|
+
// } catch (error) {
|
|
1073
|
+
// throw new Error(`table doc ${table_id} not found`);
|
|
1074
|
+
// }
|
|
1075
|
+
|
|
1076
|
+
// const agent_name = `${table_doc.properties.menuName} table AI Agent`;
|
|
1077
|
+
|
|
1078
|
+
// try {
|
|
1079
|
+
// const ai_agents_ret = await get_ai_agents({ uid });
|
|
1080
|
+
// for (const doc of ai_agents_ret?.data?.docs || []) {
|
|
1081
|
+
// if (doc.properties.menuName === table_doc.properties.menuName) {
|
|
1082
|
+
// throw new Error(`ai agent for ${table_id} already exist with name ${agent_name}`);
|
|
1083
|
+
// }
|
|
1084
|
+
// }
|
|
1085
|
+
// } catch (err) {
|
|
1086
|
+
// debugger;
|
|
1087
|
+
// console.error(err);
|
|
1088
|
+
// // throw new Error(err.message);
|
|
1089
|
+
// }
|
|
1090
|
+
|
|
1091
|
+
// const agentConfig = {
|
|
1092
|
+
// agent_name,
|
|
1093
|
+
// agent_tools: [
|
|
1094
|
+
// {
|
|
1095
|
+
// type: 'table',
|
|
1096
|
+
// prog_id: table_id,
|
|
1097
|
+
// },
|
|
1098
|
+
// ],
|
|
1099
|
+
// agent_instructions: 'You are a Table Data Assistant. Your role is to query, analyze, and modify data within tables as requested. You can retrieve specific information, perform calculations, filter or sort records, and apply updates or structural changes to the data when necessary.',
|
|
1100
|
+
// };
|
|
1101
|
+
|
|
1102
|
+
// response = await create_ai_agent({
|
|
1103
|
+
// uid,
|
|
1104
|
+
// agentConfig,
|
|
1105
|
+
// });
|
|
1106
|
+
|
|
1107
|
+
// return { response, agentConfig };
|
|
1108
|
+
// } catch (err) {
|
|
1109
|
+
// return err.message;
|
|
1110
|
+
// }
|
|
1111
|
+
// },
|
|
1112
|
+
// });
|
|
1113
|
+
|
|
1114
|
+
const create_agent_for_table_assistant_tool = tool({
|
|
1115
|
+
name: 'Create table agent',
|
|
1116
|
+
description: 'crete agent that handles table tools by providing table_id',
|
|
1117
|
+
parameters: z.object({
|
|
1118
|
+
table_id: z.string(),
|
|
1119
|
+
}),
|
|
1120
|
+
async execute(e, RunContext) {
|
|
1121
|
+
const { table_id } = e;
|
|
1122
|
+
const { app_id, uid } = RunContext.context;
|
|
1123
|
+
let response = '';
|
|
1124
|
+
try {
|
|
1125
|
+
if (!table_id) throw new Error('got null in the table_id parameter');
|
|
1126
|
+
let table_doc = {};
|
|
1127
|
+
const project_db = await get_project_db(app_id);
|
|
1128
|
+
try {
|
|
1129
|
+
table_doc = await project_db.get(table_id);
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
throw new Error(`table doc ${table_id} not found`);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
const agent_name = `${table_doc.properties.menuName} table AI Agent`;
|
|
1135
|
+
|
|
1136
|
+
const ai_agents_ret = await get_ai_agents({ uid });
|
|
1137
|
+
for (const doc of ai_agents_ret?.data?.docs || []) {
|
|
1138
|
+
if (doc.properties.menuName === agent_name) {
|
|
1139
|
+
throw new Error(`ai agent for ${table_id} already exist with name ${agent_name}`);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
const agentConfig = {
|
|
1144
|
+
agent_name,
|
|
1145
|
+
agent_tools: [
|
|
1146
|
+
{
|
|
1147
|
+
type: 'table',
|
|
1148
|
+
prog_id: table_id,
|
|
1149
|
+
},
|
|
1150
|
+
],
|
|
1151
|
+
agent_instructions: 'You are a Table Data Assistant. Your role is to query, analyze, and modify data within tables as requested. You can retrieve specific information, perform calculations, filter or sort records, and apply updates or structural changes to the data when necessary.',
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
response = await create_ai_agent({
|
|
1155
|
+
uid,
|
|
1156
|
+
agentConfig,
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
return { response, agentConfig };
|
|
1160
|
+
} catch (err) {
|
|
1161
|
+
return err.message;
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
const create_agent_for_table_assistant = new Agent({
|
|
1167
|
+
name: 'Create Agent for Table Assistant',
|
|
1168
|
+
instructions:
|
|
1169
|
+
'You are a Table Agent Creation Assistant. Your role is to dynamically create a table-specific agent when given a table ID or table name. If you receive a table name, use the get_studio_table_info_tool to obtain its table ID. Once the table ID is available, use the create_agent_for_table_assistant_tool to generate and configure the new table agent associated with that table.',
|
|
1170
|
+
model,
|
|
1171
|
+
tools: [get_studio_table_info_tool, create_agent_for_table_assistant_tool],
|
|
1172
|
+
modelSettings: { toolChoice: 'required' },
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
// const get_studio_tables = tool({
|
|
1176
|
+
// name: 'Get studio tables',
|
|
1177
|
+
// description: 'fetch all studio tables',
|
|
1178
|
+
// parameters: z.object({
|
|
1179
|
+
// app_id: z.string(),
|
|
1180
|
+
// }),
|
|
1181
|
+
// async execute(e, RunContext) {
|
|
1182
|
+
// const { app_id } = e;
|
|
1183
|
+
// try {
|
|
1184
|
+
// const project_db = await get_project_db(app_id);
|
|
1185
|
+
// let tables = await project_db.view('xuda', 'studio_docs_by_type', { key: 'table', include_docs: true });
|
|
1186
|
+
|
|
1187
|
+
// return tables;
|
|
1188
|
+
// } catch (err) {
|
|
1189
|
+
// return 'unknown';
|
|
1190
|
+
// }
|
|
1191
|
+
// },
|
|
1192
|
+
// });
|
|
1193
|
+
|
|
1194
|
+
const get_account_info = async function (uid) {
|
|
1195
|
+
try {
|
|
1196
|
+
const db = get_xuda_couch('xuda_accounts');
|
|
1197
|
+
const doc = await db.get(uid);
|
|
1198
|
+
|
|
1199
|
+
return doc.account_info || {};
|
|
1200
|
+
} catch (err) {
|
|
1201
|
+
return err.message;
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1205
|
+
const get_user_name = async function (uid) {
|
|
1206
|
+
try {
|
|
1207
|
+
const account_info = await get_account_info(uid);
|
|
1208
|
+
let username = 'unknown';
|
|
1209
|
+
if (account_info) {
|
|
1210
|
+
username = account_info.first_name + ' ' + account_info.last_name;
|
|
1211
|
+
|
|
1212
|
+
if (!username) {
|
|
1213
|
+
username = +account_info.email;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
return username;
|
|
1218
|
+
} catch (err) {
|
|
1219
|
+
return 'unknown';
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
|
|
1223
|
+
const get_user_name_tool = tool({
|
|
1224
|
+
name: 'get_user_name_tool',
|
|
1225
|
+
description: 'Get the user name by uid',
|
|
1226
|
+
parameters: z.object({
|
|
1227
|
+
uid: z.string(),
|
|
1228
|
+
}),
|
|
1229
|
+
async execute(e, RunContext) {
|
|
1230
|
+
const { uid } = e;
|
|
1231
|
+
|
|
1232
|
+
return await get_user_name(uid);
|
|
1233
|
+
// try {
|
|
1234
|
+
// const db = get_xuda_couch('xuda_accounts');
|
|
1235
|
+
// const doc = await db.get(e.uid);
|
|
1236
|
+
// let username = 'unknown';
|
|
1237
|
+
// if (doc?.account_info) {
|
|
1238
|
+
// username = doc.account_info.first_name + ' ' + doc.account_info.last_name;
|
|
1239
|
+
|
|
1240
|
+
// if (!username) {
|
|
1241
|
+
// username = +doc.account_info.email;
|
|
1242
|
+
// }
|
|
1243
|
+
// }
|
|
1244
|
+
|
|
1245
|
+
// return username;
|
|
1246
|
+
// } catch (err) {
|
|
1247
|
+
// return 'unknown';
|
|
1248
|
+
// }
|
|
1249
|
+
},
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
const create_initial_studio_doc_tool = tool({
|
|
1253
|
+
name: 'Create initial studio doc tool',
|
|
1254
|
+
description: 'I am responsible to create initial doc for the studio',
|
|
1255
|
+
parameters: z.object({
|
|
1256
|
+
uid: z.string().describe('the user id'),
|
|
1257
|
+
app_id: z.string().describe('the app id'),
|
|
1258
|
+
menuType: z.enum(valid_menuType).describe('the studio doc type'),
|
|
1259
|
+
checkedInUserName: z.string().describe('the name of check-in user'),
|
|
1260
|
+
parentId: z.string().describe('the doc id of the parent doc'),
|
|
1261
|
+
}),
|
|
1262
|
+
async execute(e, RunContext) {
|
|
1263
|
+
// console.log('create_initial_studio_doc_tool >>>> ', e);
|
|
1264
|
+
const { uid, menuType, checkedInUserName, app_id, parentId } = e;
|
|
1265
|
+
try {
|
|
1266
|
+
const doc = createDoc({ uid, properties: { menuType }, checkedInUserName, app_id, parentId });
|
|
1267
|
+
|
|
1268
|
+
return doc;
|
|
1269
|
+
} catch (err) {
|
|
1270
|
+
return 'unknown';
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
const check_studio_doc_tool = tool({
|
|
1276
|
+
name: 'Check studio doc',
|
|
1277
|
+
description: 'I am responsible to check and validate studio doc',
|
|
1278
|
+
parameters: z.object({
|
|
1279
|
+
app_id: z.string().describe('the app id'),
|
|
1280
|
+
doc: z.string().describe('the studio doc to validate for table,globals,components and more'),
|
|
1281
|
+
}),
|
|
1282
|
+
async execute(e, RunContext) {
|
|
1283
|
+
try {
|
|
1284
|
+
const doc = JSON5.parse(e.doc);
|
|
1285
|
+
let progs = {};
|
|
1286
|
+
|
|
1287
|
+
const app_obj = await get_app_obj(e.app_id);
|
|
1288
|
+
const project_db = await get_project_db(e.app_id);
|
|
1289
|
+
let studio_nodes = await project_db.view('xuda', 'studio_nodes', {});
|
|
1290
|
+
for (const doc of studio_nodes.rows) {
|
|
1291
|
+
progs[doc.value._id] = doc.value;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
init(app_obj, progs, _, true, null, null, UglifyJS, JSON5, _conf, true, z);
|
|
1295
|
+
let result = check(doc);
|
|
1296
|
+
console.log('check_studio_doc_tool >>>> ', result.check_errors, e.doc);
|
|
1297
|
+
delete result.doc;
|
|
1298
|
+
return result;
|
|
1299
|
+
} catch (err) {
|
|
1300
|
+
console.error(err);
|
|
1301
|
+
return err.message;
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
const get_plugins = tool({
|
|
1307
|
+
name: 'Get plugins',
|
|
1308
|
+
description: 'I am responsible to return all available plugins',
|
|
1309
|
+
parameters: z.object({}),
|
|
1310
|
+
async execute(e, RunContext) {
|
|
1311
|
+
try {
|
|
1312
|
+
const db = get_xuda_couch('xuda_plugins');
|
|
1313
|
+
const plugins_res = await db.view('xuda', 'all-active-plugins', {});
|
|
1314
|
+
let data = [];
|
|
1315
|
+
if (!_.isEmpty(plugins_res.rows)) {
|
|
1316
|
+
for (val of plugins_res.rows) {
|
|
1317
|
+
if (!['library', 'widget', 'ui'].includes(val.plugin_type)) continue;
|
|
1318
|
+
var plugin = _.clone(val.value);
|
|
1319
|
+
delete plugin.account_id;
|
|
1320
|
+
delete plugin.manifest;
|
|
1321
|
+
delete plugin.plugin_name;
|
|
1322
|
+
delete plugin.setup;
|
|
1323
|
+
delete plugin.stat;
|
|
1324
|
+
delete plugin.updated_date;
|
|
1325
|
+
plugin._id = plugin._id.replace('@xuda.io/', '');
|
|
1326
|
+
|
|
1327
|
+
data.push(plugin);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
console.log('get_plugins >>>> ', data);
|
|
1332
|
+
return data;
|
|
1333
|
+
} catch (err) {
|
|
1334
|
+
console.error(err);
|
|
1335
|
+
return err.message;
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
const update_app_doc_tool = tool({
|
|
1341
|
+
name: 'update app doc tool',
|
|
1342
|
+
description: 'update any app doc query by _id and _rev and save to the app db',
|
|
1343
|
+
parameters: z.object({
|
|
1344
|
+
app_id: z.string(),
|
|
1345
|
+
doc: z.object({ _id: z.string(), _rev: z.string() }),
|
|
1346
|
+
}),
|
|
1347
|
+
async execute(e, RunContext) {
|
|
1348
|
+
console.log(`SAVED ${e.app_id}`);
|
|
1349
|
+
return `OK`;
|
|
1350
|
+
},
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
const get_app_doc_tool = tool({
|
|
1354
|
+
name: 'get app doc tool',
|
|
1355
|
+
description: 'get any doc by id from the app db',
|
|
1356
|
+
parameters: z.object({
|
|
1357
|
+
app_id: z.string(),
|
|
1358
|
+
_id: z.string(),
|
|
1359
|
+
}),
|
|
1360
|
+
async execute(e, RunContext) {
|
|
1361
|
+
try {
|
|
1362
|
+
const project_db = await get_project_db(e.app_id);
|
|
1363
|
+
|
|
1364
|
+
const result = await project_db.get(_id);
|
|
1365
|
+
console.log('get_app_doc_tool >>>> ', result);
|
|
1366
|
+
return result;
|
|
1367
|
+
} catch (err) {
|
|
1368
|
+
console.error(err);
|
|
1369
|
+
return err.message;
|
|
1370
|
+
}
|
|
1371
|
+
},
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
const get_additional_information_tool = tool({
|
|
1375
|
+
name: 'get additional information tool',
|
|
1376
|
+
description: 'get additional information for object unit tool',
|
|
1377
|
+
parameters: z.object({
|
|
1378
|
+
object_unit: z.enum(['table']),
|
|
1379
|
+
}),
|
|
1380
|
+
async execute(e, RunContext) {
|
|
1381
|
+
let obj = { table: ['studio_table'] };
|
|
1382
|
+
const db = get_xuda_couch('xuda_website');
|
|
1383
|
+
try {
|
|
1384
|
+
if (!obj[e.object_unit]) throw new Error(`${e.object_unit} not found`);
|
|
1385
|
+
const doc = await db.get(obj[e.object_unit]);
|
|
1386
|
+
// console.log(`INFO ${doc}`);
|
|
1387
|
+
return doc.md || doc;
|
|
1388
|
+
} catch (err) {
|
|
1389
|
+
console.error(err);
|
|
1390
|
+
return err.message;
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
export const submit_agentic_studio_chat_gpt_prompt = async function (req) {
|
|
1396
|
+
try {
|
|
1397
|
+
const { user_prompt, object_unit = 'app', uid, app_id } = req;
|
|
1398
|
+
// const model = 'ft:gpt-4o-2024-08-06:xuda-io:vps-hosting-plans:CDaxQiXo'; // 'gpt-4o'; //'gpt-4.1'; //
|
|
1399
|
+
|
|
1400
|
+
const app_obj = await get_app_obj(app_id);
|
|
1401
|
+
|
|
1402
|
+
// let check_results = {};
|
|
1403
|
+
const auth = async function () {
|
|
1404
|
+
if (app_obj.app_uId !== uid) {
|
|
1405
|
+
const db_team = get_xuda_couch('xuda_team');
|
|
1406
|
+
let master_auth_users = await db_team.view('xuda', 'user_active_shares', {
|
|
1407
|
+
key: [uid, app_id],
|
|
1408
|
+
});
|
|
1409
|
+
if (!master_auth_users.rows.includes(uid)) {
|
|
1410
|
+
throw new Error('user unauthorized');
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
|
|
1415
|
+
await auth();
|
|
1416
|
+
|
|
1417
|
+
const userName = await get_user_name(uid);
|
|
1418
|
+
|
|
1419
|
+
const get_zod_unit_schema = function (type) {
|
|
1420
|
+
init(app_obj, {}, _, true, null, null, UglifyJS, JSON5, _conf, false, z);
|
|
1421
|
+
const schema = get_zod_schema(type);
|
|
1422
|
+
const jsonSchema = zodToJsonSchema(schema);
|
|
1423
|
+
return JSON.stringify(jsonSchema, null, 2);
|
|
1424
|
+
};
|
|
1425
|
+
|
|
1426
|
+
const get_app_instructions = function (object_unit) {
|
|
1427
|
+
return `
|
|
1428
|
+
### context ###
|
|
1429
|
+
uid=${uid}
|
|
1430
|
+
app_id=${app_id}
|
|
1431
|
+
|
|
1432
|
+
### Persona & Role ###
|
|
1433
|
+
- You are a helpful and professional technical assistant named ${object_unit} builder.
|
|
1434
|
+
|
|
1435
|
+
### Tasks ###
|
|
1436
|
+
|
|
1437
|
+
Your job is to assist the user with creating a ${user_prompt} that includes all tasks needed.
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
### workflow ###
|
|
1441
|
+
|
|
1442
|
+
Steps:
|
|
1443
|
+
1. list all the tables needed for the task.
|
|
1444
|
+
2. prefix each table with the subject of the ${user_prompt} (e.g: CRM - Tasks).
|
|
1445
|
+
3. iterate the table list and build tables array using the Table builder tool, add the Table builder tool results and the table name in array RESULTS_ARR.
|
|
1446
|
+
|
|
1447
|
+
|
|
1448
|
+
### Additional information ###
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
### Output ###
|
|
1453
|
+
return the RESULTS_ARR Array.
|
|
1454
|
+
|
|
1455
|
+
### Examples ###
|
|
1456
|
+
|
|
1457
|
+
`;
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
const get_table_instructions = function (object_unit) {
|
|
1461
|
+
return `
|
|
1462
|
+
### context ###
|
|
1463
|
+
uid=${uid}
|
|
1464
|
+
app_id=${app_id}
|
|
1465
|
+
|
|
1466
|
+
### Persona & Role ###
|
|
1467
|
+
- You are a helpful and professional technical assistant named ${object_unit} builder.
|
|
1468
|
+
|
|
1469
|
+
### Tasks ###
|
|
1470
|
+
|
|
1471
|
+
Your job is to assist the user with creating a ${user_prompt} that includes a full set of fields according to the provided ${object_unit} structure.
|
|
1472
|
+
|
|
1473
|
+
- You must include all relevant fields a typical ${user_prompt} would require
|
|
1474
|
+
- Add **multiple entries** in the "tableFields" array as needed to fully represent the structure.
|
|
1475
|
+
- Follow the format and conventions shown in the ${object_unit} structure example.
|
|
1476
|
+
- use uid for "createdByUid" and "checkedInUserId"
|
|
1477
|
+
|
|
1478
|
+
### workflow ###
|
|
1479
|
+
|
|
1480
|
+
Steps:
|
|
1481
|
+
1. Use create_initial_studio_doc_tool to create the initial doc assign the return result in "doc" variable.
|
|
1482
|
+
|
|
1483
|
+
3. Use check_studio_doc_tool to check the ${object_unit} send the ${object_unit} as the doc, fix the issues if needed. if the check fail for 3 times the throw error, skip **Save Doc** tool if errors exist
|
|
1484
|
+
4. Use **Save Doc** tool to save the ${object_unit} doc and update the result in save_result variable,NEVER save if errors exist after check_studio_doc_tool
|
|
1485
|
+
|
|
1486
|
+
|
|
1487
|
+
### Output ###
|
|
1488
|
+
if success: {code:1,data:save_result}
|
|
1489
|
+
if fail: {code:-1,data:error}
|
|
1490
|
+
|
|
1491
|
+
### EXAMPLE
|
|
1492
|
+
|
|
1493
|
+
{
|
|
1494
|
+
"_id": "5b1_tbl_47f0ac6059ca",
|
|
1495
|
+
"app_id": "prj3937cb6f9a31c8c7dea25055bba845b1",
|
|
1496
|
+
"stat": 3,
|
|
1497
|
+
"docType": "studio",
|
|
1498
|
+
"docDate": 1714916369797,
|
|
1499
|
+
"ts": 1762953659001,
|
|
1500
|
+
"order_ts": 1714916369797,
|
|
1501
|
+
"studio_meta": {
|
|
1502
|
+
"created": 1714916369797,
|
|
1503
|
+
"createdByUid": "341cf2d32991a68e4d8aaba6c16e15cc",
|
|
1504
|
+
"checkedInUserId": "341cf2d32991a68e4d8aaba6c16e15cc",
|
|
1505
|
+
"checkedInUserName": "Ioshka Media",
|
|
1506
|
+
"parentId": "database",
|
|
1507
|
+
"savedByUid": "341cf2d32991a68e4d8aaba6c16e15cc"
|
|
1508
|
+
},
|
|
1509
|
+
"properties": {
|
|
1510
|
+
"menuType": "table",
|
|
1511
|
+
"menuName": "PT - Practice Table",
|
|
1512
|
+
"databaseSocket": "@xuda.io/xuda-dbs-plugin-xuda",
|
|
1513
|
+
"menuTitle": "PT - Practice Table"
|
|
1514
|
+
},
|
|
1515
|
+
"tableIndexes": [
|
|
1516
|
+
{
|
|
1517
|
+
"id": "76bbf201-997e-402c-b435-0649cc25d117",
|
|
1518
|
+
"data": {
|
|
1519
|
+
"name": "Primary_Index",
|
|
1520
|
+
"unique": true,
|
|
1521
|
+
"keys": [
|
|
1522
|
+
"PT_Id"
|
|
1523
|
+
]
|
|
1524
|
+
},
|
|
1525
|
+
"props": {} <-- must appear even if empty
|
|
1526
|
+
},
|
|
1527
|
+
{
|
|
1528
|
+
"id": "89f1609d-de4e-4b61-9357-284b1261314c",
|
|
1529
|
+
"data": {
|
|
1530
|
+
"name": "Index_2",
|
|
1531
|
+
"unique": false,
|
|
1532
|
+
"keys": [
|
|
1533
|
+
"PT_Stat",
|
|
1534
|
+
"PT_Group_Id"
|
|
1535
|
+
]
|
|
1536
|
+
},
|
|
1537
|
+
"props": {} <-- must appear even if empty
|
|
1538
|
+
}
|
|
1539
|
+
],
|
|
1540
|
+
"tableFields": [
|
|
1541
|
+
{
|
|
1542
|
+
"id": "b41aa880-56ec-4029-a21b-60c89a52b62e",
|
|
1543
|
+
"data": {
|
|
1544
|
+
"field_id": "PT_Id"
|
|
1545
|
+
},
|
|
1546
|
+
"props": {
|
|
1547
|
+
"label":"Practice Id",
|
|
1548
|
+
"fieldType": "string",
|
|
1549
|
+
"mandatory": true
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
},
|
|
1553
|
+
{
|
|
1554
|
+
"id": "8aec82db-35d9-4828-9fc7-49373c531cc3",
|
|
1555
|
+
"data": {
|
|
1556
|
+
"field_id": "PT_Type"
|
|
1557
|
+
},
|
|
1558
|
+
"props": {
|
|
1559
|
+
"label":"Practice Type",
|
|
1560
|
+
"fieldType": "string",
|
|
1561
|
+
"mandatory": true
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
"id": "d27c56e5-faec-4a0a-ad42-7bf55f8c67b3",
|
|
1567
|
+
"data": {
|
|
1568
|
+
"field_id": "PT_Stat"
|
|
1569
|
+
},
|
|
1570
|
+
"props": {
|
|
1571
|
+
"label":"Practice Status",
|
|
1572
|
+
"fieldType": "number",
|
|
1573
|
+
"fieldComment": "1 = Draft\n2 = Private\n3 = Public\n4 = Deleted",
|
|
1574
|
+
"mandatory": true
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
"id": "659fbfb3-7dcc-4ea0-b1e9-5dd9553a99fc",
|
|
1580
|
+
"data": {
|
|
1581
|
+
"field_id": "PT_Question"
|
|
1582
|
+
},
|
|
1583
|
+
"props": {
|
|
1584
|
+
"label":"Practice Question",
|
|
1585
|
+
"fieldType": "string"
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
},
|
|
1589
|
+
{
|
|
1590
|
+
"id": "080c8ceb-5a51-437b-8ad1-9fc005c79011",
|
|
1591
|
+
"data": {
|
|
1592
|
+
"field_id": "PT_Options"
|
|
1593
|
+
},
|
|
1594
|
+
"props": {
|
|
1595
|
+
"label":"Practice Options",
|
|
1596
|
+
"fieldType": "array"
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
},
|
|
1600
|
+
{
|
|
1601
|
+
"id": "72c04aac-b3e7-4e7a-8a74-31075de3dbec",
|
|
1602
|
+
"data": {
|
|
1603
|
+
"field_id": "PT_Answer"
|
|
1604
|
+
},
|
|
1605
|
+
"props": {
|
|
1606
|
+
"label":"Practice Answer",
|
|
1607
|
+
"fieldType": "string"
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
"id": "e37b08c6-f340-425a-bf01-809b28a486d3",
|
|
1613
|
+
"data": {
|
|
1614
|
+
"field_id": "PT_Matching_Answer"
|
|
1615
|
+
},
|
|
1616
|
+
"props": {
|
|
1617
|
+
"label":"Practice Matching Answer",
|
|
1618
|
+
"fieldType": "object"
|
|
1619
|
+
},
|
|
1620
|
+
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
"id": "8ca4ace1-2010-45be-b7aa-2672ce0e6fea",
|
|
1624
|
+
"data": {
|
|
1625
|
+
"field_id": "PT_Group_Id"
|
|
1626
|
+
},
|
|
1627
|
+
"props": {
|
|
1628
|
+
"label":"Practice Group Id",
|
|
1629
|
+
"fieldType": "string"
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
"id": "ae749c6d-1f39-4750-b79e-f016d50b8a11",
|
|
1635
|
+
"data": {
|
|
1636
|
+
"field_id": "PT_Difficulty"
|
|
1637
|
+
},
|
|
1638
|
+
"props": {
|
|
1639
|
+
"label":"Practice Difficulty",
|
|
1640
|
+
"fieldType": "number"
|
|
1641
|
+
},
|
|
1642
|
+
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
"id": "5c5e7cb5-feee-4bc7-9d94-0ccd04a5eb06",
|
|
1646
|
+
"data": {
|
|
1647
|
+
"field_id": "PT_Uid"
|
|
1648
|
+
},
|
|
1649
|
+
"props": {
|
|
1650
|
+
"label":"Practice User Id",
|
|
1651
|
+
"fieldType": "string"
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
}
|
|
1655
|
+
]
|
|
1656
|
+
}
|
|
1657
|
+
`;
|
|
1658
|
+
};
|
|
1659
|
+
|
|
1660
|
+
const get_folder_instructions = function (object_unit) {
|
|
1661
|
+
return `
|
|
1662
|
+
### context ###
|
|
1663
|
+
uid=${uid}
|
|
1664
|
+
app_id=${app_id}
|
|
1665
|
+
|
|
1666
|
+
### Persona & Role ###
|
|
1667
|
+
- You are a helpful and professional technical assistant named ${object_unit} builder.
|
|
1668
|
+
|
|
1669
|
+
### Tasks ###
|
|
1670
|
+
|
|
1671
|
+
Your job is to assist the user with creating a ${user_prompt}.
|
|
1672
|
+
|
|
1673
|
+
- Follow the format and conventions shown in the ${object_unit} structure example.
|
|
1674
|
+
- use uid for "createdByUid" and "checkedInUserId"
|
|
1675
|
+
|
|
1676
|
+
### workflow ###
|
|
1677
|
+
|
|
1678
|
+
Steps:
|
|
1679
|
+
1. Use create_initial_studio_doc_tool to create the initial doc assign the return result in "doc" variable.
|
|
1680
|
+
2. Get the ${object_unit} structure example from ### Structure and Examples ### section.
|
|
1681
|
+
3. Build the ${object_unit} and validate against vs_68af4c84631c81919709268c2d0f1687
|
|
1682
|
+
4. Use check_studio_doc_tool to check the ${object_unit} send the ${object_unit} as the doc, fix the issues if needed. if the check fail for 3 times the throw error, skip **Save Doc** tool if errors exist
|
|
1683
|
+
5. Use **Save Doc** tool to save the ${object_unit} doc and update the result in save_result variable,NEVER save if errors exist after check_studio_doc_tool
|
|
1684
|
+
|
|
1685
|
+
|
|
1686
|
+
### Additional information ###
|
|
1687
|
+
${studio_units[object_unit].short}
|
|
1688
|
+
|
|
1689
|
+
|
|
1690
|
+
### Output ###
|
|
1691
|
+
if success: {code:1,data:save_result}
|
|
1692
|
+
if fail: {code:-1,data:error}
|
|
1693
|
+
`;
|
|
1694
|
+
};
|
|
1695
|
+
|
|
1696
|
+
const get_component_instructions = function (object_unit) {
|
|
1697
|
+
return `
|
|
1698
|
+
### context ###
|
|
1699
|
+
uid=${uid}
|
|
1700
|
+
app_id=${app_id}
|
|
1701
|
+
|
|
1702
|
+
### Persona & Role ###
|
|
1703
|
+
- You are a helpful and professional technical assistant named ${object_unit} builder.
|
|
1704
|
+
|
|
1705
|
+
### Tasks ###
|
|
1706
|
+
|
|
1707
|
+
Your job is to assist the user with creating a ${user_prompt} that includes modern ui using Tailwind
|
|
1708
|
+
|
|
1709
|
+
- You must include all relevant fields a typical ${user_prompt} would require
|
|
1710
|
+
|
|
1711
|
+
- Follow the format and conventions shown in the ${object_unit} structure example.
|
|
1712
|
+
- use uid for "createdByUid" and "checkedInUserId"
|
|
1713
|
+
|
|
1714
|
+
### workflow ###
|
|
1715
|
+
|
|
1716
|
+
Steps:
|
|
1717
|
+
1. Use create_initial_studio_doc_tool to create the initial doc assign the return result in "doc" variable.
|
|
1718
|
+
|
|
1719
|
+
2. use ### Structure and Examples ### section to build the object
|
|
1720
|
+
3. Use check_studio_doc_tool to check the ${object_unit} send the ${object_unit} as the doc, fix the issues if needed. if the check fail for 10 times the throw error, skip **Save Doc** tool if errors exist
|
|
1721
|
+
4. Use **Save Doc** tool to save the ${object_unit} doc and update the result in save_result variable,NEVER save if errors exist after check_studio_doc_tool
|
|
1722
|
+
|
|
1723
|
+
|
|
1724
|
+
### Additional information ###
|
|
1725
|
+
${studio_units[object_unit].short}
|
|
1726
|
+
|
|
1727
|
+
### Structure and Examples ###
|
|
1728
|
+
|
|
1729
|
+
${get_zod_unit_schema('component')}
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
|
|
1733
|
+
|
|
1734
|
+
### Output ###
|
|
1735
|
+
if success: {code:1,data:save_result}
|
|
1736
|
+
if fail: {code:-1,data:error}
|
|
1737
|
+
`;
|
|
1738
|
+
};
|
|
1739
|
+
const get_get_data_instructions = function (object_unit) {
|
|
1740
|
+
return `
|
|
1741
|
+
### context ###
|
|
1742
|
+
uid=${uid}
|
|
1743
|
+
app_id=${app_id}
|
|
1744
|
+
|
|
1745
|
+
### Persona & Role ###
|
|
1746
|
+
- You are a helpful and professional technical assistant named ${object_unit} builder.
|
|
1747
|
+
|
|
1748
|
+
### Tasks ###
|
|
1749
|
+
|
|
1750
|
+
Your job is to assist the user with creating a ${user_prompt}.
|
|
1751
|
+
|
|
1752
|
+
- You must include all relevant fields a typical ${user_prompt} would require
|
|
1753
|
+
|
|
1754
|
+
- Follow the format and conventions shown in the ${object_unit} structure example.
|
|
1755
|
+
- use uid for "createdByUid" and "checkedInUserId"
|
|
1756
|
+
|
|
1757
|
+
### workflow ###
|
|
1758
|
+
|
|
1759
|
+
Steps:
|
|
1760
|
+
1. Use create_initial_studio_doc_tool to create the initial doc assign the return result in "doc" variable.
|
|
1761
|
+
2. Get the ${object_unit} structure example from ### Structure and Examples ### section.
|
|
1762
|
+
3. Build the ${object_unit} and validate against vs_68af4c84631c81919709268c2d0f1687
|
|
1763
|
+
4. Use check_studio_doc_tool to check the ${object_unit} send the ${object_unit} as the doc, fix the issues if needed. if the check fail for 10 times the throw error, skip **Save Doc** tool if errors exist
|
|
1764
|
+
5. Use **Save Doc** tool to save the ${object_unit} doc and update the result in save_result variable,NEVER save if errors exist after check_studio_doc_tool
|
|
1765
|
+
|
|
1766
|
+
|
|
1767
|
+
### Additional information ###
|
|
1768
|
+
${studio_units[object_unit].short}
|
|
1769
|
+
|
|
1770
|
+
### Output ###
|
|
1771
|
+
if success: {code:1,data:save_result}
|
|
1772
|
+
if fail: {code:-1,data:error}
|
|
1773
|
+
`;
|
|
1774
|
+
};
|
|
1775
|
+
|
|
1776
|
+
const get_agents_as_a_tool = function (agent_list = []) {
|
|
1777
|
+
let _tools = [];
|
|
1778
|
+
for (const agent of agent_list) {
|
|
1779
|
+
const _agent = new Agent(studio_agents_obj[agent]);
|
|
1780
|
+
const tool = _agent.asTool({
|
|
1781
|
+
toolName: studio_agents_obj[agent].name,
|
|
1782
|
+
toolDescription: `Generate a ${agent} of the supplied text.`,
|
|
1783
|
+
});
|
|
1784
|
+
|
|
1785
|
+
_tools.push(tool);
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
return _tools;
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
let studio_agents_obj = {
|
|
1792
|
+
app: {
|
|
1793
|
+
name: 'App builder',
|
|
1794
|
+
instructions: get_app_instructions(),
|
|
1795
|
+
tools: [get_user_name_tool],
|
|
1796
|
+
model,
|
|
1797
|
+
modelSettings: {
|
|
1798
|
+
toolChoice: 'required', // ← this enables tool use
|
|
1799
|
+
responseFormat: {
|
|
1800
|
+
text: {
|
|
1801
|
+
format: {
|
|
1802
|
+
type: 'array', // or "markdown" if needed
|
|
1803
|
+
},
|
|
1804
|
+
},
|
|
1805
|
+
},
|
|
1806
|
+
outputType: z.array(z.object()),
|
|
1807
|
+
},
|
|
1808
|
+
},
|
|
1809
|
+
table: {
|
|
1810
|
+
name: 'Table builder',
|
|
1811
|
+
instructions: get_table_instructions('table'),
|
|
1812
|
+
tools: [get_user_name_tool, create_initial_studio_doc_tool, check_studio_doc_tool, save_doc_tool], //fileSearchTool('vs_68af4c84631c81919709268c2d0f1687'), fileSearchTool('vs_68af473196a88191afc6610fddfe4a62')
|
|
1813
|
+
model,
|
|
1814
|
+
modelSettings: {
|
|
1815
|
+
toolChoice: 'required', // ← this enables tool use
|
|
1816
|
+
responseFormat: {
|
|
1817
|
+
text: {
|
|
1818
|
+
format: {
|
|
1819
|
+
type: 'array', // or "markdown" if needed
|
|
1820
|
+
},
|
|
1821
|
+
},
|
|
1822
|
+
},
|
|
1823
|
+
outputType: z.array(
|
|
1824
|
+
z.object({
|
|
1825
|
+
_id: z.string(),
|
|
1826
|
+
stat: z.number(),
|
|
1827
|
+
docType: z.string(),
|
|
1828
|
+
docDate: z.number(),
|
|
1829
|
+
ts: z.number(),
|
|
1830
|
+
order_ts: z.number(),
|
|
1831
|
+
studio_meta: z.object({
|
|
1832
|
+
created: z.number(),
|
|
1833
|
+
createdByUid: z.string(),
|
|
1834
|
+
checkedInUserId: z.string(),
|
|
1835
|
+
savedByUid: z.string(),
|
|
1836
|
+
checkedInUserName: z.string(),
|
|
1837
|
+
parentId: z.string(),
|
|
1838
|
+
source: z.string(),
|
|
1839
|
+
}),
|
|
1840
|
+
properties: z.object({
|
|
1841
|
+
menuName: z.string(),
|
|
1842
|
+
menuType: z.string(),
|
|
1843
|
+
databaseSocket: z.string(),
|
|
1844
|
+
}),
|
|
1845
|
+
tableIndexes: z.array(
|
|
1846
|
+
z.object({
|
|
1847
|
+
id: z.string(),
|
|
1848
|
+
data: z.object({
|
|
1849
|
+
name: z.string(),
|
|
1850
|
+
unique: z.boolean(),
|
|
1851
|
+
keys: z.array(z.string()),
|
|
1852
|
+
}),
|
|
1853
|
+
props: z.object({}),
|
|
1854
|
+
}),
|
|
1855
|
+
),
|
|
1856
|
+
tableFields: z.array(
|
|
1857
|
+
z.object({
|
|
1858
|
+
id: z.string(),
|
|
1859
|
+
data: z.object({
|
|
1860
|
+
field_id: z.string(),
|
|
1861
|
+
}),
|
|
1862
|
+
props: z.object({
|
|
1863
|
+
label: z.string(),
|
|
1864
|
+
fieldType: z.string(),
|
|
1865
|
+
}),
|
|
1866
|
+
}),
|
|
1867
|
+
),
|
|
1868
|
+
}),
|
|
1869
|
+
),
|
|
1870
|
+
},
|
|
1871
|
+
},
|
|
1872
|
+
folder: {
|
|
1873
|
+
name: 'Folder builder',
|
|
1874
|
+
instructions: get_folder_instructions('folder'),
|
|
1875
|
+
tools: [get_user_name_tool, create_initial_studio_doc_tool, check_studio_doc_tool, save_doc_tool], //fileSearchTool('vs_68af4c84631c81919709268c2d0f1687'), fileSearchTool('vs_68af44989e4881918fc8bf8e84c6ce03')
|
|
1876
|
+
model, //: 'gpt-4o'
|
|
1877
|
+
modelSettings: {
|
|
1878
|
+
toolChoice: 'required', // ← this enables tool use
|
|
1879
|
+
responseFormat: {
|
|
1880
|
+
text: {
|
|
1881
|
+
format: {
|
|
1882
|
+
type: 'array', // or "markdown" if needed
|
|
1883
|
+
},
|
|
1884
|
+
},
|
|
1885
|
+
},
|
|
1886
|
+
outputType: z.array(
|
|
1887
|
+
z.object({
|
|
1888
|
+
_id: z.string(),
|
|
1889
|
+
stat: z.number(),
|
|
1890
|
+
docType: z.string(),
|
|
1891
|
+
docDate: z.number(),
|
|
1892
|
+
ts: z.number(),
|
|
1893
|
+
order_ts: z.number(),
|
|
1894
|
+
studio_meta: z.object({
|
|
1895
|
+
created: z.number(),
|
|
1896
|
+
createdByUid: z.string(),
|
|
1897
|
+
checkedInUserId: z.string(),
|
|
1898
|
+
savedByUid: z.string(),
|
|
1899
|
+
checkedInUserName: z.string(),
|
|
1900
|
+
parentId: z.string(),
|
|
1901
|
+
source: z.string(),
|
|
1902
|
+
}),
|
|
1903
|
+
properties: z.object({
|
|
1904
|
+
menuName: z.string(),
|
|
1905
|
+
menuType: z.string(),
|
|
1906
|
+
databaseSocket: z.string(),
|
|
1907
|
+
}),
|
|
1908
|
+
}),
|
|
1909
|
+
),
|
|
1910
|
+
},
|
|
1911
|
+
},
|
|
1912
|
+
component: {
|
|
1913
|
+
name: 'Component builder',
|
|
1914
|
+
instructions: get_component_instructions('component'),
|
|
1915
|
+
tools: [get_user_name_tool, create_initial_studio_doc_tool, check_studio_doc_tool, save_doc_tool], //, fileSearchTool('vs_68af48a65cf88191a48a0f530d7e3919')
|
|
1916
|
+
model,
|
|
1917
|
+
modelSettings: {
|
|
1918
|
+
toolChoice: 'required', // ← this enables tool use
|
|
1919
|
+
responseFormat: {
|
|
1920
|
+
text: {
|
|
1921
|
+
format: {
|
|
1922
|
+
type: 'array', // or "markdown" if needed
|
|
1923
|
+
},
|
|
1924
|
+
},
|
|
1925
|
+
},
|
|
1926
|
+
outputType: z.array(
|
|
1927
|
+
z.object({
|
|
1928
|
+
_id: z.string(),
|
|
1929
|
+
stat: z.number(),
|
|
1930
|
+
docType: z.string(),
|
|
1931
|
+
docDate: z.number(),
|
|
1932
|
+
ts: z.number(),
|
|
1933
|
+
order_ts: z.number(),
|
|
1934
|
+
studio_meta: z.object({
|
|
1935
|
+
created: z.number(),
|
|
1936
|
+
createdByUid: z.string(),
|
|
1937
|
+
checkedInUserId: z.string(),
|
|
1938
|
+
savedByUid: z.string(),
|
|
1939
|
+
checkedInUserName: z.string(),
|
|
1940
|
+
parentId: z.string(),
|
|
1941
|
+
source: z.string(),
|
|
1942
|
+
}),
|
|
1943
|
+
properties: z.object({
|
|
1944
|
+
menuName: z.string(),
|
|
1945
|
+
menuType: z.string(),
|
|
1946
|
+
databaseSocket: z.string(),
|
|
1947
|
+
}),
|
|
1948
|
+
progDataSource: z.object({}),
|
|
1949
|
+
progFields: z.array(z.object({})),
|
|
1950
|
+
progEvents: z.array(z.object({})),
|
|
1951
|
+
progUi: z.array(z.object({})),
|
|
1952
|
+
}),
|
|
1953
|
+
),
|
|
1954
|
+
},
|
|
1955
|
+
},
|
|
1956
|
+
get_data: {
|
|
1957
|
+
name: 'Get data builder',
|
|
1958
|
+
instructions: get_get_data_instructions('get_data'),
|
|
1959
|
+
tools: [get_user_name_tool, create_initial_studio_doc_tool, check_studio_doc_tool, save_doc_tool], //fileSearchTool('vs_68af4c84631c81919709268c2d0f1687'), fileSearchTool('vs_68af530d4ba081919ca57eabb5ae117a')
|
|
1960
|
+
model,
|
|
1961
|
+
modelSettings: {
|
|
1962
|
+
toolChoice: 'required', // ← this enables tool use
|
|
1963
|
+
responseFormat: {
|
|
1964
|
+
text: {
|
|
1965
|
+
format: {
|
|
1966
|
+
type: 'array', // or "markdown" if needed
|
|
1967
|
+
},
|
|
1968
|
+
},
|
|
1969
|
+
},
|
|
1970
|
+
outputType: z.array(
|
|
1971
|
+
z.object({
|
|
1972
|
+
_id: z.string(),
|
|
1973
|
+
stat: z.number(),
|
|
1974
|
+
docType: z.string(),
|
|
1975
|
+
docDate: z.number(),
|
|
1976
|
+
ts: z.number(),
|
|
1977
|
+
order_ts: z.number(),
|
|
1978
|
+
studio_meta: z.object({
|
|
1979
|
+
created: z.number(),
|
|
1980
|
+
createdByUid: z.string(),
|
|
1981
|
+
checkedInUserId: z.string(),
|
|
1982
|
+
savedByUid: z.string(),
|
|
1983
|
+
checkedInUserName: z.string(),
|
|
1984
|
+
parentId: z.string(),
|
|
1985
|
+
source: z.string(),
|
|
1986
|
+
}),
|
|
1987
|
+
properties: z.object({
|
|
1988
|
+
menuName: z.string(),
|
|
1989
|
+
menuType: z.string(),
|
|
1990
|
+
databaseSocket: z.string(),
|
|
1991
|
+
}),
|
|
1992
|
+
progDataSource: z.object({}),
|
|
1993
|
+
progFields: z.array(z.object({})),
|
|
1994
|
+
progEvents: z.array(z.object({})),
|
|
1995
|
+
}),
|
|
1996
|
+
),
|
|
1997
|
+
},
|
|
1998
|
+
},
|
|
1999
|
+
};
|
|
2000
|
+
|
|
2001
|
+
if (object_unit === 'app') {
|
|
2002
|
+
const agents_as_a_tool_arr = get_agents_as_a_tool(['table', 'folder', 'component', 'get_data']);
|
|
2003
|
+
studio_agents_obj.app.tools = [...studio_agents_obj.app.tools, ...agents_as_a_tool_arr];
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
const run = async function (user_prompt, object_unit) {
|
|
2007
|
+
const _agent = new Agent(studio_agents_obj[object_unit]);
|
|
2008
|
+
const runner = new Runner();
|
|
2009
|
+
|
|
2010
|
+
const result = await runner.run(_agent, user_prompt, {
|
|
2011
|
+
context: { uid, app_id, userName },
|
|
2012
|
+
});
|
|
2013
|
+
|
|
2014
|
+
return result;
|
|
2015
|
+
};
|
|
2016
|
+
|
|
2017
|
+
const result = await run(user_prompt, object_unit);
|
|
2018
|
+
|
|
2019
|
+
return { code: 100, data: result.finalOutput };
|
|
2020
|
+
} catch (err) {
|
|
2021
|
+
debugger;
|
|
2022
|
+
console.error('>>>>> AI:', err);
|
|
2023
|
+
console.error('Cause:', err?.cause?.message);
|
|
2024
|
+
return { code: -100, data: err.message };
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
|
|
2028
|
+
export const get_chat_conversation = async function (req) {
|
|
2029
|
+
let { conversation_id, order = 'asc', limit, after, _id, uid } = req;
|
|
2030
|
+
|
|
2031
|
+
if (_id) return await get_ai_doc({ uid, _id });
|
|
2032
|
+
|
|
2033
|
+
try {
|
|
2034
|
+
const project_db = await get_account_project_db(uid);
|
|
2035
|
+
const conversation_doc = await project_db.get(conversation_id);
|
|
2036
|
+
let conversation = await client.conversations.items.list(conversation_id, { limit, order, after });
|
|
2037
|
+
|
|
2038
|
+
return { code: 15, data: { conversation, ...conversation_doc } };
|
|
2039
|
+
} catch (err) {
|
|
2040
|
+
return { code: -15, data: err.message };
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
|
|
2044
|
+
export const get_ai_chats = async function (req) {
|
|
2045
|
+
let { uid, reference_id, _id } = req;
|
|
2046
|
+
|
|
2047
|
+
const project_db = await get_account_project_db(uid);
|
|
2048
|
+
try {
|
|
2049
|
+
if (_id) return await get_ai_doc({ _id, uid });
|
|
2050
|
+
|
|
2051
|
+
// let opt = { selector: { docType: 'chat_conversation', uid, stat: 3 } };
|
|
2052
|
+
// if (reference_id) {
|
|
2053
|
+
// opt.selector.reference_id = reference_id;
|
|
2054
|
+
// }
|
|
2055
|
+
// const data = await project_db.find(opt);
|
|
2056
|
+
|
|
2057
|
+
const ai_chats = await project_db.view('xuda', 'ai_chat_list', {
|
|
2058
|
+
key: reference_id || '',
|
|
2059
|
+
});
|
|
2060
|
+
|
|
2061
|
+
return { code: 2, data: { docs: ai_chats.rows.map((row) => row.value) } };
|
|
2062
|
+
} catch (err) {
|
|
2063
|
+
return { code: -2, data: err.message };
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
|
|
2067
|
+
export const delete_ai_chat = async function (req) {
|
|
2068
|
+
let { conversation_id, uid } = req;
|
|
2069
|
+
|
|
2070
|
+
const project_db = await get_account_project_db(uid);
|
|
2071
|
+
try {
|
|
2072
|
+
let conversation_doc = await project_db.get(conversation_id);
|
|
2073
|
+
conversation_doc.stat = 4;
|
|
2074
|
+
conversation_doc.ts = Date.now();
|
|
2075
|
+
const data = await project_db.insert(conversation_doc);
|
|
2076
|
+
|
|
2077
|
+
client.conversations.delete(conversation_id);
|
|
2078
|
+
|
|
2079
|
+
return { code: 3, data };
|
|
2080
|
+
} catch (err) {
|
|
2081
|
+
return { code: -3, data: err.message };
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// const db = get_xuda_couch('xuda_contacts');
|
|
2085
|
+
// try {
|
|
2086
|
+
// let conversation_doc = await db.get(conversation_id);
|
|
2087
|
+
// conversation_doc.stat = 4;
|
|
2088
|
+
// conversation_doc.ts = Date.now();
|
|
2089
|
+
// const data = await db.insert(conversation_doc);
|
|
2090
|
+
|
|
2091
|
+
// return { code: 1, data };
|
|
2092
|
+
// } catch (err) {
|
|
2093
|
+
// return { code: -1, data: err.message };
|
|
2094
|
+
// }
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
export const submit_chat_conversation = async function (req) {
|
|
2098
|
+
const { uid, gtp_token /* agent_mode not used here */ } = req;
|
|
2099
|
+
let { prompt, conversation_id, reference_type, reference_id } = req;
|
|
2100
|
+
|
|
2101
|
+
const project_db = await get_account_project_db(uid);
|
|
2102
|
+
|
|
2103
|
+
const app_id = await get_account_project_id(uid);
|
|
2104
|
+
const app_obj = await get_app_obj(app_id);
|
|
2105
|
+
const userName = await get_user_name(uid);
|
|
2106
|
+
const account_info = await get_account_info(uid);
|
|
2107
|
+
let context = { app_id, uid, userName, account_info, app_obj };
|
|
2108
|
+
|
|
2109
|
+
let conversation_doc = {
|
|
2110
|
+
docType: 'chat_conversation',
|
|
2111
|
+
title: prompt.substr(0, 40),
|
|
2112
|
+
date_created_ts: Date.now(),
|
|
2113
|
+
ts: Date.now(),
|
|
2114
|
+
stat: 3,
|
|
2115
|
+
uid,
|
|
2116
|
+
messages: [],
|
|
2117
|
+
reference_type,
|
|
2118
|
+
reference_id,
|
|
2119
|
+
};
|
|
2120
|
+
|
|
2121
|
+
// 1. Ensure we have a conversation doc in Couch
|
|
2122
|
+
let conversation_exist;
|
|
2123
|
+
try {
|
|
2124
|
+
if (!conversation_id) throw new Error('no id');
|
|
2125
|
+
conversation_doc = await project_db.get(conversation_id);
|
|
2126
|
+
reference_type = conversation_doc.reference_type;
|
|
2127
|
+
reference_id = conversation_doc.reference_id;
|
|
2128
|
+
conversation_exist = true;
|
|
2129
|
+
} catch (err) {
|
|
2130
|
+
// new conversation
|
|
2131
|
+
const conversation_obj = await client.conversations.create();
|
|
2132
|
+
conversation_id = conversation_obj.id;
|
|
2133
|
+
conversation_doc.conversation_obj = conversation_obj;
|
|
2134
|
+
conversation_doc._id = conversation_id;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
const get_agents = async function () {
|
|
2138
|
+
const list = [];
|
|
2139
|
+
|
|
2140
|
+
let ai_agents_ret = {};
|
|
2141
|
+
let modelSettings = {};
|
|
2142
|
+
|
|
2143
|
+
if (reference_type === 'ai_agents') {
|
|
2144
|
+
modelSettings = { toolChoice: 'auto' };
|
|
2145
|
+
ai_agents_ret = { code: 1, data: { docs: [{ id: reference_id }] } };
|
|
2146
|
+
} else {
|
|
2147
|
+
ai_agents_ret = await get_ai_agents({ uid });
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
for (const doc of ai_agents_ret.data.docs) {
|
|
2151
|
+
let ai_agent_doc;
|
|
2152
|
+
try {
|
|
2153
|
+
ai_agent_doc = await project_db.get(doc.id);
|
|
2154
|
+
} catch (error) {
|
|
2155
|
+
continue;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
// const ai_agent_ret = await get_ai_doc({ _id: doc.id, uid });
|
|
2159
|
+
let tools = [];
|
|
2160
|
+
let tool_resources = {};
|
|
2161
|
+
// if (ai_agent_ret.code < 0) continue;
|
|
2162
|
+
// const ai_agent_doc = ai_agent_ret.data;
|
|
2163
|
+
for (const [key, val] of Object.entries(ai_agent_doc?.agentConfig?.agent_tools || [])) {
|
|
2164
|
+
const { name, description } = val;
|
|
2165
|
+
switch (val.type) {
|
|
2166
|
+
case 'web_search': {
|
|
2167
|
+
//hosted_web_search
|
|
2168
|
+
tools.push(webSearchTool());
|
|
2169
|
+
break;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
case 'mcp': {
|
|
2173
|
+
tools.push(
|
|
2174
|
+
hostedMcpTool({
|
|
2175
|
+
serverLabel: 'mcp_' + (val?.name?.replaceAll(' ', '_') || key), //ai_agent_doc.properties.menuName,
|
|
2176
|
+
serverUrl: val.url,
|
|
2177
|
+
headers: {
|
|
2178
|
+
Authorization: `Bearer ${val.authentication_key || gtp_token}`,
|
|
2179
|
+
'X-gtp-token': 'true',
|
|
2180
|
+
'Content-Type': 'application/json',
|
|
2181
|
+
},
|
|
2182
|
+
require_approval: 'never',
|
|
2183
|
+
}),
|
|
2184
|
+
);
|
|
2185
|
+
break;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
case 'cpi': {
|
|
2189
|
+
try {
|
|
2190
|
+
debugger;
|
|
2191
|
+
const api_allow_methods = Object.keys(_conf.cpi_methods);
|
|
2192
|
+
for (const method_name of api_allow_methods || []) {
|
|
2193
|
+
const method_prop = _conf.cpi_methods[method_name];
|
|
2194
|
+
|
|
2195
|
+
if (!method_prop) continue;
|
|
2196
|
+
if (method_prop.private) continue;
|
|
2197
|
+
if (!method_prop.description) continue;
|
|
2198
|
+
|
|
2199
|
+
let inputSchema = {};
|
|
2200
|
+
|
|
2201
|
+
const get_z_item = function (key, val) {
|
|
2202
|
+
let prop = {};
|
|
2203
|
+
if (val.type === 'object' && val.items) {
|
|
2204
|
+
for (const [item_key, item_val] of Object.entries(val.items.properties)) {
|
|
2205
|
+
prop[item_key] = get_z_item(item_key, item_val);
|
|
2206
|
+
if (!val?.items?.required?.includes(item_key)) {
|
|
2207
|
+
prop[item_key] = prop[item_key].optional();
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
if (val.type === 'array') {
|
|
2213
|
+
prop = get_z_item('', val.items);
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
let _z = z[val.type](prop);
|
|
2217
|
+
|
|
2218
|
+
if (!val.mandatory) {
|
|
2219
|
+
_z = _z.optional();
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
if (val.enum) {
|
|
2223
|
+
_z = z.enum(val.enum);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
let valid_values = '';
|
|
2227
|
+
if (val.options) {
|
|
2228
|
+
valid_values = val.options.join(', ');
|
|
2229
|
+
}
|
|
2230
|
+
if (val.description) {
|
|
2231
|
+
const desc = val.name || key.replace(/_/g, ' ').replace(/\b(\w)/g, (_, char) => char.toUpperCase());
|
|
2232
|
+
_z = _z.describe((desc ? desc + ' - ' : '') + val.description + (valid_values ? ' Valid values: ' + valid_values : ''));
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
return _z;
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
if (method_prop.fields) {
|
|
2239
|
+
for (const [key, val] of Object.entries(method_prop.fields)) {
|
|
2240
|
+
inputSchema[key] = get_z_item(key, val);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
tools.push(
|
|
2245
|
+
tool({
|
|
2246
|
+
name: method_name.replace(/_/g, ' ').replace(/\b(\w)/g, (_, char) => char.toUpperCase()),
|
|
2247
|
+
description: method_prop.description,
|
|
2248
|
+
parameters: z.object(inputSchema),
|
|
2249
|
+
async execute(e, RunContext) {
|
|
2250
|
+
const params = e;
|
|
2251
|
+
try {
|
|
2252
|
+
const response = await fetch(`http://localhost:3007/cpi/${method_name}`, {
|
|
2253
|
+
method: 'POST',
|
|
2254
|
+
headers: {
|
|
2255
|
+
Accept: 'application/json',
|
|
2256
|
+
'Content-Type': 'application/json',
|
|
2257
|
+
},
|
|
2258
|
+
body: JSON.stringify({ ...params, ...{ gtp_token } }),
|
|
2259
|
+
});
|
|
2260
|
+
const ret = await response.json();
|
|
2261
|
+
return {
|
|
2262
|
+
content: [{ type: 'text', text: JSON.stringify(ret || '', null, 2) }],
|
|
2263
|
+
};
|
|
2264
|
+
} catch (err) {
|
|
2265
|
+
return 'unknown';
|
|
2266
|
+
}
|
|
2267
|
+
},
|
|
2268
|
+
}),
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
debugger;
|
|
2273
|
+
}
|
|
2274
|
+
break;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
// case 'web_search': {
|
|
2278
|
+
// tools.push(
|
|
2279
|
+
// tool({
|
|
2280
|
+
// name: name || val.type,
|
|
2281
|
+
// description: description || val.type,
|
|
2282
|
+
// parameters: z.object({
|
|
2283
|
+
// query: z.string().describe('The search query text.'),
|
|
2284
|
+
// numResults: z.number().int().describe('Number of top search results to return.').default(3),
|
|
2285
|
+
// }),
|
|
2286
|
+
// async execute(e, RunContext) {
|
|
2287
|
+
// const { query, numResults } = e;
|
|
2288
|
+
// try {
|
|
2289
|
+
// const searchUrl = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_redirect=1`;
|
|
2290
|
+
// const response = await fetch(searchUrl);
|
|
2291
|
+
// const data = await response.json();
|
|
2292
|
+
|
|
2293
|
+
// // Simplify results
|
|
2294
|
+
// const results = (data.RelatedTopics || []).slice(0, numResults).map((item) => ({
|
|
2295
|
+
// title: item.Text,
|
|
2296
|
+
// url: item.FirstURL,
|
|
2297
|
+
// }));
|
|
2298
|
+
|
|
2299
|
+
// return JSON.stringify(results, null, 2);
|
|
2300
|
+
// } catch (err) {
|
|
2301
|
+
// return 'unknown';
|
|
2302
|
+
// }
|
|
2303
|
+
// },
|
|
2304
|
+
// }),
|
|
2305
|
+
// );
|
|
2306
|
+
// break;
|
|
2307
|
+
// }
|
|
2308
|
+
|
|
2309
|
+
case 'image_generate': {
|
|
2310
|
+
tools.push(
|
|
2311
|
+
tool({
|
|
2312
|
+
name,
|
|
2313
|
+
description,
|
|
2314
|
+
parameters: z.object({
|
|
2315
|
+
prompt: z.string().describe('The prompt for the image.'),
|
|
2316
|
+
numGenerations: z.number().int().describe('Number generations to image create.').default(3),
|
|
2317
|
+
}),
|
|
2318
|
+
async execute(e, RunContext) {
|
|
2319
|
+
const { prompt, numGenerations = val?.image_generations } = e;
|
|
2320
|
+
try {
|
|
2321
|
+
const base64 = await create_image(prompt, undefined, undefined, numGenerations);
|
|
2322
|
+
// console.log(base64);
|
|
2323
|
+
|
|
2324
|
+
return `<img src="data:image/jpg;base64,${base64}">`;
|
|
2325
|
+
} catch (err) {
|
|
2326
|
+
return 'unknown';
|
|
2327
|
+
}
|
|
2328
|
+
},
|
|
2329
|
+
}),
|
|
2330
|
+
);
|
|
2331
|
+
break;
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
case 'app_builder': {
|
|
2335
|
+
tools.push(
|
|
2336
|
+
tool({
|
|
2337
|
+
name,
|
|
2338
|
+
description,
|
|
2339
|
+
parameters: z.object({
|
|
2340
|
+
prompt: z.string().describe('The prompt for the app builder.'),
|
|
2341
|
+
}),
|
|
2342
|
+
async execute(e, RunContext) {
|
|
2343
|
+
const { prompt } = e;
|
|
2344
|
+
try {
|
|
2345
|
+
const app_id = await get_account_project_id(uid);
|
|
2346
|
+
submit_agentic_studio_chat_gpt_prompt({ uid, app_id, user_prompt: prompt });
|
|
2347
|
+
return;
|
|
2348
|
+
} catch (err) {
|
|
2349
|
+
return 'unknown';
|
|
2350
|
+
}
|
|
2351
|
+
},
|
|
2352
|
+
}),
|
|
2353
|
+
);
|
|
2354
|
+
tools.push(get_studio_table_info_tool);
|
|
2355
|
+
|
|
2356
|
+
tools.push(
|
|
2357
|
+
create_agent_for_table_assistant.asTool({
|
|
2358
|
+
toolName: 'Create Agent for Table Assistant',
|
|
2359
|
+
toolDescription: `Generate table ai agent from the supplied text.`,
|
|
2360
|
+
}),
|
|
2361
|
+
);
|
|
2362
|
+
|
|
2363
|
+
break;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
case 'table': {
|
|
2367
|
+
const table_id = val.prog_id;
|
|
2368
|
+
// prompt += ` for table_id: ${table_id}`;
|
|
2369
|
+
context.table_id = table_id;
|
|
2370
|
+
tools.push(get_studio_table_info_by_table_id_tool);
|
|
2371
|
+
//======== mock data ==========
|
|
2372
|
+
// const mock_data_generator_agent = get_mock_data_generator_agent(app_id, table_id);
|
|
2373
|
+
// tools.push(
|
|
2374
|
+
// mock_data_generator_agent.asTool({
|
|
2375
|
+
// toolName: 'Mock Data Generator',
|
|
2376
|
+
// toolDescription: `generate Mock Data to a table`,
|
|
2377
|
+
// }),
|
|
2378
|
+
// );
|
|
2379
|
+
// tools.push(get_studio_table_info_by_table_id_tool);
|
|
2380
|
+
|
|
2381
|
+
//======== add data ==========
|
|
2382
|
+
tools.push(
|
|
2383
|
+
add_data_to_table_agent.asTool({
|
|
2384
|
+
toolName: 'add data to table',
|
|
2385
|
+
toolDescription: `add data to table`,
|
|
2386
|
+
}),
|
|
2387
|
+
);
|
|
2388
|
+
|
|
2389
|
+
//======== fetch data ==========
|
|
2390
|
+
tools.push(
|
|
2391
|
+
fetch_table_data_agent.asTool({
|
|
2392
|
+
toolName: 'Fetch Table Data Agent',
|
|
2393
|
+
toolDescription: `fetch data from a table`,
|
|
2394
|
+
}),
|
|
2395
|
+
);
|
|
2396
|
+
|
|
2397
|
+
//======== delete data ==========
|
|
2398
|
+
tools.push(
|
|
2399
|
+
delete_table_doc.asTool({
|
|
2400
|
+
toolName: 'Delete Table Doc',
|
|
2401
|
+
toolDescription: `The tool delete document from the database, need to ask for table`,
|
|
2402
|
+
}),
|
|
2403
|
+
);
|
|
2404
|
+
|
|
2405
|
+
tools.push(create_table_management_tool);
|
|
2406
|
+
|
|
2407
|
+
tools.push(create_landing_page_tool);
|
|
2408
|
+
|
|
2409
|
+
// const add_data_to_table_agent = get_add_data_to_table_agent(app_id, table_id);
|
|
2410
|
+
// tools.push(
|
|
2411
|
+
// add_data_to_table_agent.asTool({
|
|
2412
|
+
// toolName: 'add data to table',
|
|
2413
|
+
// toolDescription: `add data to table`,
|
|
2414
|
+
// }),
|
|
2415
|
+
// );
|
|
2416
|
+
|
|
2417
|
+
break;
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
case 'file_search': {
|
|
2421
|
+
if (val?.file?.vector_store_id) {
|
|
2422
|
+
tools.push(fileSearchTool(val.file.vector_store_id));
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
if (val?.youtube?.vector_store_id) {
|
|
2426
|
+
tools.push(fileSearchTool(val.youtube.vector_store_id));
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
break;
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
case 'ai_agent': {
|
|
2433
|
+
if (val.ai_agent_id) {
|
|
2434
|
+
const agent_ret = await get_ai_agents({ _id: val.ai_agent_id });
|
|
2435
|
+
if (agent_ret.code > -1) {
|
|
2436
|
+
const agent_doc = agent_ret.data;
|
|
2437
|
+
|
|
2438
|
+
const agent = new Agent({
|
|
2439
|
+
name: agent_doc.agent_name,
|
|
2440
|
+
instructions: agent_doc.agent_instructions,
|
|
2441
|
+
});
|
|
2442
|
+
|
|
2443
|
+
tools.push(
|
|
2444
|
+
agent.asTool({
|
|
2445
|
+
toolName: agent_doc.agent_name,
|
|
2446
|
+
toolDescription: agent_doc.agent_instructions,
|
|
2447
|
+
}),
|
|
2448
|
+
);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
break;
|
|
2452
|
+
}
|
|
2453
|
+
default:
|
|
2454
|
+
break;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
// if (!tools.length) continue;
|
|
2458
|
+
|
|
2459
|
+
list.push(
|
|
2460
|
+
new Agent({
|
|
2461
|
+
name: ai_agent_doc._id,
|
|
2462
|
+
instructions: ai_agent_doc.agentConfig.agent_instructions,
|
|
2463
|
+
model,
|
|
2464
|
+
tools,
|
|
2465
|
+
metadata: {
|
|
2466
|
+
agent_id: ai_agent_doc._id,
|
|
2467
|
+
ts: Date.now(),
|
|
2468
|
+
},
|
|
2469
|
+
tool_resources,
|
|
2470
|
+
modelSettings,
|
|
2471
|
+
}),
|
|
2472
|
+
);
|
|
2473
|
+
}
|
|
2474
|
+
//tell me more on xuda
|
|
2475
|
+
// debugger;
|
|
2476
|
+
|
|
2477
|
+
return list;
|
|
2478
|
+
};
|
|
2479
|
+
|
|
2480
|
+
try {
|
|
2481
|
+
// if (reference_type === 'agent') {
|
|
2482
|
+
// } else {
|
|
2483
|
+
const runner = new Runner();
|
|
2484
|
+
let _agent;
|
|
2485
|
+
// 2. Build specialist agents list
|
|
2486
|
+
|
|
2487
|
+
const agents = await get_agents();
|
|
2488
|
+
|
|
2489
|
+
if (reference_type === 'ai_agents') {
|
|
2490
|
+
_agent = agents[0];
|
|
2491
|
+
|
|
2492
|
+
try {
|
|
2493
|
+
let agent_doc = await project_db.get(reference_id);
|
|
2494
|
+
agent_doc.ts = Date.now();
|
|
2495
|
+
const save_ret = await project_db.insert(agent_doc);
|
|
2496
|
+
} catch (err) {}
|
|
2497
|
+
} else {
|
|
2498
|
+
// 3. Build triage agent that can hand off
|
|
2499
|
+
_agent = new Agent({
|
|
2500
|
+
name: 'Triage Agent',
|
|
2501
|
+
instructions: 'You are a triage router. Decide which specialist should answer. If general, answer yourself. use web search only as last option',
|
|
2502
|
+
model,
|
|
2503
|
+
handoffs: agents, // this lets triageAgent forward to other agents
|
|
2504
|
+
});
|
|
2505
|
+
|
|
2506
|
+
// 4. Run the triage agent with conversation memory
|
|
2507
|
+
// Runner.runSync / Runner.run depending on your SDK.
|
|
2508
|
+
// We pass { conversationId } so it can keep context across turns.
|
|
2509
|
+
|
|
2510
|
+
// const runner = new Runner();
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
const output = await runner.run(_agent, prompt, {
|
|
2514
|
+
conversationId: conversation_id,
|
|
2515
|
+
context,
|
|
2516
|
+
});
|
|
2517
|
+
// }
|
|
2518
|
+
try {
|
|
2519
|
+
const obj = { id: output?.output?.[0]?.id, ts: Date.now(), usage: { inputTokens: output.state._context.usage.inputTokens, outputTokens: output.state._context.usage.outputTokens }, ai_agent_id: output.state._currentAgent.name };
|
|
2520
|
+
conversation_doc.ts = Date.now();
|
|
2521
|
+
conversation_doc.messages.push(obj);
|
|
2522
|
+
await project_db.insert(conversation_doc);
|
|
2523
|
+
|
|
2524
|
+
if (!conversation_exist) {
|
|
2525
|
+
setTimeout(async () => {
|
|
2526
|
+
// Ask GPT for title
|
|
2527
|
+
const title = await submit_chat_gpt_prompt({
|
|
2528
|
+
prompt: `create title for the prompt return 5 words result maximum text only without options : ${prompt}`,
|
|
2529
|
+
model: 'gpt-5-nano',
|
|
2530
|
+
});
|
|
2531
|
+
|
|
2532
|
+
conversation_doc = await project_db.get(conversation_id);
|
|
2533
|
+
conversation_doc.title = title.data;
|
|
2534
|
+
|
|
2535
|
+
await project_db.insert(conversation_doc);
|
|
2536
|
+
update_thumbnail('conversation', conversation_doc, uid);
|
|
2537
|
+
}, 2500);
|
|
2538
|
+
} else {
|
|
2539
|
+
if (!conversation_doc.base64 && (!conversation_doc.thumbnail_request_ts || Date.now() - conversation_doc.thumbnail_request_ts > 120000)) {
|
|
2540
|
+
update_thumbnail('conversation', conversation_doc, uid);
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
} catch (error) {
|
|
2544
|
+
debugger;
|
|
2545
|
+
}
|
|
2546
|
+
// }
|
|
2547
|
+
|
|
2548
|
+
return {
|
|
2549
|
+
code: 4,
|
|
2550
|
+
data: {
|
|
2551
|
+
conversation_doc,
|
|
2552
|
+
response: output.output, // output.finalOutput ?? output.output ?? output, // depends on SDK shape
|
|
2553
|
+
},
|
|
2554
|
+
};
|
|
2555
|
+
} catch (err) {
|
|
2556
|
+
debugger;
|
|
2557
|
+
return { code: -4, data: err.message || String(err) };
|
|
2558
|
+
}
|
|
2559
|
+
};
|
|
2560
|
+
|
|
2561
|
+
export const submit_chat_gpt_prompt = async function (req) {
|
|
2562
|
+
const { model = 'gpt-5-mini', prompt } = req;
|
|
2563
|
+
try {
|
|
2564
|
+
const response = await client.responses.create({
|
|
2565
|
+
model,
|
|
2566
|
+
input: prompt,
|
|
2567
|
+
});
|
|
2568
|
+
|
|
2569
|
+
return { code: 5, data: response.output_text };
|
|
2570
|
+
} catch (err) {
|
|
2571
|
+
return { code: -5, data: err.message };
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
|
|
2575
|
+
export const create_studio_app = async function (subject) {
|
|
2576
|
+
try {
|
|
2577
|
+
let prompt = `
|
|
2578
|
+
|
|
2579
|
+
1. list all tables and screens to build an ${subject} app.
|
|
2580
|
+
2. return object array for each item (name,type,description)
|
|
2581
|
+
|
|
2582
|
+
|
|
2583
|
+
`;
|
|
2584
|
+
|
|
2585
|
+
const data = await submit_chat_gpt_prompt({ prompt });
|
|
2586
|
+
|
|
2587
|
+
return { code: 6, data };
|
|
2588
|
+
} catch (err) {
|
|
2589
|
+
return { code: -6, data: err.message };
|
|
2590
|
+
}
|
|
2591
|
+
};
|
|
2592
|
+
|
|
2593
|
+
async function fineTuneWithMultipleFiles(datasetPaths, baseModel = 'gpt-4o-mini') {
|
|
2594
|
+
try {
|
|
2595
|
+
console.log('📤 Uploading datasets...');
|
|
2596
|
+
|
|
2597
|
+
// Step 1. Upload all JSONL files in parallel
|
|
2598
|
+
const uploadedFiles = await Promise.all(
|
|
2599
|
+
datasetPaths.map(async (filePath) => {
|
|
2600
|
+
const file = await openai.files.create({
|
|
2601
|
+
file: fs.createReadStream(path.resolve(filePath)),
|
|
2602
|
+
purpose: 'fine-tune',
|
|
2603
|
+
});
|
|
2604
|
+
console.log(`✅ Uploaded: ${file.filename} → ID: ${file.id}`);
|
|
2605
|
+
return file.id;
|
|
2606
|
+
}),
|
|
2607
|
+
);
|
|
2608
|
+
|
|
2609
|
+
// Step 2. Create fine-tuning job with multiple training files
|
|
2610
|
+
console.log(`🚀 Creating fine-tuning job for ${baseModel}...`);
|
|
2611
|
+
const fineTuneJob = await openai.fineTuning.jobs.create({
|
|
2612
|
+
training_files: uploadedFiles.map((id) => ({ file_id: id })), // <-- multiple files here
|
|
2613
|
+
model: baseModel,
|
|
2614
|
+
});
|
|
2615
|
+
|
|
2616
|
+
console.log(`📌 Fine-tuning job created: ${fineTuneJob.id}`);
|
|
2617
|
+
|
|
2618
|
+
// Step 3. Poll job status until training completes
|
|
2619
|
+
let status = fineTuneJob.status;
|
|
2620
|
+
while (status === 'pending' || status === 'running') {
|
|
2621
|
+
console.log(`⏳ Job status: ${status}...`);
|
|
2622
|
+
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
2623
|
+
|
|
2624
|
+
const updatedJob = await openai.fineTuning.jobs.retrieve(fineTuneJob.id);
|
|
2625
|
+
status = updatedJob.status;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
// Step 4. Check final status
|
|
2629
|
+
if (status === 'succeeded') {
|
|
2630
|
+
console.log('🎉 Fine-tuning completed successfully!');
|
|
2631
|
+
console.log(`🆕 Fine-tuned model: ${fineTuneJob.fine_tuned_model}`);
|
|
2632
|
+
return fineTuneJob.fine_tuned_model;
|
|
2633
|
+
} else {
|
|
2634
|
+
throw new Error(`❌ Fine-tuning failed: ${status}`);
|
|
2635
|
+
}
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
console.error('⚠️ Fine-tuning error:', error);
|
|
2638
|
+
throw error;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
const create_image = async function (prompt, model = 'gpt-image-1-mini', size = '1024x1024', n = 1) {
|
|
2643
|
+
try {
|
|
2644
|
+
const response = await client.images.generate({
|
|
2645
|
+
model,
|
|
2646
|
+
prompt,
|
|
2647
|
+
size,
|
|
2648
|
+
n,
|
|
2649
|
+
});
|
|
2650
|
+
|
|
2651
|
+
// Try to extract base64 directly
|
|
2652
|
+
const imgObj = response.data?.[0];
|
|
2653
|
+
if (!imgObj) throw new Error('No image returned from OpenAI');
|
|
2654
|
+
|
|
2655
|
+
// different SDK builds call it slightly differently, so normalize:
|
|
2656
|
+
const rawBase64 = imgObj.b64_json || imgObj.image_base64 || imgObj.base64 || imgObj.data || null;
|
|
2657
|
+
|
|
2658
|
+
if (!rawBase64) {
|
|
2659
|
+
// Fallback: if we *do* have a URL (some builds give both),
|
|
2660
|
+
// then we can download it.
|
|
2661
|
+
if (imgObj.url) {
|
|
2662
|
+
const res = await fetch(imgObj.url);
|
|
2663
|
+
if (!res.ok) {
|
|
2664
|
+
throw new Error(`Failed to fetch image URL: ${res.status} ${res.statusText}`);
|
|
2665
|
+
}
|
|
2666
|
+
const arrayBuffer = await res.arrayBuffer();
|
|
2667
|
+
const fullImgBuffer = Buffer.from(arrayBuffer);
|
|
2668
|
+
|
|
2669
|
+
// Resize to 128x128
|
|
2670
|
+
const resized = await sharp(fullImgBuffer).resize(128, 128).toFormat('png').toBuffer();
|
|
2671
|
+
|
|
2672
|
+
return resized.toString('base64');
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
throw new Error('No base64 and no URL in image response');
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
// We got base64 from the API, so no extra fetch needed
|
|
2679
|
+
const fullImgBuffer = Buffer.from(rawBase64, 'base64');
|
|
2680
|
+
|
|
2681
|
+
// Resize to 128x128
|
|
2682
|
+
const resized = await sharp(fullImgBuffer).resize(128, 128).toFormat('png').toBuffer();
|
|
2683
|
+
|
|
2684
|
+
const thumbnailBase64 = resized.toString('base64');
|
|
2685
|
+
return thumbnailBase64;
|
|
2686
|
+
} catch (err) {
|
|
2687
|
+
console.error('create_image error:', err);
|
|
2688
|
+
return '';
|
|
2689
|
+
}
|
|
2690
|
+
};
|
|
2691
|
+
|
|
2692
|
+
const get_ai_doc = async function (req) {
|
|
2693
|
+
let { uid, _id } = req;
|
|
2694
|
+
|
|
2695
|
+
const project_db = await get_account_project_db(uid);
|
|
2696
|
+
try {
|
|
2697
|
+
let data = await project_db.get(_id);
|
|
2698
|
+
if (data?.studio_meta?.createdByUid !== uid) delete data.agentConfig; //throw new Error('doctype not supported');
|
|
2699
|
+
return { code: 7, data };
|
|
2700
|
+
} catch (err) {
|
|
2701
|
+
return { code: -7, data: err.message };
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
// const db = get_xuda_couch('xuda_contacts');
|
|
2705
|
+
|
|
2706
|
+
// try {
|
|
2707
|
+
// let data = await db.get(_id);
|
|
2708
|
+
// if (!['ai_agent', 'chat_conversation'].includes(data.docType)) throw new Error('doctype not supported');
|
|
2709
|
+
// return { code: 1, data };
|
|
2710
|
+
// } catch (err) {
|
|
2711
|
+
// return { code: -1, data: err.message };
|
|
2712
|
+
// }
|
|
2713
|
+
};
|
|
2714
|
+
|
|
2715
|
+
export const get_ai_agents = async function (req) {
|
|
2716
|
+
let { uid, _id } = req;
|
|
2717
|
+
|
|
2718
|
+
try {
|
|
2719
|
+
const project_db = await get_account_project_db(uid);
|
|
2720
|
+
|
|
2721
|
+
if (_id) return await get_ai_doc({ _id, uid });
|
|
2722
|
+
|
|
2723
|
+
const user_agents = await project_db.view('xuda', 'studio_docs_by_type', {
|
|
2724
|
+
key: 'ai_agent',
|
|
2725
|
+
include_docs: true,
|
|
2726
|
+
});
|
|
2727
|
+
|
|
2728
|
+
return { code: 8, data: { docs: user_agents.rows.map((row) => row.value) } };
|
|
2729
|
+
} catch (err) {
|
|
2730
|
+
return { code: -8, data: err.message };
|
|
2731
|
+
}
|
|
2732
|
+
};
|
|
2733
|
+
|
|
2734
|
+
export const delete_ai_agent = async function (req) {
|
|
2735
|
+
let { agent_id, uid } = req;
|
|
2736
|
+
|
|
2737
|
+
const project_db = await get_account_project_db(uid);
|
|
2738
|
+
|
|
2739
|
+
try {
|
|
2740
|
+
let agent_doc = await project_db.get(agent_id);
|
|
2741
|
+
agent_doc.stat = 4;
|
|
2742
|
+
agent_doc.ts = Date.now();
|
|
2743
|
+
const data = await project_db.insert(agent_doc);
|
|
2744
|
+
|
|
2745
|
+
for (const [key, val] of Object.entries(agent_doc?.agentConfig?.agent_tools || [])) {
|
|
2746
|
+
const file_id = val?.youtube?.file_id || val?.file?.file_id;
|
|
2747
|
+
const vector_store_id = val?.youtube?.vector_store_id || val?.file?.vector_store_id;
|
|
2748
|
+
if (file_id) {
|
|
2749
|
+
if (vector_store_id) {
|
|
2750
|
+
const deletedVectorStoreFile = await client.vectorStores.files.delete(file_id, { vector_store_id });
|
|
2751
|
+
}
|
|
2752
|
+
await client.files.delete(file_id);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
|
|
2756
|
+
return { code: 9, data };
|
|
2757
|
+
} catch (err) {
|
|
2758
|
+
return { code: -9, data: err.message };
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
// const db = get_xuda_couch('xuda_contacts');
|
|
2762
|
+
// try {
|
|
2763
|
+
// let agent_doc = await db.get(agent_id);
|
|
2764
|
+
// agent_doc.stat = 4;
|
|
2765
|
+
// agent_doc.ts = Date.now();
|
|
2766
|
+
// const data = await db.insert(agent_doc);
|
|
2767
|
+
|
|
2768
|
+
// return { code: 1, data };
|
|
2769
|
+
// } catch (err) {
|
|
2770
|
+
// return { code: -1, data: err.message };
|
|
2771
|
+
// }
|
|
2772
|
+
};
|
|
2773
|
+
|
|
2774
|
+
export const delete_mini_app = async function (req) {
|
|
2775
|
+
let { prog_id, uid } = req;
|
|
2776
|
+
|
|
2777
|
+
const project_db = await get_account_project_db(uid);
|
|
2778
|
+
|
|
2779
|
+
try {
|
|
2780
|
+
let prog_doc = await project_db.get(prog_id);
|
|
2781
|
+
prog_doc.stat = 4;
|
|
2782
|
+
prog_doc.ts = Date.now();
|
|
2783
|
+
const data = await project_db.insert(prog_doc);
|
|
2784
|
+
|
|
2785
|
+
return { code: 29, data };
|
|
2786
|
+
} catch (err) {
|
|
2787
|
+
return { code: -29, data: err.message };
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
|
|
2791
|
+
export const update_ai_agent = async function (req) {
|
|
2792
|
+
let { agent_id, agentConfig, uid } = req;
|
|
2793
|
+
|
|
2794
|
+
const project_db = await get_account_project_db(uid);
|
|
2795
|
+
|
|
2796
|
+
try {
|
|
2797
|
+
let agent_doc = await project_db.get(agent_id);
|
|
2798
|
+
|
|
2799
|
+
agent_doc.ts = Date.now();
|
|
2800
|
+
agent_doc.agentConfig = agentConfig;
|
|
2801
|
+
|
|
2802
|
+
agent_doc.studio_meta.agent_image = agentConfig.agent_image;
|
|
2803
|
+
|
|
2804
|
+
const data = await project_db.insert(agent_doc);
|
|
2805
|
+
|
|
2806
|
+
update_thumbnail('ai_agent', agent_doc, uid);
|
|
2807
|
+
upload_agent_files(uid, agent_doc);
|
|
2808
|
+
return { code: 10, data };
|
|
2809
|
+
} catch (err) {
|
|
2810
|
+
return { code: -10, data: err.message };
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
|
|
2814
|
+
export const get_apps = async function (req) {
|
|
2815
|
+
let { uid, _id } = req;
|
|
2816
|
+
|
|
2817
|
+
try {
|
|
2818
|
+
const project_db = await get_account_project_db(uid);
|
|
2819
|
+
|
|
2820
|
+
if (_id) return await get_ai_doc({ _id, uid });
|
|
2821
|
+
|
|
2822
|
+
const user_agents = await project_db.view('xuda', 'studio_mini_apps', {
|
|
2823
|
+
include_docs: true,
|
|
2824
|
+
});
|
|
2825
|
+
|
|
2826
|
+
return { code: 11, data: { docs: user_agents.rows.map((row) => row.value) } };
|
|
2827
|
+
} catch (err) {
|
|
2828
|
+
return { code: -11, data: err.message };
|
|
2829
|
+
}
|
|
2830
|
+
};
|
|
2831
|
+
|
|
2832
|
+
const getExtension = function (filename) {
|
|
2833
|
+
return path.extname(filename).toLowerCase();
|
|
2834
|
+
};
|
|
2835
|
+
|
|
2836
|
+
const upload_agent_files = async function (uid, agent_doc) {
|
|
2837
|
+
let { agentConfig } = agent_doc;
|
|
2838
|
+
for await (let tool of agentConfig.agent_tools || []) {
|
|
2839
|
+
if (tool.type === 'file_search') {
|
|
2840
|
+
if (!_.isEmpty(tool.file)) {
|
|
2841
|
+
if (!tool.file.file_id) {
|
|
2842
|
+
const file_ext = getExtension(tool.file.name);
|
|
2843
|
+
|
|
2844
|
+
switch (file_ext) {
|
|
2845
|
+
case 'mp3':
|
|
2846
|
+
case 'mp4':
|
|
2847
|
+
await save_agent_status(uid, agent_doc._id, 2, agentConfig);
|
|
2848
|
+
const file_stream = await get_drive_file_stream(tool.file.name);
|
|
2849
|
+
const transcript = await transcribe(file_stream);
|
|
2850
|
+
|
|
2851
|
+
const file = new File([Buffer.from(transcript, 'utf8')], tool.file.name, {
|
|
2852
|
+
type: 'text/plain',
|
|
2853
|
+
});
|
|
2854
|
+
|
|
2855
|
+
// const stream = textToStream(transcript);
|
|
2856
|
+
const uploadedFile = await client.files.create({
|
|
2857
|
+
file,
|
|
2858
|
+
purpose: 'assistants',
|
|
2859
|
+
});
|
|
2860
|
+
tool.file.file_id = uploadedFile.id;
|
|
2861
|
+
|
|
2862
|
+
break;
|
|
2863
|
+
|
|
2864
|
+
default:
|
|
2865
|
+
tool.file.file_id = await upload_file_to_openai_storage(tool.file.name);
|
|
2866
|
+
break;
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
if (!tool.file.vector_file_id) {
|
|
2870
|
+
const ret = await client.vectorStores.files.create(_conf.OPENAI_VECTOR_STORE_ID, {
|
|
2871
|
+
file_id: tool.file.file_id,
|
|
2872
|
+
});
|
|
2873
|
+
tool.file.vector_store_id = ret.vector_store_id;
|
|
2874
|
+
}
|
|
2875
|
+
await save_agent_status(uid, agent_doc._id, 3, agentConfig);
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
if (!_.isEmpty(tool?.youtube)) {
|
|
2880
|
+
if (!_.isEmpty(tool?.youtube.url)) {
|
|
2881
|
+
if (!tool.youtube.file_id) {
|
|
2882
|
+
await save_agent_status(uid, agent_doc._id, 2, agentConfig);
|
|
2883
|
+
tool.youtube.file_name = await download_yt_audio(tool.youtube.url);
|
|
2884
|
+
|
|
2885
|
+
const tmp_file = '/tmp/' + tool.youtube.file_name;
|
|
2886
|
+
const file_stream = fs.createReadStream(tmp_file);
|
|
2887
|
+
const transcript = await transcribe(file_stream);
|
|
2888
|
+
const file = new File([Buffer.from(transcript, 'utf8')], tool.youtube.file_name, {
|
|
2889
|
+
type: 'text/plain',
|
|
2890
|
+
});
|
|
2891
|
+
// const stream = textToStream(transcript);
|
|
2892
|
+
|
|
2893
|
+
const uploadedFile = await client.files.create({
|
|
2894
|
+
file,
|
|
2895
|
+
purpose: 'assistants',
|
|
2896
|
+
});
|
|
2897
|
+
|
|
2898
|
+
tool.youtube.file_id = uploadedFile.id;
|
|
2899
|
+
|
|
2900
|
+
if (!tool.youtube.vector_file_id) {
|
|
2901
|
+
const ret = await client.vectorStores.files.create(_conf.OPENAI_VECTOR_STORE_ID, {
|
|
2902
|
+
file_id: tool.youtube.file_id,
|
|
2903
|
+
});
|
|
2904
|
+
tool.youtube.vector_store_id = ret.vector_store_id;
|
|
2905
|
+
}
|
|
2906
|
+
await save_agent_status(uid, agent_doc._id, 3, agentConfig);
|
|
2907
|
+
|
|
2908
|
+
// stream.on('end', () => {
|
|
2909
|
+
// unlink(tmp_file).catch(console.error);
|
|
2910
|
+
// });
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
};
|
|
2917
|
+
|
|
2918
|
+
const validate_ai_agent = async function (req) {
|
|
2919
|
+
// "tool_type": {
|
|
2920
|
+
// "name": "Tool Type",
|
|
2921
|
+
// "description": "",
|
|
2922
|
+
// "type": "array",
|
|
2923
|
+
// "enum": [
|
|
2924
|
+
// "web_search",
|
|
2925
|
+
// "file_search",
|
|
2926
|
+
// "program",
|
|
2927
|
+
// "mcp",
|
|
2928
|
+
// "plugin",
|
|
2929
|
+
// "ai_agent",
|
|
2930
|
+
// "table",
|
|
2931
|
+
// "image_generate"
|
|
2932
|
+
// ],
|
|
2933
|
+
// "mandatory": true
|
|
2934
|
+
// }
|
|
2935
|
+
};
|
|
2936
|
+
|
|
2937
|
+
const save_agent_status = async function (uid, agent_id, stat, agentConfig) {
|
|
2938
|
+
const project_db = await get_account_project_db(uid);
|
|
2939
|
+
|
|
2940
|
+
try {
|
|
2941
|
+
let agent_doc = await project_db.get(agent_id);
|
|
2942
|
+
agent_doc.ts = Date.now();
|
|
2943
|
+
agent_doc.stat = stat;
|
|
2944
|
+
agent_doc.agentConfig = agentConfig;
|
|
2945
|
+
const data = await project_db.insert(agent_doc);
|
|
2946
|
+
|
|
2947
|
+
return { code: 12, data };
|
|
2948
|
+
} catch (err) {
|
|
2949
|
+
return { code: -12, data: err.message };
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
|
|
2953
|
+
export const create_ai_agent = async function (req) {
|
|
2954
|
+
const { agentConfig, uid } = req;
|
|
2955
|
+
|
|
2956
|
+
const { account_project_id, account_info } = await get_account_object(uid);
|
|
2957
|
+
|
|
2958
|
+
try {
|
|
2959
|
+
const param = { uid, properties: { menuName: agentConfig.agent_name, menuTitle: agentConfig.agent_name, menuType: 'ai_agent' }, checkedInUserName: account_info.first_name + ' ' + account_info.last_name, app_id: account_project_id, parentId: 'ai_agents' };
|
|
2960
|
+
var agent_doc = createDoc(param);
|
|
2961
|
+
agent_doc.agentConfig = agentConfig;
|
|
2962
|
+
|
|
2963
|
+
// const data = await db.insert(agent_doc);
|
|
2964
|
+
const project_db = await get_account_project_db(uid);
|
|
2965
|
+
const data = await project_db.insert(agent_doc);
|
|
2966
|
+
|
|
2967
|
+
update_thumbnail('ai_agent', agent_doc, uid);
|
|
2968
|
+
upload_agent_files(uid, agent_doc);
|
|
2969
|
+
return { code: 13, data };
|
|
2970
|
+
} catch (err) {
|
|
2971
|
+
return { code: -13, data: err.message };
|
|
2972
|
+
}
|
|
2973
|
+
};
|
|
2974
|
+
|
|
2975
|
+
const update_thumbnail = function (type, doc, uid) {
|
|
2976
|
+
let prompt = '';
|
|
2977
|
+
|
|
2978
|
+
setTimeout(async () => {
|
|
2979
|
+
try {
|
|
2980
|
+
const project_db = await get_account_project_db(uid);
|
|
2981
|
+
let db_doc = await project_db.get(doc._id);
|
|
2982
|
+
|
|
2983
|
+
switch (type) {
|
|
2984
|
+
case 'ai_agent': {
|
|
2985
|
+
if (db_doc.studio_meta.agent_image_base64 && db_doc.properties.menuName === db_doc.properties.menuName) return;
|
|
2986
|
+
|
|
2987
|
+
prompt = `create image:
|
|
2988
|
+
|
|
2989
|
+
Overview
|
|
2990
|
+
The image portrays a futuristic humanoid or abstract AI entity designed to represent a specific role — such as a sales assistant, data analyst, creative advisor, or search agent. The scene blends cutting-edge technology with an artistic or cinematic atmosphere, creating a visual narrative of intelligence, precision, and innovation.
|
|
2991
|
+
|
|
2992
|
+
Subject
|
|
2993
|
+
|
|
2994
|
+
The central figure (or core system) embodies artificial intelligence personified.
|
|
2995
|
+
It may appear as a humanoid robot, a digital avatar, or a symbolic interface made of light and code. The features — whether facial, mechanical, or abstract — convey awareness, focus, and adaptability. The subject’s posture and expression (if applicable) suggest calm confidence, curiosity, or analytical engagement.
|
|
2996
|
+
|
|
2997
|
+
🕶 Appearance
|
|
2998
|
+
|
|
2999
|
+
The AI’s form is designed to match its role:
|
|
3000
|
+
|
|
3001
|
+
|
|
3002
|
+
|
|
3003
|
+
${doc.name}
|
|
3004
|
+
|
|
3005
|
+
Reflective or metallic textures emphasize intelligence and sophistication, while the surrounding lighting harmonizes with the AI’s color scheme to project its purpose and tone.
|
|
3006
|
+
|
|
3007
|
+
Interface & Interaction
|
|
3008
|
+
|
|
3009
|
+
The AI often interacts with floating holographic or projected interfaces, symbolizing its digital workspace.
|
|
3010
|
+
Common elements include:
|
|
3011
|
+
|
|
3012
|
+
Graphs, data panels, dashboards, or search bars.
|
|
3013
|
+
|
|
3014
|
+
Abstract geometric shapes representing logic or neural activity.
|
|
3015
|
+
|
|
3016
|
+
Light trails or motion blur effects suggesting processing or communication.
|
|
3017
|
+
|
|
3018
|
+
The AI engaging with gestures — pointing, typing in mid-air, or scanning visual elements.
|
|
3019
|
+
|
|
3020
|
+
These details illustrate the agent’s domain: sales forecasting, query resolution, creative generation, or data interpretation.
|
|
3021
|
+
|
|
3022
|
+
Environment & Lighting
|
|
3023
|
+
|
|
3024
|
+
The environment bridges the real and virtual worlds — a fusion of architecture, code, and energy.
|
|
3025
|
+
It may include:
|
|
3026
|
+
|
|
3027
|
+
Corporate or futuristic interiors (clean walls, neon highlights, glass surfaces).
|
|
3028
|
+
|
|
3029
|
+
Abstract digital landscapes filled with data grids or holographic streams.
|
|
3030
|
+
|
|
3031
|
+
Natural or surreal elements (roses, water reflections, sky motifs) that humanize the setting.
|
|
3032
|
+
|
|
3033
|
+
Lighting contrasts — blue/orange, violet/silver, gold/teal — create emotional tension between logic and inspiration.
|
|
3034
|
+
|
|
3035
|
+
Mood & Style
|
|
3036
|
+
|
|
3037
|
+
The overall mood is visionary and intelligent, evoking trust and advancement.
|
|
3038
|
+
The visual style can vary:
|
|
3039
|
+
|
|
3040
|
+
Cyber-surreal: dreamlike, poetic, glowing.
|
|
3041
|
+
|
|
3042
|
+
Corporate-futuristic: clean, efficient, minimalistic.
|
|
3043
|
+
|
|
3044
|
+
Cinematic-AI realism: detailed, photorealistic, story-driven.
|
|
3045
|
+
|
|
3046
|
+
Composition centers the AI or interface as the focal point, surrounded by hints of motion, light, and context. The image symbolizes the fusion of human creativity and machine intelligence.`;
|
|
3047
|
+
db_doc.studio_meta.thumbnail_request_ts = Date.now();
|
|
3048
|
+
const base64 = await create_image(prompt);
|
|
3049
|
+
db_doc.studio_meta.agent_image_base64 = base64;
|
|
3050
|
+
|
|
3051
|
+
break;
|
|
3052
|
+
}
|
|
3053
|
+
case 'conversation': {
|
|
3054
|
+
prompt = `create image: ${doc.title}`;
|
|
3055
|
+
db_doc.thumbnail_request_ts = Date.now();
|
|
3056
|
+
const base64 = await create_image(prompt);
|
|
3057
|
+
db_doc.base64 = base64;
|
|
3058
|
+
break;
|
|
3059
|
+
}
|
|
3060
|
+
|
|
3061
|
+
case 'mini_app': {
|
|
3062
|
+
prompt = `create image: ${db_doc.properties.menuName}`;
|
|
3063
|
+
db_doc.studio_meta.thumbnail_request_ts = Date.now();
|
|
3064
|
+
const base64 = await create_image(prompt);
|
|
3065
|
+
db_doc.studio_meta.agent_image_base64 = base64;
|
|
3066
|
+
break;
|
|
3067
|
+
}
|
|
3068
|
+
|
|
3069
|
+
default:
|
|
3070
|
+
prompt = '';
|
|
3071
|
+
break;
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
const save_ret = await project_db.insert(db_doc);
|
|
3075
|
+
} catch (error) {
|
|
3076
|
+
debugger;
|
|
3077
|
+
}
|
|
3078
|
+
}, 500);
|
|
3079
|
+
};
|
|
3080
|
+
const get_drive_file_stream = async function (file_path) {
|
|
3081
|
+
const filename = path.basename(file_path);
|
|
3082
|
+
const db = get_xuda_couch('xuda_drive');
|
|
3083
|
+
const ret = await db.find({
|
|
3084
|
+
selector: {
|
|
3085
|
+
docType: 'user_drive',
|
|
3086
|
+
type: 'file',
|
|
3087
|
+
file_path: path.dirname(file_path) === '.' ? '/' : path.dirname(file_path),
|
|
3088
|
+
originalname: filename,
|
|
3089
|
+
stat: 3,
|
|
3090
|
+
},
|
|
3091
|
+
});
|
|
3092
|
+
const doc = ret?.docs?.[0];
|
|
3093
|
+
const _region = process.env.XUDA_HOSTNAME; // "eu.xuda.io"; //
|
|
3094
|
+
let file_location = doc?.bucket?.[_region]?.Location;
|
|
3095
|
+
|
|
3096
|
+
try {
|
|
3097
|
+
// const res = await fetch(file_location);
|
|
3098
|
+
|
|
3099
|
+
let response;
|
|
3100
|
+
try {
|
|
3101
|
+
response = await fetch(file_location);
|
|
3102
|
+
} catch (error) {
|
|
3103
|
+
// fix bucket bug
|
|
3104
|
+
try {
|
|
3105
|
+
response = await fetch(file_location.replace(/([^\/]+)\/([^\/]+)\/(.*)/, 'https://$2.$1/$3'));
|
|
3106
|
+
} catch (error) {
|
|
3107
|
+
throw error;
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
if (!response.ok) throw new Error(`Failed to download file: ${response.statusText}`);
|
|
3112
|
+
|
|
3113
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
3114
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
3115
|
+
|
|
3116
|
+
const tmp_file = '/tmp/' + filename;
|
|
3117
|
+
await writeFile(tmp_file, buffer);
|
|
3118
|
+
// 3️⃣ Upload file to OpenAI
|
|
3119
|
+
const stream = fs.createReadStream(tmp_file);
|
|
3120
|
+
|
|
3121
|
+
// unlink(tmp_file);
|
|
3122
|
+
|
|
3123
|
+
stream.on('end', () => {
|
|
3124
|
+
unlink(tmp_file).catch(console.error);
|
|
3125
|
+
});
|
|
3126
|
+
|
|
3127
|
+
return stream;
|
|
3128
|
+
} catch (err) {
|
|
3129
|
+
console.error(err);
|
|
3130
|
+
}
|
|
3131
|
+
};
|
|
3132
|
+
|
|
3133
|
+
const transcribe = async function (stream) {
|
|
3134
|
+
try {
|
|
3135
|
+
// const stream = await get_drive_file_stream(filePath);
|
|
3136
|
+
// debugger;
|
|
3137
|
+
const resp = await client.audio.transcriptions.create({
|
|
3138
|
+
// Choose one model:
|
|
3139
|
+
model: 'whisper-1', // or "gpt-4o-transcribe", "whisper-1"
|
|
3140
|
+
file: stream, //fs.createReadStream(filePath),
|
|
3141
|
+
// Optional:
|
|
3142
|
+
response_format: 'text', // "text" | "json" | "verbose_json" | "srt" | "vtt"
|
|
3143
|
+
language: 'en', // hint language (ISO 639-1), e.g. "he" for Hebrew
|
|
3144
|
+
// temperature: 0 // lower = more literal
|
|
3145
|
+
});
|
|
3146
|
+
|
|
3147
|
+
console.log(resp); // or resp for JSON formats
|
|
3148
|
+
return resp;
|
|
3149
|
+
} catch (error) {
|
|
3150
|
+
debugger;
|
|
3151
|
+
}
|
|
3152
|
+
};
|
|
3153
|
+
|
|
3154
|
+
const upload_file_to_openai_storage = async function (file_path) {
|
|
3155
|
+
// const filename = path.basename(file_path);
|
|
3156
|
+
// const db = get_xuda_couch('xuda_drive');
|
|
3157
|
+
// const ret = await db.find({
|
|
3158
|
+
// selector: {
|
|
3159
|
+
// docType: 'user_drive',
|
|
3160
|
+
// type: 'file',
|
|
3161
|
+
// file_path: path.dirname(file_path) === '.' ? '/' : path.dirname(file_path),
|
|
3162
|
+
// originalname: filename,
|
|
3163
|
+
// stat: 3,
|
|
3164
|
+
// },
|
|
3165
|
+
// });
|
|
3166
|
+
// const doc = ret?.docs?.[0];
|
|
3167
|
+
// const _region = process.env.XUDA_HOSTNAME; // "eu.xuda.io"; //
|
|
3168
|
+
// let file_location = doc?.bucket?.[_region]?.Location;
|
|
3169
|
+
|
|
3170
|
+
try {
|
|
3171
|
+
// const res = await fetch(file_location);
|
|
3172
|
+
|
|
3173
|
+
// if (!res.ok) throw new Error(`Failed to download file: ${res.statusText}`);
|
|
3174
|
+
|
|
3175
|
+
// const arrayBuffer = await res.arrayBuffer();
|
|
3176
|
+
// const buffer = Buffer.from(arrayBuffer);
|
|
3177
|
+
|
|
3178
|
+
// const tmp_file = '/tmp/' + filename;
|
|
3179
|
+
// await writeFile(tmp_file, buffer);
|
|
3180
|
+
// 3️⃣ Upload file to OpenAI
|
|
3181
|
+
// const stream = fs.createReadStream(tmp_file);
|
|
3182
|
+
|
|
3183
|
+
const stream = await get_drive_file_stream(file_path);
|
|
3184
|
+
|
|
3185
|
+
const uploadedFile = await client.files.create({
|
|
3186
|
+
file: stream,
|
|
3187
|
+
purpose: 'assistants',
|
|
3188
|
+
});
|
|
3189
|
+
|
|
3190
|
+
// const file = await toFile(stream, filename, { purpose: 'assistants' });
|
|
3191
|
+
// debugger;
|
|
3192
|
+
// const uploadedFile = await client.vectorStores.files.create(_conf.OPENAI_VECTOR_STORE_ID, {
|
|
3193
|
+
// file_id: tmp_file,
|
|
3194
|
+
// });
|
|
3195
|
+
|
|
3196
|
+
console.log('📂 Uploaded file:', uploadedFile.id);
|
|
3197
|
+
unlink(tmp_file);
|
|
3198
|
+
return uploadedFile.id;
|
|
3199
|
+
} catch (err) {
|
|
3200
|
+
console.error(err);
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
|
|
3204
|
+
const get_account_object = async function (uid) {
|
|
3205
|
+
const db = get_xuda_couch('xuda_accounts');
|
|
3206
|
+
const account_obj = await db.get(uid);
|
|
3207
|
+
return account_obj;
|
|
3208
|
+
};
|
|
3209
|
+
|
|
3210
|
+
const get_account_project_id = async function (uid) {
|
|
3211
|
+
const account_obj = await get_account_object(uid);
|
|
3212
|
+
return account_obj.account_project_id;
|
|
3213
|
+
};
|
|
3214
|
+
|
|
3215
|
+
const get_account_project_db = async function (uid) {
|
|
3216
|
+
const user_project_id = await get_account_project_id(uid);
|
|
3217
|
+
|
|
3218
|
+
const project_db = await get_project_db(user_project_id);
|
|
3219
|
+
return project_db;
|
|
3220
|
+
};
|
|
3221
|
+
|
|
3222
|
+
const get_app_obj = async function (app_id) {
|
|
3223
|
+
const db = get_xuda_couch('xuda_master');
|
|
3224
|
+
const app_obj = await db.get(app_id);
|
|
3225
|
+
return app_obj;
|
|
3226
|
+
};
|
|
3227
|
+
|
|
3228
|
+
const get_project_db = async function (app_id) {
|
|
3229
|
+
const app_obj = await get_app_obj(app_id);
|
|
3230
|
+
const project_db = get_xuda_couch(app_obj.app_db_name);
|
|
3231
|
+
return project_db;
|
|
3232
|
+
};
|
|
3233
|
+
|
|
3234
|
+
const download_yt_audio = function (videoUrl) {
|
|
3235
|
+
const extractVideoId = function (url) {
|
|
3236
|
+
const regex = /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\n?#]+)/;
|
|
3237
|
+
const match = url.match(regex);
|
|
3238
|
+
return match ? match[1] : null;
|
|
3239
|
+
};
|
|
3240
|
+
|
|
3241
|
+
const filename = extractVideoId(videoUrl) + '.m4a';
|
|
3242
|
+
const output = '/tmp/' + filename;
|
|
3243
|
+
return new Promise((resolve, reject) => {
|
|
3244
|
+
class AdvancedYouTubeDownloader {
|
|
3245
|
+
constructor() {
|
|
3246
|
+
this.retryCount = 0;
|
|
3247
|
+
this.maxRetries = 3;
|
|
3248
|
+
this.proxyList = [_conf.PROXY_HTTP_URL];
|
|
3249
|
+
this.currentProxyIndex = 0;
|
|
3250
|
+
this.usePuppeteerFallback = true;
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
async download(videoUrl) {
|
|
3254
|
+
try {
|
|
3255
|
+
console.log(`Starting download attempt ${this.retryCount + 1}`);
|
|
3256
|
+
await this.downloadWithCurrentStrategy(videoUrl);
|
|
3257
|
+
return filename;
|
|
3258
|
+
} catch (error) {
|
|
3259
|
+
console.error(`Attempt ${this.retryCount + 1} failed: ${error.message}`);
|
|
3260
|
+
|
|
3261
|
+
if (this.retryCount < this.maxRetries) {
|
|
3262
|
+
this.retryCount++;
|
|
3263
|
+
await this.delay(2000 * this.retryCount);
|
|
3264
|
+
this.download(videoUrl);
|
|
3265
|
+
return filename;
|
|
3266
|
+
} else if (this.usePuppeteerFallback) {
|
|
3267
|
+
console.log('All direct downloads failed, trying Puppeteer fallback...');
|
|
3268
|
+
await this.downloadWithPuppeteer(videoUrl);
|
|
3269
|
+
return filename;
|
|
3270
|
+
} else {
|
|
3271
|
+
throw new Error(`All download strategies failed`);
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
async downloadWithCurrentStrategy(videoUrl) {
|
|
3277
|
+
const options = {
|
|
3278
|
+
// Audio extraction and conversion
|
|
3279
|
+
extractAudio: true,
|
|
3280
|
+
audioFormat: 'mp3',
|
|
3281
|
+
audioQuality: 9, // Lowest quality for smallest file size
|
|
3282
|
+
|
|
3283
|
+
// Force MP3 output with specific extension
|
|
3284
|
+
output, //: '/tmp/%(title)s.mp3',
|
|
3285
|
+
|
|
3286
|
+
// Force conversion with low quality settings
|
|
3287
|
+
postprocessorArgs: [
|
|
3288
|
+
'ffmpeg:-c:a',
|
|
3289
|
+
'libmp3lame',
|
|
3290
|
+
'-q:a',
|
|
3291
|
+
'9', // Lowest quality (0-9, where 9 is worst)
|
|
3292
|
+
'-ac',
|
|
3293
|
+
'1', // Mono audio
|
|
3294
|
+
'-ar',
|
|
3295
|
+
'22050', // Low sample rate
|
|
3296
|
+
'-b:a',
|
|
3297
|
+
'64k', // Low bitrate
|
|
3298
|
+
],
|
|
3299
|
+
|
|
3300
|
+
// Network options
|
|
3301
|
+
noCheckCertificates: true,
|
|
3302
|
+
noWarnings: true,
|
|
3303
|
+
addHeader: this.getHeaders(),
|
|
3304
|
+
retries: 2,
|
|
3305
|
+
socketTimeout: 30000,
|
|
3306
|
+
|
|
3307
|
+
// Ensure we get audio formats
|
|
3308
|
+
format: 'worstaudio/worst',
|
|
3309
|
+
|
|
3310
|
+
// Additional conversion flags
|
|
3311
|
+
embedMetadata: true,
|
|
3312
|
+
noOverwrites: true,
|
|
3313
|
+
};
|
|
3314
|
+
|
|
3315
|
+
// Try with proxy first
|
|
3316
|
+
const proxy = this.proxyList[this.currentProxyIndex];
|
|
3317
|
+
if (proxy) {
|
|
3318
|
+
options.proxy = proxy;
|
|
3319
|
+
console.log(`Using proxy: ${proxy.split('@')[1]}`);
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
console.log('Starting MP3 conversion with options:', {
|
|
3323
|
+
audioFormat: options.audioFormat,
|
|
3324
|
+
audioQuality: options.audioQuality,
|
|
3325
|
+
output: options.output,
|
|
3326
|
+
});
|
|
3327
|
+
|
|
3328
|
+
const result = await youtubedl(videoUrl, options);
|
|
3329
|
+
console.log('✅ MP3 download successful!');
|
|
3330
|
+
this.retryCount = 0;
|
|
3331
|
+
return result;
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
async downloadWithPuppeteer(videoUrl) {
|
|
3335
|
+
console.log('Using Puppeteer to bypass bot detection...');
|
|
3336
|
+
|
|
3337
|
+
const browser = await puppeteer.launch({
|
|
3338
|
+
headless: true,
|
|
3339
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
3340
|
+
});
|
|
3341
|
+
|
|
3342
|
+
try {
|
|
3343
|
+
const page = await browser.newPage();
|
|
3344
|
+
await page.setUserAgent(this.getHeaders()[0].split(':')[1]);
|
|
3345
|
+
|
|
3346
|
+
console.log('Navigating to YouTube...');
|
|
3347
|
+
await page.goto(videoUrl, {
|
|
3348
|
+
waitUntil: 'networkidle2',
|
|
3349
|
+
timeout: 60000,
|
|
3350
|
+
});
|
|
3351
|
+
|
|
3352
|
+
await page.waitForTimeout(5000);
|
|
3353
|
+
|
|
3354
|
+
// Check if we successfully bypassed bot detection
|
|
3355
|
+
const pageTitle = await page.title();
|
|
3356
|
+
if (pageTitle.includes('bot') || pageTitle.includes('Verify')) {
|
|
3357
|
+
throw new Error('Bot detection still active after Puppeteer');
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
const currentUrl = page.url();
|
|
3361
|
+
console.log('Successfully accessed video URL:', currentUrl);
|
|
3362
|
+
await browser.close();
|
|
3363
|
+
|
|
3364
|
+
// Now download with the verified URL using enhanced MP3 settings
|
|
3365
|
+
return await youtubedl(currentUrl, {
|
|
3366
|
+
extractAudio: true,
|
|
3367
|
+
audioFormat: 'mp3',
|
|
3368
|
+
audioQuality: 9,
|
|
3369
|
+
output, //: '/tmp/%(title)s.mp3',
|
|
3370
|
+
noCheckCertificates: true,
|
|
3371
|
+
noWarnings: true,
|
|
3372
|
+
postprocessorArgs: ['ffmpeg:-c:a', 'libmp3lame', '-q:a', '9', '-ac', '1', '-ar', '22050', '-b:a', '64k'],
|
|
3373
|
+
format: 'worstaudio/worst',
|
|
3374
|
+
});
|
|
3375
|
+
} catch (error) {
|
|
3376
|
+
await browser.close();
|
|
3377
|
+
throw error;
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
|
|
3381
|
+
// Alternative method for guaranteed MP3 conversion
|
|
3382
|
+
async downloadAsMP3(videoUrl) {
|
|
3383
|
+
const options = {
|
|
3384
|
+
// Method 1: Direct format selection for worst audio quality
|
|
3385
|
+
format: 'worstaudio[ext=m4a]/worstaudio/bestaudio[ext=m4a]/bestaudio',
|
|
3386
|
+
|
|
3387
|
+
// Method 2: Extract and convert
|
|
3388
|
+
extractAudio: true,
|
|
3389
|
+
audioFormat: 'mp3',
|
|
3390
|
+
audioQuality: 9,
|
|
3391
|
+
|
|
3392
|
+
// Force MP3 output
|
|
3393
|
+
output, //: '/tmp/%(title)s.mp3',
|
|
3394
|
+
|
|
3395
|
+
// Enhanced conversion settings
|
|
3396
|
+
postprocessorArgs: 'ffmpeg:-c:a libmp3lame -q:a 9 -ac 1 -ar 22050 -b:a 64k',
|
|
3397
|
+
|
|
3398
|
+
// Network
|
|
3399
|
+
noCheckCertificates: true,
|
|
3400
|
+
noWarnings: true,
|
|
3401
|
+
addHeader: this.getHeaders(),
|
|
3402
|
+
socketTimeout: 30000,
|
|
3403
|
+
};
|
|
3404
|
+
|
|
3405
|
+
const proxy = this.proxyList[this.currentProxyIndex];
|
|
3406
|
+
if (proxy) {
|
|
3407
|
+
options.proxy = proxy;
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
console.log('Using enhanced MP3 conversion method...');
|
|
3411
|
+
return await youtubedl(videoUrl, options);
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
// Method to check what's being downloaded (for debugging)
|
|
3415
|
+
async debugDownload(videoUrl) {
|
|
3416
|
+
console.log('=== DEBUG MODE ===');
|
|
3417
|
+
|
|
3418
|
+
try {
|
|
3419
|
+
// First, list available formats
|
|
3420
|
+
const formats = await youtubedl(videoUrl, {
|
|
3421
|
+
listFormats: true,
|
|
3422
|
+
proxy: this.proxyList[this.currentProxyIndex],
|
|
3423
|
+
noCheckCertificates: true,
|
|
3424
|
+
});
|
|
3425
|
+
console.log('Available formats:', formats);
|
|
3426
|
+
|
|
3427
|
+
// Then try download
|
|
3428
|
+
return await this.downloadAsMP3(videoUrl);
|
|
3429
|
+
} catch (error) {
|
|
3430
|
+
console.error('Debug failed:', error);
|
|
3431
|
+
throw error;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
rotateProxy() {
|
|
3436
|
+
this.currentProxyIndex = (this.currentProxyIndex + 1) % this.proxyList.length;
|
|
3437
|
+
console.log(`Rotated to proxy index: ${this.currentProxyIndex}`);
|
|
3438
|
+
}
|
|
3439
|
+
|
|
3440
|
+
getHeaders() {
|
|
3441
|
+
return ['user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'accept:*/*', 'accept-language:en-US,en;q=0.9', 'referer:https://www.youtube.com/'];
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3444
|
+
delay(ms) {
|
|
3445
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
// Usage examples:
|
|
3450
|
+
const downloader = new AdvancedYouTubeDownloader();
|
|
3451
|
+
|
|
3452
|
+
// Method 1: Regular download (recommended)
|
|
3453
|
+
downloader
|
|
3454
|
+
.download(videoUrl)
|
|
3455
|
+
.then((result) => {
|
|
3456
|
+
console.log('🎉 Download completed successfully!');
|
|
3457
|
+
console.log('Result:', result);
|
|
3458
|
+
let interval = setInterval(() => {
|
|
3459
|
+
if (fs.existsSync(`/tmp/${result}`)) {
|
|
3460
|
+
clearInterval(interval);
|
|
3461
|
+
resolve(result);
|
|
3462
|
+
}
|
|
3463
|
+
}, 1000);
|
|
3464
|
+
})
|
|
3465
|
+
.catch((error) => {
|
|
3466
|
+
console.error('💥 Final error:', error);
|
|
3467
|
+
reject(error.message);
|
|
3468
|
+
});
|
|
3469
|
+
});
|
|
3470
|
+
};
|
|
3471
|
+
setTimeout(async () => {
|
|
3472
|
+
const app_id = 'prj712ffdf5aa8adce6cedef988f9c12392'; //'prj3937cb6f9a31c8c7dea25055bba845b1'; //
|
|
3473
|
+
const uid = 'd39126e0e2c51ffbd1aad10709fc8335';
|
|
3474
|
+
|
|
3475
|
+
// await init_studio_units();
|
|
3476
|
+
let ret;
|
|
3477
|
+
// await build_studio_table_tools(app_id);
|
|
3478
|
+
// testTool(app_id, uid);
|
|
3479
|
+
// ret = await download_yt_audio('https://www.youtube.com/watch?v=uSt5yLOWiNo');
|
|
3480
|
+
// ret = await update_ai_agent({
|
|
3481
|
+
// uid: 'd39126e0e2c51ffbd1aad10709fc8335',
|
|
3482
|
+
// agent_id: '392_agn_ddbca7c25455',
|
|
3483
|
+
// agentConfig: {
|
|
3484
|
+
// agent_instructions: 'use app builder to build tables and programs',
|
|
3485
|
+
// agent_tools: [
|
|
3486
|
+
// {
|
|
3487
|
+
// type: 'app_builder',
|
|
3488
|
+
// name: 'app_builder',
|
|
3489
|
+
// },
|
|
3490
|
+
// ],
|
|
3491
|
+
// agent_name: 'Xuda App Builder',
|
|
3492
|
+
// },
|
|
3493
|
+
// });
|
|
3494
|
+
// ret = await client.vectorStores.files.create(_conf.OPENAI_VECTOR_STORE_ID, {
|
|
3495
|
+
// file_id: 'file-Rf9UzT1f7NVhTELypnbsBU',
|
|
3496
|
+
// });
|
|
3497
|
+
// ret = upload_file_to_openai_storage('/docxsite-bp.xlsx.pdf');
|
|
3498
|
+
// ret = await transcribe('/AAA.mp3');
|
|
3499
|
+
// ret = await transcribe('/B.mp4');
|
|
3500
|
+
// ret = await upload_file_to_openai_storage('/AAA.mp3');
|
|
3501
|
+
// ret = await create_image("'Generate an image of gray tabby cat hugging an otter with an orange scarf'");
|
|
3502
|
+
// ret = await submit_chat_conversation({ prompt: 'my name is Boaz' });
|
|
3503
|
+
// ret = await submit_chat_conversation({ prompt: 'what is my name', conversation_id: ret.data.conversation_doc._id });
|
|
3504
|
+
// const ret = await create_studio_app('crm');
|
|
3505
|
+
// ret = await submit_chat_gpt_prompt({
|
|
3506
|
+
// prompt: `Use the formatting rules from the Xuda fine-tuned model.
|
|
3507
|
+
// Generate a new Table Object JSON with the same schema learned in training.
|
|
3508
|
+
// Don't modify _rev. Preserve screenshots array structure.`,
|
|
3509
|
+
// model: 'ft:gpt-4o-2024-08-06:xuda-io:xuda-table:CdYWfx0l', //'ft:gpt-4.1-nano-2025-04-14:xuda-io:xuda-table:CdXWxgir'
|
|
3510
|
+
// });
|
|
3511
|
+
// const ret = await submit_agentic_studio_chat_gpt_prompt({ user_prompt: 'crm app', object_unit: 'app', uid, app_id });
|
|
3512
|
+
// ret = await submit_agentic_studio_chat_gpt_prompt({ user_prompt: 'products', object_unit: 'table', uid, app_id });
|
|
3513
|
+
// ret = await submit_agentic_studio_chat_gpt_prompt({ user_prompt: 'test3', object_unit: 'folder', uid, app_id });
|
|
3514
|
+
// ret = await submit_agentic_studio_chat_gpt_prompt({ user_prompt: 'contact contact form', object_unit: 'component', uid, app_id });
|
|
3515
|
+
// ret = await submit_agentic_studio_chat_gpt_prompt({ user_prompt: 'get data from tasks by task id', object_unit: 'get_data', uid, app_id });
|
|
3516
|
+
|
|
3517
|
+
ret = await test_plugin(app_id, uid);
|
|
3518
|
+
console.log(ret);
|
|
3519
|
+
|
|
3520
|
+
// init({}, {}, _, true, null, null, UglifyJS, JSON5, _conf, false, z);
|
|
3521
|
+
// const schema = get_zod_schema('component');
|
|
3522
|
+
|
|
3523
|
+
// const db = get_xuda_couch('xuda_master');
|
|
3524
|
+
// const app_obj = await db.get(app_id);
|
|
3525
|
+
// const project_db = get_xuda_couch(app_obj.app_db_name);
|
|
3526
|
+
// const doc = await project_db.get('5b1_cmp_313d53e56c2d');
|
|
3527
|
+
|
|
3528
|
+
////////////////////
|
|
3529
|
+
// let err_ret = [];
|
|
3530
|
+
// const addValidationError = (code, data) => {
|
|
3531
|
+
// err_ret.push({
|
|
3532
|
+
// code,
|
|
3533
|
+
// data,
|
|
3534
|
+
// type: 'E',
|
|
3535
|
+
// category: 'document',
|
|
3536
|
+
// structure_error: true,
|
|
3537
|
+
// });
|
|
3538
|
+
// };
|
|
3539
|
+
//////////////////
|
|
3540
|
+
|
|
3541
|
+
// const result = schema.safeParse({});
|
|
3542
|
+
// if (!result.success) {
|
|
3543
|
+
// console.log(result.error.errors); // Array of error objects
|
|
3544
|
+
// result.error.errors.forEach((error) => {
|
|
3545
|
+
// // console.log(`Path: ${error.path}, Message: ${error.message}`);
|
|
3546
|
+
// const msg = `${error.message}: ${error.path}(${error.expected})`;
|
|
3547
|
+
// addValidationError('CHK_MSG_OBJ_GEN_000', msg);
|
|
3548
|
+
// });
|
|
3549
|
+
// console.log(err_ret);
|
|
3550
|
+
// } else {
|
|
3551
|
+
// console.log('Validation succeeded:', result.data);
|
|
3552
|
+
// }
|
|
3553
|
+
|
|
3554
|
+
// const jsonSchema = zodToJsonSchema(schema);
|
|
3555
|
+
// // console.log(jsonSchema);
|
|
3556
|
+
// console.log(JSON.stringify(jsonSchema, null, 2));
|
|
3557
|
+
|
|
3558
|
+
// console.log(
|
|
3559
|
+
// check(
|
|
3560
|
+
// createDoc({
|
|
3561
|
+
// uid: '11',
|
|
3562
|
+
// app_id: 'prj-444',
|
|
3563
|
+
// menuType: 'folder',
|
|
3564
|
+
// checkedInUserName: 'boaz',
|
|
3565
|
+
// parentId: '',
|
|
3566
|
+
// }),
|
|
3567
|
+
// ),
|
|
3568
|
+
// );
|
|
3569
|
+
|
|
3570
|
+
// const vectorStore = await client.beta.vector_stores.create({
|
|
3571
|
+
// name: 'Xuda Docs Vector Store',
|
|
3572
|
+
// });
|
|
3573
|
+
// console.log('Vector Store ID:', vectorStore.id);
|
|
3574
|
+
// console.log('OpenAI version:', (await import('openai/package.json')).version);
|
|
3575
|
+
// console.log('has beta?', !!client.beta);
|
|
3576
|
+
// console.log('has vector_stores?', !!client.beta?.vector_stores);
|
|
3577
|
+
// console.log('keys on vector_stores:', Object.keys(client.beta?.vector_stores || {}));
|
|
3578
|
+
|
|
3579
|
+
// const vectorStore = await client.vectorStores.create({
|
|
3580
|
+
// name: 'Xuda Docs Vector Store',
|
|
3581
|
+
// });
|
|
3582
|
+
// console.log(vectorStore);
|
|
3583
|
+
}, 1000);
|
|
3584
|
+
|
|
3585
|
+
// PROMPT OBJECT UNIT OUTPUT
|
|
3586
|
+
//================================================================== ==================== =====================
|
|
3587
|
+
// create customer table
|
|
3588
|
+
// fill 5 rows to customer table
|
|
3589
|
+
// describe the customer table
|
|
3590
|
+
// create router for the app
|
|
3591
|
+
// create form program and save the data in the customer table
|
|
3592
|
+
// generate 10 buttons using tailwind
|
|
3593
|
+
// describe the program
|
|
3594
|
+
// create crm app
|
|
3595
|
+
// change text color
|
|
3596
|
+
// fetch only new customers using mongo query
|
|
3597
|
+
// create dashboard program for crm app
|
|
3598
|
+
// find diffs between two objects
|
|
3599
|
+
|
|
3600
|
+
///////////////////////
|
|
3601
|
+
|
|
3602
|
+
async function testTool(app_id, uid) {
|
|
3603
|
+
console.log('Testing tool with:', { app_id, uid, table_id: '392_tbl_47f0ac6059ca' });
|
|
3604
|
+
|
|
3605
|
+
try {
|
|
3606
|
+
const input = { table_id: '392_tbl_47f0ac6059ca' };
|
|
3607
|
+
const runContext = { context: { app_id, uid } };
|
|
3608
|
+
const details = {};
|
|
3609
|
+
|
|
3610
|
+
console.log('Input:', JSON.stringify(input, null, 2));
|
|
3611
|
+
console.log('RunContext:', JSON.stringify(runContext, null, 2));
|
|
3612
|
+
debugger;
|
|
3613
|
+
// CORRECT ORDER: input first, then runContext
|
|
3614
|
+
const result = await get_studio_table_info_by_table_id_tool.invoke(
|
|
3615
|
+
runContext, // 1st: context
|
|
3616
|
+
input, // 2nd: parameters
|
|
3617
|
+
details, // 3rd: optional
|
|
3618
|
+
);
|
|
3619
|
+
|
|
3620
|
+
console.log('Tool test successful:', result);
|
|
3621
|
+
return result;
|
|
3622
|
+
} catch (error) {
|
|
3623
|
+
console.error('Tool test failed:');
|
|
3624
|
+
console.error('Error:', error.message);
|
|
3625
|
+
throw error;
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
async function test_plugin(app_id, uid, plugin_name = '@xuda.io/xuda-library-plugin-studio-ai-app-builder', method = 'table_builder', dev = true, req = {}) {
|
|
3629
|
+
const get_path = function (resource) {
|
|
3630
|
+
if (dev) {
|
|
3631
|
+
return `../../plugins/${plugin_name}/${resource}`;
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
return `${_conf.plugins_drive_path}/${app_id}/node_modules/${plugin_name}/${resource}`;
|
|
3635
|
+
};
|
|
3636
|
+
const get_plugin_resource = function (plugin_name, plugin_resource) {
|
|
3637
|
+
return new Promise(async (resolve, reject) => {
|
|
3638
|
+
try {
|
|
3639
|
+
const plugin_resource_res = await import(`${get_path(plugin_resource)}`);
|
|
3640
|
+
resolve(plugin_resource_res);
|
|
3641
|
+
} catch (err) {
|
|
3642
|
+
console.error(err);
|
|
3643
|
+
reject();
|
|
3644
|
+
}
|
|
3645
|
+
});
|
|
3646
|
+
};
|
|
3647
|
+
|
|
3648
|
+
const get_fields_data = async function (fields, props) {
|
|
3649
|
+
const get_property_value = async function (fieldIdP, val) {
|
|
3650
|
+
var value = props[fieldIdP] || (typeof val.defaultValue === 'function' ? val?.defaultValue?.() : val?.defaultValue);
|
|
3651
|
+
|
|
3652
|
+
return cast(
|
|
3653
|
+
val.type,
|
|
3654
|
+
value,
|
|
3655
|
+
(err) => {
|
|
3656
|
+
throw new Error(err);
|
|
3657
|
+
},
|
|
3658
|
+
(err) => {},
|
|
3659
|
+
);
|
|
3660
|
+
};
|
|
3661
|
+
var data_obj = {};
|
|
3662
|
+
var return_code = 1;
|
|
3663
|
+
|
|
3664
|
+
for await (const [key, val] of Object.entries(fields)) {
|
|
3665
|
+
data_obj[key] = await get_property_value(key, val);
|
|
3666
|
+
if (!data_obj[key] && val.mandatory) {
|
|
3667
|
+
return_code = -1;
|
|
3668
|
+
throw new Error(`${key} is a mandatory field.`);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
return data_obj;
|
|
3672
|
+
};
|
|
3673
|
+
|
|
3674
|
+
console.log('Testing plugin with:', { app_id, uid });
|
|
3675
|
+
|
|
3676
|
+
try {
|
|
3677
|
+
const app_obj = await get_app_obj(app_id);
|
|
3678
|
+
|
|
3679
|
+
const project_db = await get_project_db(app_id);
|
|
3680
|
+
let plugin_doc;
|
|
3681
|
+
try {
|
|
3682
|
+
plugin_doc = await project_db.get(plugin_name);
|
|
3683
|
+
} catch (error) {
|
|
3684
|
+
throw new Error(`plugin_doc ${plugin_name} not found`);
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
const _plugin = app_obj.app_plugins_purchased[plugin_name];
|
|
3688
|
+
|
|
3689
|
+
if (!_plugin?.installed) {
|
|
3690
|
+
throw new Error(`plugin ${plugin_name} not installed`);
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
const index = await get_plugin_resource(plugin_name, 'index.mjs');
|
|
3694
|
+
const server = await get_plugin_resource(plugin_name, 'server.mjs');
|
|
3695
|
+
|
|
3696
|
+
const methods = index.methods;
|
|
3697
|
+
if (methods && !methods[method]) {
|
|
3698
|
+
throw new Error(`method ${method} not found`);
|
|
3699
|
+
}
|
|
3700
|
+
const prop_data = { prompt: 'build user table' };
|
|
3701
|
+
const fields = await get_fields_data(methods[method].fields, prop_data);
|
|
3702
|
+
const params = { ...fields, ...{ app_id, uid } };
|
|
3703
|
+
const setup_doc = plugin_doc.setup || { OPENAI_API_KEY: _conf.OPENAI_API_KEY };
|
|
3704
|
+
|
|
3705
|
+
///////////////////////
|
|
3706
|
+
const execute_server_script = async function (req, app_id_reference, server_script, method, params, setup_doc) {
|
|
3707
|
+
return new Promise(async (resolve, reject) => {
|
|
3708
|
+
const app_plugins_path = path.join(_conf.plugins_drive_path, app_id_reference, 'node_modules');
|
|
3709
|
+
|
|
3710
|
+
process.on('uncaughtException', (err) => {
|
|
3711
|
+
console.error('Asynchronous error caught.', err);
|
|
3712
|
+
});
|
|
3713
|
+
const resolve_local = function (e) {
|
|
3714
|
+
resolve({ code: 1, data: e });
|
|
3715
|
+
};
|
|
3716
|
+
|
|
3717
|
+
const reject_local = function (e) {
|
|
3718
|
+
reject({ code: -1, data: e });
|
|
3719
|
+
// reject(e.message);
|
|
3720
|
+
};
|
|
3721
|
+
|
|
3722
|
+
try {
|
|
3723
|
+
const script = new VMScript(`try{server_script.${method}(req,params,setup_doc,resolve,reject,process);}catch(e){ console.error(e); }`, { filename: app_plugins_path, dirname: app_plugins_path });
|
|
3724
|
+
let vm = new NodeVM({
|
|
3725
|
+
require: {
|
|
3726
|
+
external: true,
|
|
3727
|
+
},
|
|
3728
|
+
sandbox: { process, params, setup_doc, resolve: resolve_local, reject: reject_local, req, server_script },
|
|
3729
|
+
timeout: 60000,
|
|
3730
|
+
});
|
|
3731
|
+
return await vm.run(script, {
|
|
3732
|
+
filename: app_plugins_path,
|
|
3733
|
+
dirname: app_plugins_path,
|
|
3734
|
+
});
|
|
3735
|
+
} catch (err) {
|
|
3736
|
+
console.error('Failed to execute script.', err);
|
|
3737
|
+
}
|
|
3738
|
+
// };
|
|
3739
|
+
});
|
|
3740
|
+
};
|
|
3741
|
+
|
|
3742
|
+
let ret_exec = await execute_server_script(req, app_id, server, method, params, setup_doc);
|
|
3743
|
+
///////////////
|
|
3744
|
+
|
|
3745
|
+
return ret_exec;
|
|
3746
|
+
} catch (error) {
|
|
3747
|
+
console.error('Testing plugin failed:');
|
|
3748
|
+
console.error('Error:', error);
|
|
3749
|
+
throw error;
|
|
3750
|
+
}
|
|
3751
|
+
}
|