perf: 优化AI创建任务

This commit is contained in:
kuaifan 2024-12-06 19:49:45 +08:00
parent 13222fbe9a
commit cbd9e8a33c
5 changed files with 180 additions and 158 deletions

View File

@ -2893,8 +2893,6 @@ class DialogController extends AbstractController
* @apiGroup dialog * @apiGroup dialog
* @apiName msg__applied * @apiName msg__applied
* *
* @apiParam {String} type 类型
* - CreateTask: 创建任务
* @apiParam {Number} index 索引 * @apiParam {Number} index 索引
* @apiParam {Number} msg_id 消息ID * @apiParam {Number} msg_id 消息ID
* *
@ -2907,7 +2905,6 @@ class DialogController extends AbstractController
User::auth(); User::auth();
// //
$msg_id = intval(Request::input('msg_id')); $msg_id = intval(Request::input('msg_id'));
$type = trim(Request::input('type'));
$index = intval(Request::input('index')); $index = intval(Request::input('index'));
// //
$msg = WebSocketDialogMsg::whereId($msg_id)->first(); $msg = WebSocketDialogMsg::whereId($msg_id)->first();
@ -2917,17 +2914,17 @@ class DialogController extends AbstractController
WebSocketDialog::checkDialog($msg->dialog_id); WebSocketDialog::checkDialog($msg->dialog_id);
// //
$originalMsg = $msg->getRawOriginal('msg'); $originalMsg = $msg->getRawOriginal('msg');
$pattern = '/```\s*' . preg_quote($type, '/') . '\s*(applying|applied)?\s*(\n|\\\\n)/'; $pattern = '/:::\s*(create-task-list|create-subtask-list)(?:\s+(\S+))?/';
$count = -1; $count = -1;
$updatedMsg = preg_replace_callback($pattern, function($matches) use (&$count, $index, $type) { $updatedMsg = preg_replace_callback($pattern, function($matches) use (&$count, $index) {
$count++; $count++;
if ($count === $index || ($index === 0 && $count === 1)) { if ($count === $index || ($index === 0 && $count === 1)) {
return "```{$type} applied{$matches[2]}"; return "::: {$matches[1]} applied";
} }
return $matches[0]; return $matches[0];
}, $originalMsg); }, $originalMsg);
if ($count === 0) { if ($count === -1) {
return Base::retError("未找到可应用的规则"); return Base::retError("未找到可应用的规则");
} }

View File

@ -593,13 +593,13 @@ class BotReceiveMsgTask extends AbstractTask
} }
$before_text[] = <<<EOF $before_text[] = <<<EOF
如果你判断我想要添加任务,请将任务标题和描述添加到回复中,例如: 如果你判断我想要添加任务,请将任务标题和描述添加到回复中,例如:
```CreateTask ::: create-task-list
title: 任务标题1 title: 任务标题1
desc: 任务描述1 desc: 任务描述1
title: 任务标题2 title: 任务标题2
desc: 任务描述2 desc: 任务描述2
``` :::
EOF; EOF;
} }
break; break;
@ -621,10 +621,10 @@ class BotReceiveMsgTask extends AbstractTask
} }
$before_text[] = <<<EOF $before_text[] = <<<EOF
如果你判断我想要添加子任务,请将子任务标题添加到回复中,例如: 如果你判断我想要添加子任务,请将子任务标题添加到回复中,例如:
```CreateSubTask ::: create-subtask-list
子任务标题1 title: 子任务标题1
子任务标题2 title: 子任务标题2
``` :::
EOF; EOF;
} }
break; break;

View File

