@xuda.io/ai_module 1.1.5595 → 1.1.5597
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/index.mjs +482 -52
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -1490,6 +1490,9 @@ export const get_chat_conversation = async function (req) {
|
|
|
1490
1490
|
|
|
1491
1491
|
for (let conversation_item_doc of conversation_items.docs || []) {
|
|
1492
1492
|
conversation_item_doc.metadata = conversation_item_doc.metadata || {};
|
|
1493
|
+
if (conversation_item_doc.plan_mode && typeof conversation_item_doc.plan_accepted === 'undefined') {
|
|
1494
|
+
conversation_item_doc.plan_accepted = false;
|
|
1495
|
+
}
|
|
1493
1496
|
if (!conversation_item_doc.read) {
|
|
1494
1497
|
conversation_item_doc.read = {};
|
|
1495
1498
|
}
|
|
@@ -4299,15 +4302,30 @@ const contactGuardrailAgent = new Agent({
|
|
|
4299
4302
|
// };
|
|
4300
4303
|
|
|
4301
4304
|
export const submit_chat_conversation = async function (req, job_id, headers) {
|
|
4302
|
-
const { profile_id, uid, metadata = {} } = req;
|
|
4305
|
+
const { profile_id, uid, metadata = {}, accepted_plan_item_id } = req;
|
|
4303
4306
|
let { conversation_id } = req;
|
|
4304
4307
|
try {
|
|
4305
4308
|
if (!conversation_id) throw new Error('missing conversation_id');
|
|
4306
4309
|
const account_profile_info = await get_active_account_profile_info(uid, profile_id);
|
|
4307
4310
|
|
|
4308
4311
|
let conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_id);
|
|
4309
|
-
|
|
4310
|
-
|
|
4312
|
+
let accepted_plan_item;
|
|
4313
|
+
if (accepted_plan_item_id) {
|
|
4314
|
+
accepted_plan_item = await db_module.get_app_couch_doc_native(account_profile_info.app_id, accepted_plan_item_id);
|
|
4315
|
+
if (accepted_plan_item.docType !== 'chat_conversation_item') throw new Error('accepted plan item must be a chat_conversation_item');
|
|
4316
|
+
if (accepted_plan_item.conversation_id !== conversation_id) throw new Error('accepted plan item does not belong to this conversation');
|
|
4317
|
+
if (!normalize_boolean(accepted_plan_item.plan_mode)) throw new Error('accepted plan item is not a plan-mode item');
|
|
4318
|
+
|
|
4319
|
+
if (!accepted_plan_item.plan_accepted) {
|
|
4320
|
+
accepted_plan_item.plan_accepted = true;
|
|
4321
|
+
accepted_plan_item.plan_accepted_at = Date.now();
|
|
4322
|
+
accepted_plan_item.plan_accepted_by_uid = uid;
|
|
4323
|
+
await db_module.save_app_couch_doc_native(account_profile_info.app_id, accepted_plan_item);
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4327
|
+
const normalized_plan_mode = accepted_plan_item_id ? false : normalize_boolean(req.plan_mode ?? conversation_doc.plan_mode);
|
|
4328
|
+
const route_to_studio = conversation_doc.conversation_type === 'studio' || req.conversation_type === 'studio' || normalized_plan_mode || Boolean(accepted_plan_item_id);
|
|
4311
4329
|
|
|
4312
4330
|
if (route_to_studio && conversation_doc.conversation_type !== 'studio') {
|
|
4313
4331
|
conversation_doc.conversation_type = 'studio';
|
|
@@ -4317,71 +4335,76 @@ export const submit_chat_conversation = async function (req, job_id, headers) {
|
|
|
4317
4335
|
|
|
4318
4336
|
req.plan_mode = normalized_plan_mode;
|
|
4319
4337
|
req.conversation_doc = conversation_doc;
|
|
4338
|
+
req.accepted_plan_item = accepted_plan_item;
|
|
4320
4339
|
req.metadata = metadata || {};
|
|
4321
4340
|
let ret;
|
|
4322
4341
|
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4342
|
+
if (route_to_studio) {
|
|
4343
|
+
ret = await chat_studio(req, job_id, headers);
|
|
4344
|
+
} else {
|
|
4345
|
+
switch (conversation_doc.reference_type) {
|
|
4346
|
+
case 'contacts': {
|
|
4347
|
+
req._thread_reentry = Date.now() - conversation_doc.date_created_ts > 60000;
|
|
4326
4348
|
|
|
4327
|
-
|
|
4349
|
+
account_msa.ts_contact(uid, conversation_doc.reference_id);
|
|
4328
4350
|
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4351
|
+
switch (conversation_doc.conversation_type) {
|
|
4352
|
+
case 'note': {
|
|
4353
|
+
ret = await chat_note(req, job_id, headers);
|
|
4354
|
+
break;
|
|
4355
|
+
}
|
|
4356
|
+
case 'ai_chat': {
|
|
4357
|
+
ret = await ai_chat_conversation(req, job_id, headers);
|
|
4358
|
+
break;
|
|
4359
|
+
}
|
|
4338
4360
|
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4361
|
+
case 'email': {
|
|
4362
|
+
ret = await chat_email(req, job_id, headers);
|
|
4363
|
+
break;
|
|
4364
|
+
}
|
|
4343
4365
|
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4366
|
+
case 'chat': {
|
|
4367
|
+
ret = await contact_chat_conversation(req, job_id, headers);
|
|
4368
|
+
break;
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
default:
|
|
4372
|
+
ret = { code: -44, data: 'invalid conversation_type ' + conversation_doc.conversation_type };
|
|
4373
|
+
break;
|
|
4347
4374
|
}
|
|
4348
4375
|
|
|
4349
|
-
|
|
4350
|
-
ret = { code: -44, data: 'invalid conversation_type ' + conversation_doc.conversation_type };
|
|
4351
|
-
break;
|
|
4376
|
+
break;
|
|
4352
4377
|
}
|
|
4353
4378
|
|
|
4354
|
-
|
|
4355
|
-
|
|
4379
|
+
case 'ai_chats': {
|
|
4380
|
+
ret = await ai_chat_conversation(req, job_id, headers);
|
|
4381
|
+
break;
|
|
4382
|
+
}
|
|
4356
4383
|
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4384
|
+
case 'apps': {
|
|
4385
|
+
ret = await chat_studio(req, job_id, headers);
|
|
4386
|
+
break;
|
|
4387
|
+
}
|
|
4361
4388
|
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4389
|
+
case 'ai_agent': {
|
|
4390
|
+
ret = await ai_chat_conversation(req, job_id, headers);
|
|
4391
|
+
break;
|
|
4392
|
+
}
|
|
4366
4393
|
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4394
|
+
case 'studio': {
|
|
4395
|
+
ret = await chat_studio(req, job_id, headers);
|
|
4396
|
+
break;
|
|
4397
|
+
}
|
|
4371
4398
|
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4399
|
+
case 'dashboard': {
|
|
4400
|
+
ret = await dashboard_chat(req, job_id, headers);
|
|
4401
|
+
break;
|
|
4402
|
+
}
|
|
4376
4403
|
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4404
|
+
default:
|
|
4405
|
+
ret = await ai_chat_conversation(req, job_id, headers);
|
|
4406
|
+
break;
|
|
4380
4407
|
}
|
|
4381
|
-
|
|
4382
|
-
default:
|
|
4383
|
-
ret = await ai_chat_conversation(req, job_id, headers);
|
|
4384
|
-
break;
|
|
4385
4408
|
}
|
|
4386
4409
|
|
|
4387
4410
|
// if (route_to_studio) {
|
|
@@ -4704,7 +4727,7 @@ const chat_email = async function (req, job_id, headers) {
|
|
|
4704
4727
|
const chat_studio = async function (req, job_id, headers) {
|
|
4705
4728
|
const { uid, profile_id } = req;
|
|
4706
4729
|
const account_profile_info = await get_active_account_profile_info(uid, profile_id);
|
|
4707
|
-
let { prompt, conversation_doc, attachments = [], plan_mode = false, stream = true } = req;
|
|
4730
|
+
let { prompt, conversation_doc, attachments = [], plan_mode = false, stream = true, accepted_plan_item_id, accepted_plan_item } = req;
|
|
4708
4731
|
|
|
4709
4732
|
const conversation_id = conversation_doc._id;
|
|
4710
4733
|
const normalized_plan_mode = normalize_boolean(plan_mode);
|
|
@@ -4800,6 +4823,8 @@ const chat_studio = async function (req, job_id, headers) {
|
|
|
4800
4823
|
|
|
4801
4824
|
const items = (ret.docs || []).reverse().filter((item) => item.text || item.prompt || item?.file?.filename);
|
|
4802
4825
|
const conversation_history = items.map(formatConversationHistoryItem).join('\n\n');
|
|
4826
|
+
const accepted_plan_text = accepted_plan_item_id ? (accepted_plan_item?.studio_result || accepted_plan_item?.text || accepted_plan_item?.prompt || '').toString().trim() : '';
|
|
4827
|
+
const accepted_plan_context = accepted_plan_text ? `\nThe user has accepted the following plan. Execute it end-to-end:\n\n${accepted_plan_text}\n` : '';
|
|
4803
4828
|
|
|
4804
4829
|
return `You are working inside Xuda Studio.
|
|
4805
4830
|
|
|
@@ -4807,6 +4832,7 @@ Use the conversation history below as the main source of context for the current
|
|
|
4807
4832
|
The last user message is the latest request that should be handled now.
|
|
4808
4833
|
When plan_mode is true, create a plan only and do not save studio docs.
|
|
4809
4834
|
When plan_mode is false, generate the studio docs and save them into the project.
|
|
4835
|
+
${accepted_plan_context}
|
|
4810
4836
|
|
|
4811
4837
|
Conversation history:
|
|
4812
4838
|
${conversation_history || `User (studio): ${prompt}`}
|
|
@@ -4914,6 +4940,7 @@ ${conversation_history || `User (studio): ${prompt}`}
|
|
|
4914
4940
|
rtl: _common.detectRTL(prompt),
|
|
4915
4941
|
job_id,
|
|
4916
4942
|
plan_mode: normalized_plan_mode,
|
|
4943
|
+
plan_accepted: false,
|
|
4917
4944
|
studio_method: plugin_method,
|
|
4918
4945
|
target_app_id,
|
|
4919
4946
|
};
|
|
@@ -5031,6 +5058,7 @@ ${conversation_history || `User (studio): ${prompt}`}
|
|
|
5031
5058
|
rtl: _common.detectRTL(response_text),
|
|
5032
5059
|
job_id,
|
|
5033
5060
|
plan_mode: normalized_plan_mode,
|
|
5061
|
+
plan_accepted: false,
|
|
5034
5062
|
studio_method: plugin_method,
|
|
5035
5063
|
studio_result,
|
|
5036
5064
|
target_app_id,
|
|
@@ -5058,6 +5086,408 @@ ${conversation_history || `User (studio): ${prompt}`}
|
|
|
5058
5086
|
}
|
|
5059
5087
|
};
|
|
5060
5088
|
|
|
5089
|
+
const get_dashboard_chat_context = function (req, conversation_doc) {
|
|
5090
|
+
return (req.context || req.dashboard_context || req.metadata?.context || conversation_doc.context || conversation_doc.dashboard_context || conversation_doc.metadata?.context || '').toString().trim().toLowerCase();
|
|
5091
|
+
};
|
|
5092
|
+
|
|
5093
|
+
const get_dashboard_cpi_keywords = function (context, prompt = '') {
|
|
5094
|
+
const haystack = `${context || ''} ${prompt || ''}`.toLowerCase();
|
|
5095
|
+
const keyword_sets = {
|
|
5096
|
+
data: ['data', 'database', 'db', 'dbs', 'table', 'tables', 'couch', 'read', 'create', 'update', 'delete', 'restore', 'count'],
|
|
5097
|
+
account: ['account', 'profile', 'profiles', 'user', 'users', 'contact', 'contacts', 'member', 'members', 'share', 'team'],
|
|
5098
|
+
apps: ['app', 'apps', 'project', 'projects', 'config', 'backup', 'release', 'notification', 'studio', 'mini'],
|
|
5099
|
+
deploy: ['deploy', 'deployment', 'instance', 'server', 'region', 'domain', 'dns', 'traffic', 'firewall', 'usage', 'build'],
|
|
5100
|
+
drive: ['drive', 'file', 'files', 'folder', 'upload', 'extract', 'rename', 'workspace'],
|
|
5101
|
+
plugins: ['plugin', 'plugins', 'install', 'uninstall', 'category', 'categories'],
|
|
5102
|
+
email: ['email', 'mailbox', 'mailboxes', 'mail'],
|
|
5103
|
+
logs: ['log', 'logs', 'security', 'session', 'traffic'],
|
|
5104
|
+
ai: ['ai', 'chat', 'agent', 'workspace', 'conversation'],
|
|
5105
|
+
};
|
|
5106
|
+
|
|
5107
|
+
for (const [key, keywords] of Object.entries(keyword_sets)) {
|
|
5108
|
+
if (haystack.includes(key) || keywords.some((keyword) => haystack.includes(keyword))) {
|
|
5109
|
+
return keywords;
|
|
5110
|
+
}
|
|
5111
|
+
}
|
|
5112
|
+
|
|
5113
|
+
return [];
|
|
5114
|
+
};
|
|
5115
|
+
|
|
5116
|
+
const get_dashboard_cpi_zod_item = function (key, val = {}) {
|
|
5117
|
+
let schema;
|
|
5118
|
+
|
|
5119
|
+
if (val.enum?.length) {
|
|
5120
|
+
schema = z.enum(val.enum);
|
|
5121
|
+
} else {
|
|
5122
|
+
switch (val.type) {
|
|
5123
|
+
case 'string':
|
|
5124
|
+
schema = z.string();
|
|
5125
|
+
break;
|
|
5126
|
+
case 'number':
|
|
5127
|
+
case 'integer':
|
|
5128
|
+
schema = z.number();
|
|
5129
|
+
if (val.type === 'integer') schema = schema.int();
|
|
5130
|
+
break;
|
|
5131
|
+
case 'boolean':
|
|
5132
|
+
schema = z.boolean();
|
|
5133
|
+
break;
|
|
5134
|
+
case 'array':
|
|
5135
|
+
schema = z.array(get_dashboard_cpi_zod_item('', val.items || { type: 'any' }));
|
|
5136
|
+
break;
|
|
5137
|
+
case 'object': {
|
|
5138
|
+
const props = {};
|
|
5139
|
+
const fields = val.items?.properties || val.properties || {};
|
|
5140
|
+
const required = val.items?.required || val.required || [];
|
|
5141
|
+
for (const [item_key, item_val] of Object.entries(fields)) {
|
|
5142
|
+
props[item_key] = get_dashboard_cpi_zod_item(item_key, item_val);
|
|
5143
|
+
if (!required.includes(item_key)) {
|
|
5144
|
+
props[item_key] = props[item_key].optional();
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
schema = z.object(props);
|
|
5148
|
+
break;
|
|
5149
|
+
}
|
|
5150
|
+
default:
|
|
5151
|
+
schema = z.any();
|
|
5152
|
+
break;
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
|
|
5156
|
+
if (!val.mandatory) {
|
|
5157
|
+
schema = schema.optional();
|
|
5158
|
+
}
|
|
5159
|
+
|
|
5160
|
+
const valid_values = val.options?.length ? ` Valid values: ${val.options.join(', ')}.` : '';
|
|
5161
|
+
const desc = val.description ? `${val.name || key.replace(/_/g, ' ')} - ${val.description}${valid_values}` : valid_values;
|
|
5162
|
+
return desc ? schema.describe(desc) : schema;
|
|
5163
|
+
};
|
|
5164
|
+
|
|
5165
|
+
const get_dashboard_cpi_tools = function ({ uid, profile_id, gtp_token, app_id, context, prompt }) {
|
|
5166
|
+
const keywords = get_dashboard_cpi_keywords(context, prompt);
|
|
5167
|
+
const tools = [];
|
|
5168
|
+
const selected_methods = [];
|
|
5169
|
+
const method_entries = [];
|
|
5170
|
+
|
|
5171
|
+
for (const [method_name, method_prop] of Object.entries(_conf.cpi_methods || {})) {
|
|
5172
|
+
if (!method_prop) continue;
|
|
5173
|
+
if (method_prop.private) continue;
|
|
5174
|
+
if (!method_prop.description && !method_prop.tooltip) continue;
|
|
5175
|
+
|
|
5176
|
+
const method_text = `${method_name} ${method_prop.name || ''} ${method_prop.description || ''} ${method_prop.tooltip || ''} ${method_prop.microservice || ''}`.toLowerCase();
|
|
5177
|
+
if (keywords.length && !keywords.some((keyword) => method_text.includes(keyword))) continue;
|
|
5178
|
+
|
|
5179
|
+
method_entries.push({ method_name, method_prop, score: method_prop.mcp ? 2 : 1 });
|
|
5180
|
+
}
|
|
5181
|
+
|
|
5182
|
+
method_entries.sort((a, b) => b.score - a.score || a.method_name.localeCompare(b.method_name));
|
|
5183
|
+
|
|
5184
|
+
for (const { method_name, method_prop } of method_entries.slice(0, 96)) {
|
|
5185
|
+
const inputSchema = {};
|
|
5186
|
+
for (const [field_key, field_val] of Object.entries(method_prop.fields || {})) {
|
|
5187
|
+
inputSchema[field_key] = get_dashboard_cpi_zod_item(field_key, field_val);
|
|
5188
|
+
}
|
|
5189
|
+
|
|
5190
|
+
selected_methods.push(method_name);
|
|
5191
|
+
tools.push(
|
|
5192
|
+
tool({
|
|
5193
|
+
name: method_name.substring(0, 60),
|
|
5194
|
+
description: method_prop.description || method_prop.tooltip || method_name,
|
|
5195
|
+
parameters: z.object(inputSchema),
|
|
5196
|
+
async execute(params) {
|
|
5197
|
+
try {
|
|
5198
|
+
const response = await fetch(`http://localhost:3007/cpi/${method_name}`, {
|
|
5199
|
+
method: 'POST',
|
|
5200
|
+
headers: {
|
|
5201
|
+
Accept: 'application/json',
|
|
5202
|
+
'Content-Type': 'application/json',
|
|
5203
|
+
},
|
|
5204
|
+
body: JSON.stringify({
|
|
5205
|
+
...params,
|
|
5206
|
+
uid,
|
|
5207
|
+
profile_id,
|
|
5208
|
+
app_id,
|
|
5209
|
+
reference_id: app_id,
|
|
5210
|
+
gtp_token,
|
|
5211
|
+
}),
|
|
5212
|
+
});
|
|
5213
|
+
const ret = await response.json();
|
|
5214
|
+
return JSON.stringify(ret || '', null, 2);
|
|
5215
|
+
} catch (err) {
|
|
5216
|
+
return `CPI method ${method_name} failed: ${err.message}`;
|
|
5217
|
+
}
|
|
5218
|
+
},
|
|
5219
|
+
}),
|
|
5220
|
+
);
|
|
5221
|
+
}
|
|
5222
|
+
|
|
5223
|
+
return { tools, selected_methods };
|
|
5224
|
+
};
|
|
5225
|
+
|
|
5226
|
+
const dashboard_chat = async function (req, job_id, headers) {
|
|
5227
|
+
const { uid, profile_id, gtp_token } = req;
|
|
5228
|
+
const account_profile_info = await get_active_account_profile_info(uid, profile_id);
|
|
5229
|
+
let { prompt, conversation_doc, attachments = [], stream = true, ai_model = _conf.default_ai_model, metadata = {} } = req;
|
|
5230
|
+
|
|
5231
|
+
const conversation_id = conversation_doc._id;
|
|
5232
|
+
const dashboard_context = get_dashboard_chat_context(req, conversation_doc);
|
|
5233
|
+
const target_app_id = (await _common.get_project_app_id(req.app_id || conversation_doc.reference_id || account_profile_info.app_id, true)) || account_profile_info.app_id;
|
|
5234
|
+
const prompt_conversation_item_id = await _common.xuda_get_uuid('chat_conversation_item');
|
|
5235
|
+
const response_conversation_item_id = await _common.xuda_get_uuid('chat_conversation_item');
|
|
5236
|
+
const request_started_at = Date.now();
|
|
5237
|
+
|
|
5238
|
+
let stream_delta_seq = 0;
|
|
5239
|
+
let stream_delta_text = '';
|
|
5240
|
+
const emitToDashboard = (type, content, params) => {
|
|
5241
|
+
const is_stream_delta = type === 'stream_delta';
|
|
5242
|
+
const is_stream_end = type === 'stream_end';
|
|
5243
|
+
const seq = is_stream_delta ? ++stream_delta_seq : undefined;
|
|
5244
|
+
if (is_stream_delta) {
|
|
5245
|
+
stream_delta_text += content || '';
|
|
5246
|
+
}
|
|
5247
|
+
ws_dashboard_msa.emit_message_to_dashboard({
|
|
5248
|
+
service: type,
|
|
5249
|
+
to: uid,
|
|
5250
|
+
data: {
|
|
5251
|
+
conversation_id,
|
|
5252
|
+
job_id,
|
|
5253
|
+
delta: content,
|
|
5254
|
+
...(type === 'stream_start' ? { request_started_at } : {}),
|
|
5255
|
+
...(seq ? { seq } : {}),
|
|
5256
|
+
...(is_stream_end
|
|
5257
|
+
? {
|
|
5258
|
+
stream_id: response_conversation_item_id,
|
|
5259
|
+
final_seq: stream_delta_seq,
|
|
5260
|
+
text: stream_delta_text,
|
|
5261
|
+
}
|
|
5262
|
+
: {}),
|
|
5263
|
+
prompt_conversation_item_id,
|
|
5264
|
+
response_conversation_item_id,
|
|
5265
|
+
params,
|
|
5266
|
+
},
|
|
5267
|
+
});
|
|
5268
|
+
};
|
|
5269
|
+
|
|
5270
|
+
const streamText = function (text, chunk_size = 280) {
|
|
5271
|
+
if (!stream || !text) return;
|
|
5272
|
+
for (let i = 0; i < text.length; i += chunk_size) {
|
|
5273
|
+
emitToDashboard('stream_delta', text.slice(i, i + chunk_size));
|
|
5274
|
+
}
|
|
5275
|
+
};
|
|
5276
|
+
|
|
5277
|
+
const saveUserItem = async function () {
|
|
5278
|
+
const conversation_item_reference_id = await add_conversation_item(uid, profile_id, conversation_id, prompt, 'dashboard', conversation_doc.reference_id, { context: dashboard_context, target_app_id });
|
|
5279
|
+
const out_conversation_item_obj = {
|
|
5280
|
+
_id: prompt_conversation_item_id,
|
|
5281
|
+
stat: 3,
|
|
5282
|
+
docType: 'chat_conversation_item',
|
|
5283
|
+
uid,
|
|
5284
|
+
conversation_type: 'dashboard',
|
|
5285
|
+
type: 'dashboard',
|
|
5286
|
+
date_created_ts: Date.now(),
|
|
5287
|
+
ts: Date.now(),
|
|
5288
|
+
conversation_id,
|
|
5289
|
+
text: prompt,
|
|
5290
|
+
reference_id: conversation_doc.reference_id,
|
|
5291
|
+
conversation_item_reference_id,
|
|
5292
|
+
direction: 'out',
|
|
5293
|
+
role: 'user',
|
|
5294
|
+
read: { [uid]: Date.now() },
|
|
5295
|
+
rtl: _common.detectRTL(prompt),
|
|
5296
|
+
job_id,
|
|
5297
|
+
context: dashboard_context,
|
|
5298
|
+
target_app_id,
|
|
5299
|
+
metadata,
|
|
5300
|
+
};
|
|
5301
|
+
|
|
5302
|
+
const save_ret = await db_module.save_app_couch_doc(account_profile_info.app_id, out_conversation_item_obj);
|
|
5303
|
+
if (save_ret.code < 0) throw new Error(save_ret.data);
|
|
5304
|
+
return save_ret;
|
|
5305
|
+
};
|
|
5306
|
+
|
|
5307
|
+
const saveAssistantItem = async function (text, extra = {}) {
|
|
5308
|
+
const in_conversation_item_obj = {
|
|
5309
|
+
_id: response_conversation_item_id,
|
|
5310
|
+
stat: 3,
|
|
5311
|
+
docType: 'chat_conversation_item',
|
|
5312
|
+
uid,
|
|
5313
|
+
conversation_type: 'dashboard',
|
|
5314
|
+
type: 'dashboard',
|
|
5315
|
+
date_created_ts: Date.now(),
|
|
5316
|
+
ts: Date.now(),
|
|
5317
|
+
conversation_id,
|
|
5318
|
+
text,
|
|
5319
|
+
reference_id: conversation_doc.reference_id,
|
|
5320
|
+
direction: 'in',
|
|
5321
|
+
role: 'assistant',
|
|
5322
|
+
read: { [uid]: Date.now() },
|
|
5323
|
+
rtl: _common.detectRTL(text),
|
|
5324
|
+
job_id,
|
|
5325
|
+
context: dashboard_context,
|
|
5326
|
+
target_app_id,
|
|
5327
|
+
prompt_conversation_item_id,
|
|
5328
|
+
...extra,
|
|
5329
|
+
};
|
|
5330
|
+
|
|
5331
|
+
return await db_module.save_app_couch_doc_native(account_profile_info.app_id, in_conversation_item_obj);
|
|
5332
|
+
};
|
|
5333
|
+
|
|
5334
|
+
const formatDashboardHistoryItem = function (item) {
|
|
5335
|
+
const role = item.role === 'assistant' ? 'Assistant' : 'User';
|
|
5336
|
+
const type = item.conversation_type || item.type || 'message';
|
|
5337
|
+
const file_name = item?.file?.filename ? ` file=${item.file.filename}` : '';
|
|
5338
|
+
let text = (item.text || item.prompt || '').trim();
|
|
5339
|
+
if (text.length > 2500) {
|
|
5340
|
+
text = `${text.slice(0, 2500)}...`;
|
|
5341
|
+
}
|
|
5342
|
+
|
|
5343
|
+
return `${role} (${type}${file_name}): ${text}`;
|
|
5344
|
+
};
|
|
5345
|
+
|
|
5346
|
+
const getDashboardContextPrompt = async function () {
|
|
5347
|
+
const ret = await db_module.find_app_couch_query(account_profile_info.app_id, {
|
|
5348
|
+
selector: {
|
|
5349
|
+
docType: 'chat_conversation_item',
|
|
5350
|
+
conversation_id,
|
|
5351
|
+
stat: 3,
|
|
5352
|
+
},
|
|
5353
|
+
limit: 12,
|
|
5354
|
+
sort: [{ date_created_ts: 'desc' }],
|
|
5355
|
+
});
|
|
5356
|
+
|
|
5357
|
+
const items = (ret.docs || []).reverse().filter((item) => item.text || item.prompt || item?.file?.filename);
|
|
5358
|
+
const conversation_history = items.map(formatDashboardHistoryItem).join('\n\n');
|
|
5359
|
+
|
|
5360
|
+
return `You are handling the latest Xuda dashboard chat request.
|
|
5361
|
+
|
|
5362
|
+
Dashboard context/tab: ${dashboard_context || 'unknown'}
|
|
5363
|
+
Target app_id/reference_id: ${target_app_id}
|
|
5364
|
+
|
|
5365
|
+
Use the conversation history, attachment transcripts, and latest user message to infer the dashboard area when context is unknown.
|
|
5366
|
+
|
|
5367
|
+
Conversation history:
|
|
5368
|
+
${conversation_history || `User (dashboard): ${prompt}`}
|
|
5369
|
+
`;
|
|
5370
|
+
};
|
|
5371
|
+
|
|
5372
|
+
try {
|
|
5373
|
+
emitToDashboard('stream_start');
|
|
5374
|
+
emitToDashboard('stream_phase', dashboard_context === 'console' ? 'Starting console request' : 'Starting dashboard request');
|
|
5375
|
+
|
|
5376
|
+
await saveUserItem();
|
|
5377
|
+
|
|
5378
|
+
if (attachments?.length) {
|
|
5379
|
+
emitToDashboard('stream_phase', 'Analyzing attachments', { update: true });
|
|
5380
|
+
await attachment_handler(uid, account_profile_info.app_id, conversation_id, attachments, account_profile_info);
|
|
5381
|
+
}
|
|
5382
|
+
|
|
5383
|
+
conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_id);
|
|
5384
|
+
conversation_doc.ts = Date.now();
|
|
5385
|
+
conversation_doc.stat = 2;
|
|
5386
|
+
conversation_doc.request_started_at = request_started_at;
|
|
5387
|
+
conversation_doc.job_id = job_id;
|
|
5388
|
+
conversation_doc.context = dashboard_context;
|
|
5389
|
+
conversation_doc.target_app_id = target_app_id;
|
|
5390
|
+
await db_module.save_app_couch_doc_native(account_profile_info.app_id, conversation_doc);
|
|
5391
|
+
|
|
5392
|
+
if (dashboard_context === 'console') {
|
|
5393
|
+
const codex_ret = await execute_codex_request({ ...req, conversation_id, conversation_doc, prompt, attachments, stream: false }, job_id, headers);
|
|
5394
|
+
const codex_events = codex_ret?.data?.events || [];
|
|
5395
|
+
const last_message = [...codex_events].reverse().find((event) => event?.item?.type === 'agent_message' && event.item.text)?.item?.text;
|
|
5396
|
+
const response_text = codex_ret.code < 0 ? `Console request failed: ${get_error_message(codex_ret.data, 'Codex request failed')}` : last_message || 'Console request completed.';
|
|
5397
|
+
|
|
5398
|
+
emitToDashboard('response_start');
|
|
5399
|
+
streamText(response_text);
|
|
5400
|
+
emitToDashboard('stream_end');
|
|
5401
|
+
|
|
5402
|
+
conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_id);
|
|
5403
|
+
conversation_doc.ts = Date.now();
|
|
5404
|
+
conversation_doc.stat = 3;
|
|
5405
|
+
conversation_doc.process_stat = codex_ret.code < 0 ? 'partial' : 'full';
|
|
5406
|
+
await db_module.save_app_couch_doc_native(account_profile_info.app_id, conversation_doc);
|
|
5407
|
+
|
|
5408
|
+
return await saveAssistantItem(response_text, { codex_result: codex_ret });
|
|
5409
|
+
}
|
|
5410
|
+
|
|
5411
|
+
const dashboard_prompt = await getDashboardContextPrompt();
|
|
5412
|
+
const app_obj = await get_app_obj(target_app_id);
|
|
5413
|
+
const userName = await account_ms.get_user_name(uid);
|
|
5414
|
+
const account_info = (await get_account_name({ uid })).data;
|
|
5415
|
+
const context = { ...{ app_id: target_app_id, uid, profile_id, userName, account_info, app_obj, account_id: uid, dashboard_context }, ...extractClientInfo(headers) };
|
|
5416
|
+
const cpi_tools_ret = get_dashboard_cpi_tools({ uid, profile_id, gtp_token, app_id: target_app_id, context: dashboard_context, prompt: dashboard_prompt });
|
|
5417
|
+
|
|
5418
|
+
if (!cpi_tools_ret.tools.length) {
|
|
5419
|
+
throw new Error(`no CPI methods available for dashboard context "${dashboard_context || 'unknown'}"`);
|
|
5420
|
+
}
|
|
5421
|
+
|
|
5422
|
+
const agent = new Agent({
|
|
5423
|
+
name: 'Dashboard CPI Agent',
|
|
5424
|
+
instructions: `
|
|
5425
|
+
You are Xuda dashboard chat.
|
|
5426
|
+
The user is interacting with the dashboard tab/context "${dashboard_context || 'unknown'}" for app_id "${target_app_id}".
|
|
5427
|
+
Use the available CPI tools to inspect, update, or explain dashboard data. Prefer tool calls over guessing.
|
|
5428
|
+
If the screenshot or prompt implies the tab/context, infer the relevant dashboard area and choose the closest CPI method.
|
|
5429
|
+
For data/database requests, use the database/table CPI methods and keep app_id/reference_id anchored to "${target_app_id}".
|
|
5430
|
+
Summarize the action and important CPI result fields clearly. Do not invent successful changes; report tool errors as errors.
|
|
5431
|
+
Available CPI methods: ${cpi_tools_ret.selected_methods.join(', ')}
|
|
5432
|
+
`.trim(),
|
|
5433
|
+
model: ai_model,
|
|
5434
|
+
tools: cpi_tools_ret.tools,
|
|
5435
|
+
});
|
|
5436
|
+
|
|
5437
|
+
agent.on('agent_tool_start', (context, tool) => {
|
|
5438
|
+
emitToDashboard('stream_phase', `Starting ${tool?.name?.replaceAll('_', ' ')} tool`, { update: true });
|
|
5439
|
+
});
|
|
5440
|
+
|
|
5441
|
+
emitToDashboard('stream_phase', 'Submitting dashboard request', { update: true });
|
|
5442
|
+
const runner = new Runner();
|
|
5443
|
+
const output = await runner.run(agent, dashboard_prompt, { context, stream });
|
|
5444
|
+
let response_text = '';
|
|
5445
|
+
|
|
5446
|
+
if (stream) {
|
|
5447
|
+
const output_stream = output.toTextStream({ compatibleWithNodeStreams: true });
|
|
5448
|
+
let response_started = false;
|
|
5449
|
+
for await (const chunk of output_stream) {
|
|
5450
|
+
if (!response_started) {
|
|
5451
|
+
response_started = true;
|
|
5452
|
+
emitToDashboard('stream_phase', 'Streaming results', { update: true });
|
|
5453
|
+
emitToDashboard('response_start');
|
|
5454
|
+
}
|
|
5455
|
+
const text = chunk.toString();
|
|
5456
|
+
response_text += text;
|
|
5457
|
+
emitToDashboard('stream_delta', text);
|
|
5458
|
+
}
|
|
5459
|
+
}
|
|
5460
|
+
|
|
5461
|
+
response_text = response_text || output?.state?._currentStep?.output || output?.finalOutput || output?.output || 'Dashboard request completed.';
|
|
5462
|
+
emitToDashboard('stream_end');
|
|
5463
|
+
|
|
5464
|
+
conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_id);
|
|
5465
|
+
conversation_doc.ts = Date.now();
|
|
5466
|
+
conversation_doc.stat = 3;
|
|
5467
|
+
conversation_doc.process_stat = 'full';
|
|
5468
|
+
await db_module.save_app_couch_doc_native(account_profile_info.app_id, conversation_doc);
|
|
5469
|
+
|
|
5470
|
+
return await saveAssistantItem(response_text, {
|
|
5471
|
+
cpi_methods: cpi_tools_ret.selected_methods,
|
|
5472
|
+
ai_agent_id: 'dashboard_cpi_agent',
|
|
5473
|
+
});
|
|
5474
|
+
} catch (err) {
|
|
5475
|
+
const error_message = get_error_message(err, 'dashboard request failed');
|
|
5476
|
+
emitToDashboard('stream_phase', 'Dashboard request failed', { update: true });
|
|
5477
|
+
emitToDashboard('response_start');
|
|
5478
|
+
streamText(`Dashboard request failed: ${error_message}`);
|
|
5479
|
+
emitToDashboard('stream_end');
|
|
5480
|
+
|
|
5481
|
+
conversation_doc = await db_module.get_app_couch_doc_native(account_profile_info.app_id, conversation_id);
|
|
5482
|
+
conversation_doc.ts = Date.now();
|
|
5483
|
+
conversation_doc.stat = 3;
|
|
5484
|
+
conversation_doc.process_stat = 'partial';
|
|
5485
|
+
await db_module.save_app_couch_doc_native(account_profile_info.app_id, conversation_doc);
|
|
5486
|
+
|
|
5487
|
+
return { code: -17, data: error_message };
|
|
5488
|
+
}
|
|
5489
|
+
};
|
|
5490
|
+
|
|
5061
5491
|
const get_ai_agent_tools = async function ({ ai_agent_doc, agent_id, reference_type, prompt_suggestion_activated, chat_suggestion_activated, gtp_token, uid, account_profile_info, job_id, headers, context, app_id }) {
|
|
5062
5492
|
let tools = [];
|
|
5063
5493
|
let tool_resources = {};
|