diff --git a/src/lib/agents/search/researcher/actions/index.ts b/src/lib/agents/search/researcher/actions/index.ts index 90559b9..8864c08 100644 --- a/src/lib/agents/search/researcher/actions/index.ts +++ b/src/lib/agents/search/researcher/actions/index.ts @@ -3,6 +3,7 @@ import doneAction from './done'; import planAction from './plan'; import ActionRegistry from './registry'; import scrapeURLAction from './scrapeURL'; +import socialSearchAction from './socialSearch'; import uploadsSearchAction from './uploadsSearch'; import webSearchAction from './webSearch'; @@ -12,5 +13,6 @@ ActionRegistry.register(planAction); ActionRegistry.register(scrapeURLAction); ActionRegistry.register(uploadsSearchAction); ActionRegistry.register(academicSearchAction); +ActionRegistry.register(socialSearchAction); export { ActionRegistry }; diff --git a/src/lib/agents/search/researcher/actions/socialSearch.ts b/src/lib/agents/search/researcher/actions/socialSearch.ts index e69de29..16468ab 100644 --- a/src/lib/agents/search/researcher/actions/socialSearch.ts +++ b/src/lib/agents/search/researcher/actions/socialSearch.ts @@ -0,0 +1,129 @@ +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 = { + 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;