Files
codex-market-app/electron/lib/title_clean.js
T

91 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
const SKILL_LINK_RE = /\[\$([^\]\n]+)\]\(([^)\n]*)\)/g;
const LEADING_SKILL_LINK_RE = /^\s*\[\$([^\]\n]+)\]\(([^)\n]*)\)\s*/;
const TRUNCATED_LEADING_SKILL_LINK_RE = /^\s*\[\$([^\]\n]+)\]\([^)\n]*$/;
const LEADING_SKILL_TOKEN_RE = /^\s*\$[A-Za-z][\w:.-]*(?:\s+|$)/;
function normalizeInlineText(text) {
return String(text || "")
.replace(/\s+/g, " ")
.trim();
}
function cleanSkillLinks(text) {
return String(text || "").replace(SKILL_LINK_RE, (_m, name) => `$${String(name || "").trim()}`);
}
function stripLeadingSkillMentions(text) {
let out = cleanSkillLinks(text);
let changed = true;
while (changed) {
changed = false;
const before = out;
out = out.replace(LEADING_SKILL_LINK_RE, "");
out = out.replace(TRUNCATED_LEADING_SKILL_LINK_RE, "");
out = out.replace(LEADING_SKILL_TOKEN_RE, "");
out = out.replace(/^\s*[::,,;;||\-–—]+\s*/, "");
changed = out !== before;
}
return out;
}
function cleanUserVisibleText(text) {
return cleanSkillLinks(text);
}
// 某些中转模型会把内部思考误当成普通正文,用 XML 风格标签直接输出。
// 只清理明确的内部块,不改动普通文本中的尖括号内容。
function stripInternalThoughtBlocks(text) {
let out = String(text || "");
for (const name of ["thinking", "analysis", "reasoning"]) {
const re = new RegExp("<" + name + "\\b[^>]*>[\\s\\S]*?</" + name + "\\s*>", "gi");
out = out.replace(re, "");
}
return out.replace(/<\/?(?:thinking|analysis|reasoning)\b[^>]*>/gi, "");
}
// 流式内容可能把标签拆在多个 chunk 中:未闭合的内部块整体暂不输出。
function stripInternalThoughtStreaming(text) {
const source = String(text || "");
let out = "";
let cursor = 0;
const openRe = /<(thinking|analysis|reasoning)\b[^>]*>/gi;
while (true) {
openRe.lastIndex = cursor;
const open = openRe.exec(source);
if (!open) {
out += source.slice(cursor);
break;
}
out += source.slice(cursor, open.index);
const closeRe = new RegExp("</" + open[1] + "\\s*>", "i");
const tail = source.slice(openRe.lastIndex);
const close = closeRe.exec(tail);
if (!close) break;
cursor = openRe.lastIndex + close.index + close[0].length;
}
return stripInternalThoughtBlocks(out);
}
function cleanThreadTitle(title, fallback = "", maxLen = 80) {
const primary = normalizeInlineText(stripLeadingSkillMentions(title));
const secondary = normalizeInlineText(stripLeadingSkillMentions(fallback));
const cleaned = primary || secondary || normalizeInlineText(cleanSkillLinks(title)) || normalizeInlineText(cleanSkillLinks(fallback));
return cleaned ? cleaned.slice(0, maxLen) : "";
}
function looksLikeSkillMentionTitle(title) {
const text = normalizeInlineText(title);
return /^\[\$[^\]]+\]\(/.test(text) || /^\$[A-Za-z][\w:.-]*(?:\s|$)/.test(text);
}
module.exports = {
cleanSkillLinks,
cleanThreadTitle,
cleanUserVisibleText,
stripInternalThoughtBlocks,
stripInternalThoughtStreaming,
looksLikeSkillMentionTitle,
};