@ -3220,18 +3220,40 @@ export default {
}); });
}, },
async applyCreateTask(event, el) { async applyCreateTask(type, event, el) {
const currentTarget = event.target; const currentTarget = event.target;
if (currentTarget.classList.contains('applying') || currentTarget.classList.contains('applied')) { if (currentTarget.classList.contains('applying') || currentTarget.classList.contains('applied')) {
return; return;
} }
currentTarget.classList.add('applying') currentTarget.classList.add('applying')
if (type === 'task') {
if (this.dialogData.group_type !== 'project') { if (this.dialogData.group_type !== 'project') {
currentTarget.classList.remove('applying') currentTarget.classList.remove('applying')
$A.modalError('只有在项目中才能创建任务') $A.modalError('只有在项目中才能创建任务')
return return
} }
if (!this.dialogData.group_info) {
currentTarget.classList.remove('applying')
$A.modalError('项目不存在')
return;
}
} else if (type === 'subtask') {
if (this.dialogData.group_type !== 'task') {
currentTarget.classList.remove('applying')
$A.modalError('只有在任务中才能创建子任务')
return
}
if (!this.dialogData.group_info) {
currentTarget.classList.remove('applying')
$A.modalError('任务不存在')
return;
}
} else {
currentTarget.classList.remove('applying')
$A.modalError('未知类型')
return
}
let target = event.target; let target = event.target;
while (target) { while (target) {
@ -3246,14 +3268,9 @@ export default {
} }
if (!target) { if (!target) {
currentTarget.classList.remove('applying') currentTarget.classList.remove('applying')
$A.modalError('未找到任务内容') $A.modalError('未找到内容')
return return
} }
if (!this.dialogData.group_info) {
currentTarget.classList.remove('applying')
$A.modalError('项目不存在')
return;
}
const allTaskElements = el.querySelectorAll('.apply-create-task'); const allTaskElements = el.querySelectorAll('.apply-create-task');
const taskIndex = Array.from(allTaskElements).indexOf(target); const taskIndex = Array.from(allTaskElements).indexOf(target);
@ -3268,6 +3285,12 @@ export default {
.map(line => `<p>${line.trim()}</p>`) .map(line => `<p>${line.trim()}</p>`)
.join('') : ''; .join('') : '';
if (type === 'subtask') {
return {
task_id: this.dialogData.group_info.id,
name: title,
};
}
return { return {
project_id: this.dialogData.group_info.id, project_id: this.dialogData.group_info.id,
name: title, name: title,
@ -3276,29 +3299,40 @@ export default {
}) })
.filter(Boolean); .filter(Boolean);
const typeCall = type === 'subtask' ? 'taskAddSub' : 'taskAdd';
const typeLabel = type === 'subtask' ? '子任务' : '任务';
const results = await Promise.all(taskList.map(item => const results = await Promise.all(taskList.map(item =>
this.$store.dispatch("taskAdd", item).then( this.$store.dispatch(typeCall, item).then(
success => ({ success: true, data: success }), success => ({ success: true, data: success }),
error => ({ success: false, error: error }) error => ({ success: false, error: error })
) )
)); ));
const successTasks = results.filter(r => r.success).map(r => r.data); const successTasks = results.filter(r => r.success).map(r => r.data);
const failedTasks = results.filter(r => !r.success).map(r => r.error); const failedTasks = results.filter(r => !r.success).map(r => r.error);
let notice = `${this.$store.state.userInfo.nickname} 成功创建 ${successTasks.length}${typeLabel}`;
if (failedTasks.length > 0) { if (failedTasks.length > 0) {
$A.modalError(`成功创建 ${successTasks.length} 个任务,${failedTasks.length} 个任务创建失败`); notice += `${failedTasks.length}${typeLabel}创建失败`;
} else {
$A.messageSuccess(`成功创建 ${successTasks.length} 个任务`);
} }
currentTarget.classList.remove('applying') currentTarget.classList.remove('applying')
currentTarget.classList.add('applied') currentTarget.classList.add('applied')
const {data} = await this.$store.dispatch("call", {
url: 'dialog/msg/sendnotice',
data: {
dialog_id: this.dialogId,
source: 'ai',
notice,
},
});
this.sendSuccess(data)
await this.$store.dispatch("call", { await this.$store.dispatch("call", {
url: 'dialog/msg/applied', url: 'dialog/msg/applied',
data: { data: {
type: 'CreateTask', msg_id: this.operateItem.id,
index: taskIndex, index: taskIndex,
msg_id: this.operateItem.id
}, },
}); });
}, },
@ -3411,7 +3445,14 @@ export default {
// //
if (target.classList.contains('apply-create-task-button')) { if (target.classList.contains('apply-create-task-button')) {
this.operateItem = this.findMsgByElement(el) this.operateItem = this.findMsgByElement(el)
this.applyCreateTask(event, el) this.applyCreateTask('task', event, el)
return;
}
//
if (target.classList.contains('apply-create-subtask-button')) {
this.operateItem = this.findMsgByElement(el)
this.applyCreateTask('subtask', event, el)
return; return;
} }

View File

@ -39,10 +39,13 @@ const MarkdownUtils = {
const MarkdownPluginUtils = { const MarkdownPluginUtils = {
// 配置选项 // 配置选项
config: { config: {
maxItems: 200,
maxTitleLength: 200, maxTitleLength: 200,
maxDescLength: 1000, maxDescLength: 1000,
maxItems: 200, buttonLabels: {
defaultTitle: '创建任务' task: '创建任务',
subtask: '创建子任务'
}
}, },
// HTML转义函数 // HTML转义函数
@ -65,90 +68,15 @@ const MarkdownPluginUtils = {
return value; return value;
}, },
// 解析任务项
parseTaskItems(content) {
const items = [];
let currentItem = {};
let itemCount = 0;
content.forEach(line => {
line = line.trim();
if (!line) {
if (Object.keys(currentItem).length > 0) {
items.push(currentItem);
currentItem = {};
itemCount++;
}
return;
}
if (itemCount >= this.config.maxItems) {
return;
}
const [key, ...valueParts] = line.split(':');
const value = valueParts.join(':').trim();
if (key === 'title' && value) {
if (Object.keys(currentItem).length > 0) {
items.push(currentItem);
currentItem = {};
itemCount++;
}
currentItem.title = this.validateInput(value, this.config.maxTitleLength);
} else if (key === 'desc' && value) {
currentItem.desc = this.validateInput(value, this.config.maxDescLength);
}
});
if (Object.keys(currentItem).length > 0 && itemCount < this.config.maxItems) {
items.push(currentItem);
}
return items;
},
// 生成HTML
generateTaskHtml(items, status = null) {
if (!Array.isArray(items) || items.length === 0) {
return '';
}
const html = [
'<div class="apply-create-task">',
'<ul>'
];
items.forEach((item, index) => {
if (item.title) {
html.push(`<li>`);
html.push(`<div class="task-index">${index + 1}.</div>`);
html.push(`<div class="task-item">`);
html.push(`<div class="title">${this.escapeHtml(item.title)}</div>`);
if (item.desc) {
html.push(`<div class="desc">${this.escapeHtml(item.desc)}</div>`);
}
html.push('</div>');
html.push('</li>');
}
});
html.push(
'</ul>',
`<div class="apply-button"><div class="apply-create-task-button ${status||''}">${this.escapeHtml($A.L(this.config.defaultTitle))}</div></div>`,
'</div>'
);
return html.join('\n');
},
// 修改初始化插件函数 // 修改初始化插件函数
initCreateTaskPlugin(md) { initCreateTaskPlugin(md) {
md.block.ruler.before('fence', 'create_task', (state, startLine, endLine, silent) => { md.block.ruler.before('fence', 'create-task', (state, startLine, endLine, silent) => {
try {
const start = state.bMarks[startLine] + state.tShift[startLine]; const start = state.bMarks[startLine] + state.tShift[startLine];
const max = state.eMarks[startLine]; const max = state.eMarks[startLine];
const firstLine = state.src.slice(start, max).trim();
const match = state.src.slice(start, max).trim().match(/^```\s*CreateTask\s*(applying|applied)?$/); // 检查开始标记并获取status值
const match = firstLine.match(/^:::\s*(create-task-list|create-subtask-list)(?:\s+(\S+))?$/);
if (!match) { if (!match) {
return false; return false;
} }
@ -157,45 +85,101 @@ const MarkdownPluginUtils = {
return true; return true;
} }
// 获取按钮标题和状态
const listType = match[1] === 'create-task-list' ? 'task' : 'subtask';
const buttonTitle = this.config.buttonLabels[listType] || '';
const status = match[2] || '';
let nextLine = startLine + 1; let nextLine = startLine + 1;
let content = []; let content = [];
let found = false;
// 查找结束标记
while (nextLine < endLine) { while (nextLine < endLine) {
const line = state.src.slice(state.bMarks[nextLine], state.eMarks[nextLine]).trim(); const lineStart = state.bMarks[nextLine] + state.tShift[nextLine];
if (line === '```') { const lineMax = state.eMarks[nextLine];
found = true; const line = state.src.slice(lineStart, lineMax);
if (line.trim() === ':::') {
break; break;
} }
content.push(line); content.push(line);
nextLine++; nextLine++;
} }
if (!found) { // 解析任务
return false; const tasks = [];
let currentTask = null;
let isCollectingDesc = false;
let descLines = [];
content.forEach(line => {
const titleMatch = line.trim().match(/^title:\s*(.+)$/);
const descMatch = line.trim().match(/^desc:\s*(.*)$/);
if (titleMatch) {
// 如果已经有一个任务在处理中,保存它
if (currentTask) {
if (descLines.length > 0) {
currentTask.desc = descLines.join('\n');
}
tasks.push(currentTask);
} }
// 创建 token 并设置为空字符串内容 // 开始新的任务
const token = state.push('html_block', '', 0); currentTask = {title: titleMatch[1]};
isCollectingDesc = false;
// 如果有内容则解析并生成HTML descLines = [];
if (content.length > 0) { } else if (descMatch) {
const items = this.parseTaskItems(content); isCollectingDesc = true;
const html = this.generateTaskHtml(items, match[1]); if (descMatch[1]) {
token.content = html || ''; descLines.push(descMatch[1]);
} else {
token.content = ''; // 空内容直接返回空字符串
} }
} else if (isCollectingDesc && line.trim() && !line.trim().startsWith('title:')) {
token.map = [startLine, nextLine + 1]; // 收集多行描述但不包括空行和新的title行
state.line = nextLine + 1; descLines.push(line.trim());
return true;
} catch (error) {
console.error('Error in create_task parser:', error);
return false;
} }
}); });
// 处理最后一个任务
if (currentTask) {
if (descLines.length > 0) {
currentTask.desc = descLines.join('\n');
}
tasks.push(currentTask);
}
// 生成HTML
const showIndex = tasks.length > 1;
const taskItems = tasks.slice(0, this.config.maxItems).map((task, index) => [
'<li>',
showIndex ? `<div class="task-index">${index + 1}.</div>` : '',
'<div class="task-item">',
`<div class="title">${this.escapeHtml(this.validateInput(task.title, this.config.maxTitleLength))}</div>`,
task.desc && match[1] === 'create-task-list' ? `<div class="desc">${this.escapeHtml(this.validateInput(task.desc, this.config.maxDescLength))}</div>` : '',
'</div>',
'</li>'
].join(''));
const htmls = [
'<div class="apply-create-task">',
'<ul>',
taskItems.join(''),
'</ul>',
'<div class="apply-button">',
`<div class="apply-create-${listType}-button${status ? ' ' + status : ''}">${$A.L(buttonTitle)}</div>`,
'</div>',
'</div>'
];
// 添加token
const token = state.push('html_block', '', 0);
token.content = htmls.join('');
token.map = [startLine, nextLine];
state.line = nextLine + 1;
return true;
})
} }
}; };

View File

@ -2118,7 +2118,7 @@
} }
} }
&.applied { &.applied {
color: #c5c8ce; color: #a5a8ae;
background-color: #f7f7f7; background-color: #f7f7f7;
border-color: #dcdee2; border-color: #dcdee2;
&:before { &:before {