TypeScript
async function handleWebSearchStep(
step: TDeepResearchPlanStep,
allMessages: TPromptMessage[],
allLearnings: TLearning[],
conversationSummary: string,
executionContext: TStepExecutionContext = {
depth: 0,
maxDepth: 2,
parentChain: [],
},
): Promise<void> {
if (executionContext.depth >= executionContext.maxDepth) return;
if (executionContext.parentChain.includes(step.stepId)) return;
const eventManager = getDeepResearchRequestContext().eventManager;
let isStepComplete = false;
let iterationCount = 0;
const maxIterations = 5;
let allProcessedResults: TLearning[] = [];
let serpQueriesEvent: SSEProgressEvent | null = null;
let stepConfidence = 0;
const enhancedContext = {
reasoning: executionContext.previousStepContext?.reasoning || "",
keyMatchingElements:
executionContext.previousStepContext?.keyMatchingElements || [],
scoredElements: executionContext.previousStepContext?.scoredElements || [],
};
while (!isStepComplete && iterationCount <= maxIterations) {
iterationCount++;
const searchActivities = [
`Searching online for ${step.title} information`,
`Looking up the latest details about ${step.title}`,
`Researching key aspects of ${step.title}`,
`Finding relevant sources about ${step.title}`,
`Gathering information on ${step.title} from trusted sources`,
];
const randomActivity =
searchActivities[Math.floor(Math.random() * searchActivities.length)];
try {
serpQueriesEvent = eventManager.createEvent(randomActivity, "GLOBE");
updateStepStatus(step.stepId, "IN_PROGRESS");
const relevantLearnings = getRelevantLearnings(step, allLearnings);
let researchContext = "";
try {
const { relevantInsights, relevantGaps, relevantContradictions } =
await getRelevantContextForStep(step);
if (relevantInsights.length > 0 || relevantGaps.length > 0) {
researchContext = formatContextForPrompt(
relevantInsights,
relevantGaps,
relevantContradictions,
);
for (const insight of relevantInsights) {
enhancedContext.scoredElements.push({
content: insight.content,
relevance: insight.confidence,
source: `insight-${insight.id}`,
type: "insight",
});
}
for (const gap of relevantGaps) {
enhancedContext.scoredElements.push({
content: gap.question,
relevance: gap.relevance,
source: `gap-${gap.id}`,
type: "knowledgeGap",
});
}
}
} catch (error) {
logger.error(error, "ERROR/DEEP_RESEARCH/RESEARCH_MEMORY_CONTEXT");
}
if (iterationCount > 1 && enhancedContext.scoredElements.length > 0) {
try {
const result =
await ResearchMemoryManager.scoreContextElementsForStep(
enhancedContext.scoredElements.map((e) => ({
content: e.content,
relevance: e.relevance,
source: e.source,
type: e.type,
})),
step,
);
enhancedContext.scoredElements = result.map((item) => ({
content: item.content,
relevance: item.score || 0.5,
source: item.metadata?.source || "",
type:
item.type === "knowledgeGap"
? "knowledgeGap"
: item.type === "contradiction"
? "contradiction"
: "insight",
}));
} catch (error) {
logger.error(error, "ERROR/DEEP_RESEARCH/SCORE_CONTEXT_ELEMENTS");
}
}
if (
step.doesRequirePreviousData &&
executionContext.previousStepContext
) {
const previousContextString = `
Previous research findings:
${executionContext.previousStepContext.reasoning || ""}
Key matching elements from previous research:
${(executionContext.previousStepContext.keyMatchingElements || []).join("\n")}
`;
researchContext = researchContext
? `${researchContext}\n\n${previousContextString}`
: previousContextString;
step.description = `${step.description}\n\n[Using context from previous step(s): ${executionContext.parentChain.join(",")}]`;
}
let searchResults: TGeneratedSerpQueries;
try {
if (researchContext) {
enhancedContext.reasoning = `${enhancedContext.reasoning}\n\nAdditional Context:\n${researchContext}`;
}
searchResults = await generateSerpQueries(
step,
conversationSummary,
relevantLearnings,
{
reasoning: enhancedContext.reasoning,
keyMatchingElements: enhancedContext.keyMatchingElements,
scoredElements: enhancedContext.scoredElements,
},
);
} catch (error) {
logger.error(error, "ERROR/DEEP_RESEARCH/SERP_QUERIES_GENERATION");
searchResults = {
serpResults: { research_pairs: [] },
webResults: [{ task: step.task, query: step.title, results: [] }],
};
}
let processedResults: TSettledResults<TLearning[][]>;
try {
processedResults = await settleAll(
searchResults.webResults.map((result) =>
processSearchResult(result, step, conversationSummary),
),
);
} catch (error) {
logger.error(error, "ERROR/DEEP_RESEARCH/PROCESS_SEARCH_RESULTS");
processedResults = { fulfilled: [], rejected: [] };
}
const newResults = processedResults.fulfilled
.flat()
.map((learning: TLearning) => ({ ...learning, stepId: step.stepId }));
allProcessedResults = [...allProcessedResults, ...newResults];
try {
stepConfidence = ResearchMemoryManager.calculateConfidence(
allProcessedResults,
iterationCount,
);
ResearchMemoryManager.updateStepConfidence(
step.stepId,
stepConfidence,
iterationCount,
);
if (stepConfidence > 0.6 && newResults.length > 0) {
const { insights, gaps } =
await ResearchMemoryManager.extractInsightsAndGaps(
step,
newResults,
);
for (const insight of insights) {
enhancedContext.scoredElements.push({
content: insight,
relevance: 0.8,
source: step.stepId,
type: "insight",
});
}
for (const gap of gaps) {
enhancedContext.scoredElements.push({
content: gap.question,
relevance: gap.relevance,
source: step.stepId,
type: "knowledgeGap",
});
}
const analysisResult = await performMetaAnalysis(step, newResults);
updatePlanWithMetaAnalysis(step.stepId, analysisResult);
try {
ResearchMemoryManager.deduplicateInsights(
getDeepResearchRequestContext().researchMemory,
);
} catch (error) {
logger.error(error, "ERROR/DEEP_RESEARCH/DEDUPLICATION");
}
}
} catch (error) {
logger.error(error, "ERROR/DEEP_RESEARCH/EXTRACT_INSIGHTS_AND_GAPS");
}
if (processedResults.fulfilled.length > 0) {
const learningsToSend = processedResults.fulfilled
.flat()
.map((learning, index) => ({
...learning,
learning: learning.learning.slice(0, 100),
id: index + 1,
stepId: step.stepId,
}));
serpQueriesEvent?.send([
{
type: "TEXT",
value: JSON.stringify({
__type: "DEEP_RESEARCH_LINK",
data: {
query: searchResults.webResults[0]?.query || step.title,
learnings: learningsToSend,
},
}),
},
]);
}
for (const result of searchResults.webResults) {
const formattedResult = {
task: result.task,
query: result.query,
results: (result.results || []).map((r) => ({
url: r.url || "",
title: r.title || "",
description: r.description || "",
content:
typeof r.content === "string"
? r.content
: JSON.stringify(r.content || ""),
})),
};
const sanitizedResult = sanitizeWebSearchContent(
JSON.stringify({ webResults: [formattedResult] }),
);
let currentStepDoneResponse;
try {
currentStepDoneResponse = await currentStepDone(
sanitizedResult,
step,
allMessages,
);
isStepComplete = currentStepDoneResponse.isRelevant;
} catch (error) {
logger.error(
error,
`ERROR/DEEP_RESEARCH/CHECK_STEP_DONE_${step.stepId}`,
);
currentStepDoneResponse = {
isRelevant: false,
reasoning: "Error evaluating results",
keyMatchingElements: [],
};
}
if (!isStepComplete) {
enhancedContext.reasoning = currentStepDoneResponse.reasoning;
enhancedContext.keyMatchingElements =
currentStepDoneResponse.keyMatchingElements || [];
enhancedContext.scoredElements.push({
content: currentStepDoneResponse.reasoning,
relevance: 0.9,
source: "current_iteration",
type: "reasoning",
});
for (const element of currentStepDoneResponse.keyMatchingElements ||
[]) {
enhancedContext.scoredElements.push({
content: element,
relevance: 0.8,
source: "current_iteration",
type: "insight",
});
}
executionContext.previousStepContext = {
reasoning: currentStepDoneResponse.reasoning,
keyMatchingElements:
currentStepDoneResponse.keyMatchingElements || [],
scoredElements: enhancedContext.scoredElements,
};
}
serpQueriesEvent?.send([
{
type: "TEXT",
value: JSON.stringify({
__type: "DEEP_RESEARCH_STEP_DONE",
data: {
reasoning: currentStepDoneResponse.reasoning,
keyMatchingElements:
currentStepDoneResponse.keyMatchingElements,
},
}),
},
]);
if (isStepComplete) break;
}
if (isStepComplete || iterationCount >= maxIterations) {
if (allProcessedResults.length > 0) {
const analysisResult = await performMetaAnalysis(
step,
allProcessedResults,
);
updatePlanWithMetaAnalysis(step.stepId, analysisResult);
}
}
} catch {
} finally {
serpQueriesEvent?.end();
}
}
if (allProcessedResults.length > 0) {
try {
const evaluation = await evaluateStepOutcomes(
step,
allProcessedResults,
isStepComplete,
);
setDeepResearchRequestContext({
...getDeepResearchRequestContext(),
stepEvaluations: {
...getDeepResearchRequestContext().stepEvaluations,
[step.stepId]: evaluation,
},
});
const shouldCreateDynamicStep =
!isStepComplete &&
evaluation.informationCompleteness < 0.7 &&
evaluation.informationQuality > 0.4 &&
evaluation.suggestedNextSteps.length > 0;
if (shouldCreateDynamicStep) {
let dynamicStepTask = "";
let dynamicStepTitle = "";
let requiresWebSearch = true;
if (evaluation.suggestedNextSteps.length > 0) {
const nextStep = evaluation.suggestedNextSteps[0];
dynamicStepTitle = nextStep.title;
dynamicStepTask = nextStep.rationale;
requiresWebSearch = nextStep.requiresWebSearch;
}
if (!dynamicStepTitle || !dynamicStepTask) {
const updatedPipeline = await updatedDeepResearchPipeline(
step,
allProcessedResults,
conversationSummary,
);
if (updatedPipeline?.shouldUpdate) {
dynamicStepTitle =
updatedPipeline.updatedPlan?.title || "Additional Research";
dynamicStepTask = updatedPipeline.updatedPlan?.task || "";
requiresWebSearch =
updatedPipeline.updatedPlan?.requiresWebSearch || true;
}
}
if (dynamicStepTitle && dynamicStepTask) {
const newStep: TDeepResearchPlanStep = {
stepId: `dynamic-${Date.now()}`,
title: dynamicStepTitle,
description: `Follow-up research for: ${step.title}`,
task: dynamicStepTask,
status: "COMPLETED",
requiresWebSearch,
doesRequirePreviousData: true,
parentStepId: step.stepId,
};
const childContext: TStepExecutionContext = {
depth: executionContext.depth + 1,
maxDepth: executionContext.maxDepth,
parentChain: [...executionContext.parentChain, step.stepId],
previousStepContext: executionContext.previousStepContext,
};
if (newStep.requiresWebSearch) {
await handleWebSearchStep(
newStep,
allMessages,
[...allLearnings, ...allProcessedResults],
conversationSummary,
childContext,
);
} else {
await handleLLMOnlyStep(
newStep,
allMessages,
[...allLearnings, ...allProcessedResults],
conversationSummary,
childContext,
);
}
updatePlanWithDynamicStep(newStep, step.stepId);
}
}
} catch (evaluationError) {
logger.error(evaluationError, "ERROR/DEEP_RESEARCH/EVALUATE_OUTCOMES");
}
}
if (allProcessedResults.length > 0) {
allLearnings.push(...allProcessedResults);
}
}