58909a21a7
Portal 在 web 技能提交 Goose 前注入专用搜索结果,并要求先 tkmind_search、禁止先 fetch_url 抓热榜整页。
142 lines
4.2 KiB
JavaScript
142 lines
4.2 KiB
JavaScript
import { searchSearxng } from './mindsearch-providers.mjs';
|
|
|
|
const LIVE_SEARCH_SKILLS = new Set(['web', 'search-enhanced', 'web-search']);
|
|
|
|
function envFlag(value, fallback = false) {
|
|
const raw = String(value ?? '').trim().toLowerCase();
|
|
if (!raw) return fallback;
|
|
return ['1', 'true', 'yes', 'on'].includes(raw);
|
|
}
|
|
|
|
export function resolvePortalSearxngEndpoint(env = process.env, config = null) {
|
|
const raw = String(
|
|
env.TKMIND_SEARCH_SEARXNG_URL
|
|
?? config?.settings?.searxngEndpoint
|
|
?? '',
|
|
).trim();
|
|
if (!raw) return '';
|
|
try {
|
|
const url = new URL(raw);
|
|
if (url.hostname === 'host.docker.internal') url.hostname = '127.0.0.1';
|
|
return url.toString();
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
export function shouldPrefetchLiveSearch({
|
|
selectedSkill = '',
|
|
suggestedSkill = '',
|
|
env = process.env,
|
|
} = {}) {
|
|
if (!envFlag(env.MEMIND_LIVE_SEARCH_PREFETCH, true)) return false;
|
|
const skill = String(selectedSkill || suggestedSkill || '').trim();
|
|
return LIVE_SEARCH_SKILLS.has(skill);
|
|
}
|
|
|
|
export function formatPrefetchedSearchContext(results = [], { query = '' } = {}) {
|
|
const rows = (Array.isArray(results) ? results : [])
|
|
.filter((item) => item?.title || item?.url)
|
|
.slice(0, 10);
|
|
if (!rows.length) return '';
|
|
const lines = [
|
|
'【联网搜索预取】系统已先调用专用联网搜索。请优先使用下列结果回答;不要向用户提及 SearXNG/DuckDuckGo 等中间件名称。',
|
|
`查询:${String(query ?? '').trim()}`,
|
|
'禁止先 fetch_url 抓取热榜/热搜整页。若需补充,先调用 tkmind_search,再按需读取个别可靠来源。',
|
|
'',
|
|
];
|
|
for (const item of rows) {
|
|
lines.push(`${item.rank ?? ''}. ${item.title || item.url}`);
|
|
if (item.url) lines.push(` URL: ${item.url}`);
|
|
if (item.snippet) lines.push(` ${item.snippet}`);
|
|
}
|
|
return lines.join('\n').trim();
|
|
}
|
|
|
|
export function appendAgentVisibleText(userMessage, extraText) {
|
|
const block = String(extraText ?? '').trim();
|
|
if (!block) return userMessage;
|
|
const content = Array.isArray(userMessage?.content) ? [...userMessage.content] : [];
|
|
const extra = `\n\n${block}`;
|
|
if (!content.length) {
|
|
return {
|
|
...userMessage,
|
|
content: [{ type: 'text', text: block }],
|
|
metadata: {
|
|
...(userMessage?.metadata ?? {}),
|
|
agentVisible: true,
|
|
},
|
|
};
|
|
}
|
|
const first = content[0];
|
|
if (typeof first === 'string') {
|
|
content[0] = `${first}${extra}`;
|
|
} else if (first?.type === 'text') {
|
|
content[0] = { ...first, text: `${String(first.text ?? '')}${extra}` };
|
|
} else {
|
|
content.push({ type: 'text', text: block });
|
|
}
|
|
return {
|
|
...userMessage,
|
|
content,
|
|
metadata: {
|
|
...(userMessage?.metadata ?? {}),
|
|
agentVisible: userMessage?.metadata?.agentVisible ?? true,
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function prefetchLiveSearchResults({
|
|
query,
|
|
selectedSkill,
|
|
suggestedSkill,
|
|
env = process.env,
|
|
config = null,
|
|
searchImpl = searchSearxng,
|
|
} = {}) {
|
|
if (!shouldPrefetchLiveSearch({ selectedSkill, suggestedSkill, env })) {
|
|
return null;
|
|
}
|
|
const q = String(query ?? '').trim();
|
|
if (!q) return null;
|
|
const endpoint = resolvePortalSearxngEndpoint(env, config);
|
|
if (!endpoint) return null;
|
|
try {
|
|
const results = await searchImpl(q, {
|
|
endpoint,
|
|
limit: Number(env.TKMIND_SEARCH_MAX_RESULTS ?? 10) || 10,
|
|
timeoutMs: Number(env.TKMIND_SEARCH_TIMEOUT_MS ?? 8000) || 8000,
|
|
});
|
|
const injectionText = formatPrefetchedSearchContext(results, { query: q });
|
|
if (!injectionText) return null;
|
|
return {
|
|
provider: 'searxng',
|
|
query: q,
|
|
resultCount: Array.isArray(results) ? results.length : 0,
|
|
injectionText,
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function defaultPrefetchLiveSearch({
|
|
userMessage,
|
|
routing = null,
|
|
env = process.env,
|
|
} = {}) {
|
|
const selectedSkill = String(
|
|
userMessage?.metadata?.memindRun?.selectedChatSkill
|
|
?? userMessage?.metadata?.selectedChatSkill
|
|
?? '',
|
|
).trim();
|
|
const suggestedSkill = String(routing?.suggestedSkill ?? routing?.suggested_skill ?? '').trim();
|
|
const query = String(userMessage?.metadata?.displayText ?? '').trim();
|
|
return prefetchLiveSearchResults({
|
|
query,
|
|
selectedSkill,
|
|
suggestedSkill,
|
|
env,
|
|
});
|
|
}
|