feat: 引入文本提取功能,优化AI内容解析逻辑,移除冗余代码

This commit is contained in:
kuaifan 2025-11-08 20:42:21 +00:00
parent ecb52c76b9
commit 0deb3113b5
5 changed files with 56 additions and 93 deletions

View File

@ -457,7 +457,7 @@ import SearchBox from "../components/SearchBox.vue";
import AIAssistant from "../components/AIAssistant.vue";
import transformEmojiToHtml from "../utils/emoji";
import {languageName} from "../language";
import {PROJECT_AI_SYSTEM_PROMPT} from "../utils/ai";
import {AINormalizeJsonContent, PROJECT_AI_SYSTEM_PROMPT} from "../utils/ai";
import Draggable from 'vuedraggable'
export default {
@ -1197,39 +1197,8 @@ export default {
return [];
},
normalizeAIJsonContent(content) {
if (!content) {
return null;
}
const raw = String(content).trim();
if (!raw) {
return null;
}
const candidates = [raw];
const block = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
if (block && block[1]) {
candidates.push(block[1].trim());
}
const start = raw.indexOf('{');
const end = raw.lastIndexOf('}');
if (start !== -1 && end !== -1 && end > start) {
candidates.push(raw.slice(start, end + 1));
}
for (const candidate of candidates) {
if (!candidate) {
continue;
}
try {
return JSON.parse(candidate);
} catch (e) {
continue;
}
}
return null;
},
parseProjectAIContent(content) {
const payload = this.normalizeAIJsonContent(content);
const payload = AINormalizeJsonContent(content);
if (!payload || typeof payload !== 'object') {
return null;
}

View File

@ -343,6 +343,7 @@ import longpress from "../../../../directives/longpress";
import {inputLoadAdd, inputLoadIsLast, inputLoadRemove} from "./one";
import {languageList, languageName} from "../../../../language";
import {isMarkdownFormat, MarkdownConver} from "../../../../utils/markdown";
import {extractPlainText} from "../../../../utils/text";
import {MESSAGE_AI_SYSTEM_PROMPT} from "../../../../utils/ai";
import emitter from "../../../../store/events";
import historyMixin from "./history";
@ -1911,7 +1912,6 @@ export default {
emitter.emit('openAIAssistant', {
placeholder: this.$L('请简要描述消息的主题、语气或要点AI 将生成完整消息'),
onBeforeSend: this.handleMessageAIBeforeSend,
onRender: this.handleMessageAIRender,
onApply: this.handleMessageAIApply,
});
},
@ -1933,10 +1933,6 @@ export default {
return prepared;
},
handleMessageAIRender({rawOutput}) {
return rawOutput || '';
},
handleMessageAIApply({rawOutput}) {
if (!rawOutput) {
$A.messageWarning('AI 未生成内容');
@ -1991,7 +1987,7 @@ export default {
}
}
const draftText = this.extractPlainText(this.value);
const draftText = extractPlainText(this.value);
if (draftText) {
sections.push('## 当前草稿');
sections.push(this.cutText(draftText, 200));
@ -2062,26 +2058,13 @@ export default {
}
try {
const preview = $A.getMsgSimpleDesc(message);
const plain = this.extractPlainText(preview || '');
const plain = extractPlainText(preview || '');
return this.cutText(plain, 160);
} catch (error) {
return '';
}
},
extractPlainText(content) {
if (!content) {
return '';
}
const value = typeof content === 'string' ? content : JSON.stringify(content);
if (typeof window === 'undefined' || !window.document) {
return value.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
}
const div = document.createElement('div');
div.innerHTML = value;
return (div.textContent || div.innerText || '').replace(/\s+/g, ' ').trim();
},
cutText(text, limit = 60) {
const value = (text || '').trim();
if (!value) {

View File

@ -201,7 +201,8 @@ import TaskExistTips from "./TaskExistTips.vue";
import TEditorTask from "../../../components/TEditorTask.vue";
import nostyle from "../../../components/VMEditor/engine/nostyle";
import {MarkdownConver} from "../../../utils/markdown";
import {TASK_AI_SYSTEM_PROMPT} from "../../../utils/ai";
import {extractPlainText} from "../../../utils/text";
import {AINormalizeJsonContent, TASK_AI_SYSTEM_PROMPT} from "../../../utils/ai";
export default {
name: "TaskAdd",
@ -639,15 +640,11 @@ export default {
buildTaskAIContextData() {
const prompts = [];
const plainText = (value, limit = 600) => {
if (!value || typeof value !== 'string') {
const text = extractPlainText(value || '');
if (!text) {
return '';
}
return value
.replace(/<[^>]+>/g, ' ')
.replace(/&nbsp;/gi, ' ')
.replace(/\s+/g, ' ')
.slice(0, limit)
.trim();
return text.slice(0, limit).trim();
};
const currentTitle = (this.addData.name || '').trim();
@ -778,7 +775,7 @@ export default {
},
parseTaskAIContent(content) {
const payload = this.normalizeAIJsonContent(content);
const payload = AINormalizeJsonContent(content);
if (!payload || typeof payload !== 'object') {
return null;
}
@ -802,37 +799,6 @@ export default {
};
},
normalizeAIJsonContent(content) {
if (!content) {
return null;
}
const raw = String(content).trim();
if (!raw) {
return null;
}
const candidates = [raw];
const block = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
if (block && block[1]) {
candidates.push(block[1].trim());
}
const start = raw.indexOf('{');
const end = raw.lastIndexOf('}');
if (start !== -1 && end !== -1 && end > start) {
candidates.push(raw.slice(start, end + 1));
}
for (const candidate of candidates) {
if (!candidate) {
continue;
}
try {
return JSON.parse(candidate);
} catch (e) {
continue;
}
}
return null;
},
normalizeAISubtasks(value) {
let raw = [];
if (Array.isArray(value)) {

View File

@ -11,6 +11,37 @@ const AIModelNames = (str) => {
}, []).filter(item => item.value);
}
const AINormalizeJsonContent = (content) => {
if (!content) {
return null;
}
const raw = String(content).trim();
if (!raw) {
return null;
}
const candidates = [raw];
const block = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
if (block && block[1]) {
candidates.push(block[1].trim());
}
const start = raw.indexOf('{');
const end = raw.lastIndexOf('}');
if (start !== -1 && end !== -1 && end > start) {
candidates.push(raw.slice(start, end + 1));
}
for (const candidate of candidates) {
if (!candidate) {
continue;
}
try {
return JSON.parse(candidate);
} catch (e) {
// continue
}
}
return null;
}
const AIBotList = []
const AIBotMap = {
@ -295,6 +326,7 @@ const PROJECT_AI_SYSTEM_PROMPT = `你是一名资深的项目规划顾问,帮
export {
AIModelNames,
AINormalizeJsonContent,
AIBotList,
AIBotMap,
AISystemConfig,

13
resources/assets/js/utils/text.js vendored Normal file
View File

@ -0,0 +1,13 @@
export function extractPlainText(content) {
if (!content) {
return '';
}
const value = typeof content === 'string' ? content : JSON.stringify(content);
if (typeof window === 'undefined' || !window.document) {
return value.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
}
const div = document.createElement('div');
div.innerHTML = value;
return (div.textContent || div.innerText || '').replace(/\s+/g, ' ').trim();
}