Files
Perplexica/src/lib/agents/search/researcher/actions/socialSearch.ts
2025-12-18 13:56:39 +05:30

130 lines
4.1 KiB
TypeScript

import z from 'zod';
import { ResearchAction } from '../../types';
import { Chunk, SearchResultsResearchBlock } from '@/lib/types';
import { searchSearxng } from '@/lib/searxng';
const schema = z.object({
queries: z.array(z.string()).describe('List of social search queries'),
});
const socialSearchDescription = `
Use this tool to perform social media searches for relevant posts, discussions, and trends related to the user's query. Provide a list of concise search queries that will help gather comprehensive social media information on the topic at hand.
You can provide up to 3 queries at a time. Make sure the queries are specific and relevant to the user's needs.
For example, if the user is interested in public opinion on electric vehicles, your queries could be:
1. "Electric vehicles public opinion 2024"
2. "Social media discussions on EV adoption"
3. "Trends in electric vehicle usage"
If this tool is present and no other tools are more relevant, you MUST use this tool to get the needed social media information.
`;
const socialSearchAction: ResearchAction<typeof schema> = {
name: 'social_search',
schema: schema,
getDescription: () => socialSearchDescription,
getToolDescription: () =>
"Use this tool to perform social media searches for relevant posts, discussions, and trends related to the user's query. Provide a list of concise search queries that will help gather comprehensive social media information on the topic at hand.",
enabled: (config) =>
config.sources.includes('discussions') &&
config.classification.classification.skipSearch === false &&
config.classification.classification.discussionSearch === true,
execute: async (input, additionalConfig) => {
input.queries = input.queries.slice(0, 3);
const researchBlock = additionalConfig.session.getBlock(
additionalConfig.researchBlockId,
);
if (researchBlock && researchBlock.type === 'research') {
researchBlock.data.subSteps.push({
type: 'searching',
id: crypto.randomUUID(),
searching: input.queries,
});
additionalConfig.session.updateBlock(additionalConfig.researchBlockId, [
{
op: 'replace',
path: '/data/subSteps',
value: researchBlock.data.subSteps,
},
]);
}
const searchResultsBlockId = crypto.randomUUID();
let searchResultsEmitted = false;
let results: Chunk[] = [];
const search = async (q: string) => {
const res = await searchSearxng(q, {
engines: ['reddit'],
});
const resultChunks: Chunk[] = res.results.map((r) => ({
content: r.content || r.title,
metadata: {
title: r.title,
url: r.url,
},
}));
results.push(...resultChunks);
if (
!searchResultsEmitted &&
researchBlock &&
researchBlock.type === 'research'
) {
searchResultsEmitted = true;
researchBlock.data.subSteps.push({
id: searchResultsBlockId,
type: 'search_results',
reading: resultChunks,
});
additionalConfig.session.updateBlock(additionalConfig.researchBlockId, [
{
op: 'replace',
path: '/data/subSteps',
value: researchBlock.data.subSteps,
},
]);
} else if (
searchResultsEmitted &&
researchBlock &&
researchBlock.type === 'research'
) {
const subStepIndex = researchBlock.data.subSteps.findIndex(
(step) => step.id === searchResultsBlockId,
);
const subStep = researchBlock.data.subSteps[
subStepIndex
] as SearchResultsResearchBlock;
subStep.reading.push(...resultChunks);
additionalConfig.session.updateBlock(additionalConfig.researchBlockId, [
{
op: 'replace',
path: '/data/subSteps',
value: researchBlock.data.subSteps,
},
]);
}
};
await Promise.all(input.queries.map(search));
return {
type: 'search_results',
results,
};
},
};
export default socialSearchAction;