chore: cleanup unused ResearchStep params

This commit is contained in:
AnotiaWang
2025-02-11 19:26:45 +08:00
parent 2c4eaed03c
commit e971a61bd3
3 changed files with 57 additions and 65 deletions

View File

@ -3,13 +3,11 @@
import type { TreeNode } from './Tree.vue' import type { TreeNode } from './Tree.vue'
const tree = ref<TreeNode>({ const tree = ref<TreeNode>({
id: 'root', id: '0',
label: 'Start', label: 'Start',
children: [], children: [],
depth: 0,
breadth: 0,
}) })
const hoveredNode = ref<TreeNode | null>(null) const selectedNode = ref<TreeNode | null>(null)
const searchResults = ref<Record<string, PartialSearchResult>>({}) const searchResults = ref<Record<string, PartialSearchResult>>({})
const modelValue = computed(() => Object.values(searchResults.value)) const modelValue = computed(() => Object.values(searchResults.value))
@ -32,9 +30,6 @@
if (!node) { if (!node) {
console.error('Creating new node', { console.error('Creating new node', {
nodeId, nodeId,
depth: step.depth,
breadth: step.breadth,
index: step.nodeIndex,
}) })
// 创建新节点 // 创建新节点
node = { node = {
@ -43,12 +38,10 @@
researchGoal: 'Generating research goal...', researchGoal: 'Generating research goal...',
learnings: [], learnings: [],
children: [], children: [],
depth: step.depth,
breadth: step.breadth,
index: step.nodeIndex,
} }
const parentNodeId = getParentNodeId(nodeId)
// 如果是根节点的直接子节点 // 如果是根节点的直接子节点
if (step.depth === 1) { if (parentNodeId === '0') {
tree.value.children.push(node) tree.value.children.push(node)
} else { } else {
// 找到父节点并添加 // 找到父节点并添加
@ -81,6 +74,7 @@
case 'search_complete': { case 'search_complete': {
if (node) { if (node) {
node.visitedUrls = step.urls
// node.label = `Found ${step.urls.length} results for: ${node.query}` // node.label = `Found ${step.urls.length} results for: ${node.query}`
} }
break break
@ -90,7 +84,6 @@
if (node) { if (node) {
node.learnings = step.result.learnings || [] node.learnings = step.result.learnings || []
node.followUpQuestions = step.result.followUpQuestions || [] node.followUpQuestions = step.result.followUpQuestions || []
// node.label = `Processing results: ${node.query}`
} }
break break
} }
@ -98,7 +91,6 @@
case 'processed_search_result': { case 'processed_search_result': {
if (node) { if (node) {
node.learnings = step.result.learnings node.learnings = step.result.learnings
// node.label = `Completed: ${node.query}`
searchResults.value[nodeId] = step.result searchResults.value[nodeId] = step.result
} }
break break
@ -144,9 +136,7 @@
) { ) {
console.log('startResearch', query, depth, breadth, feedback) console.log('startResearch', query, depth, breadth, feedback)
tree.value.children = [] tree.value.children = []
tree.value.depth = depth selectedNode.value = null
tree.value.breadth = breadth
hoveredNode.value = null
searchResults.value = {} searchResults.value = {}
try { try {
const combinedQuery = ` const combinedQuery = `
@ -178,18 +168,29 @@ ${feedback.map((qa) => `Q: ${qa.assistantQuestion}\nA: ${qa.userAnswer}`).join('
</template> </template>
<div class="flex flex-col"> <div class="flex flex-col">
<div class="overflow-y-auto"> <div class="overflow-y-auto">
<Tree :node="tree" @select="hoveredNode = $event" /> <Tree :node="tree" :selected-node="selectedNode" @select="selectedNode = $event" />
</div> </div>
<div v-if="hoveredNode" class="p-4"> <div v-if="selectedNode" class="p-4">
<h2 class="text-xl font-bold">{{ hoveredNode.label }}</h2> <h2 class="text-xl font-bold">{{ selectedNode.label }}</h2>
<!-- Root node has no additional information -->
<p v-if="selectedNode.id === '0'"> This is the beginning of your deep research journey! </p>
<template v-else>
<h3 class="text-lg font-semibold mt-2">Research Goal:</h3> <h3 class="text-lg font-semibold mt-2">Research Goal:</h3>
<p>{{ hoveredNode.researchGoal }}</p> <p>{{ selectedNode.researchGoal }}</p>
<div v-if="hoveredNode.learnings">
<h3 class="text-lg font-semibold mt-2">Learnings:</h3> <h3 class="text-lg font-semibold mt-2">Visited URLs:</h3>
<ul> <ul class="list-disc list-inside">
<li v-for="(learning, index) in hoveredNode.learnings" :key="index">{{ learning }}</li> <li v-for="(url, index) in selectedNode.visitedUrls" :key="index">
<ULink :href="url" target="_blank">{{ url }}</ULink>
</li>
</ul> </ul>
</div>
<h3 class="text-lg font-semibold mt-2">Learnings:</h3>
<ul class="list-disc list-inside">
<li v-for="(learning, index) in selectedNode.learnings" :key="index">{{ learning }}</li>
</ul>
</template>
</div> </div>
</div> </div>
</UCard> </UCard>

View File

@ -10,18 +10,14 @@
researchGoal?: string researchGoal?: string
learnings?: string[] learnings?: string[]
followUpQuestions?: string[] followUpQuestions?: string[]
visitedUrls?: string[]
status?: TreeNodeStatus status?: TreeNodeStatus
children: TreeNode[] children: TreeNode[]
/** Current depth of the node */
depth: number
/** Maximum breadth at the current depth */
breadth: number
/** Index of the node among its siblings */
index?: number
} }
const props = defineProps<{ const props = defineProps<{
node: TreeNode node: TreeNode
selectedNode: TreeNode | null
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
@ -64,12 +60,19 @@
<template> <template>
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">
<UIcon name="i-lucide-circle-dot" /> <UIcon name="i-lucide-circle-dot" />
<UButton :class="icon.pulse && 'animate-pulse'" :icon="icon.name" size="sm" color="info" @click="emit('select', node)">{{ <UButton
node.label :class="icon.pulse && 'animate-pulse'"
}}</UButton> :icon="icon.name"
size="sm"
:color="selectedNode?.id === node.id ? 'primary' : 'info'"
:variant="selectedNode?.id === node.id ? 'soft' : 'outline'"
@click="emit('select', node)"
>
{{ node.label }}
</UButton>
<ol v-if="node.children.length > 0" class="space-y-2"> <ol v-if="node.children.length > 0" class="space-y-2">
<li v-for="node in node.children" :key="node.id"> <li v-for="node in node.children" :key="node.id">
<Tree class="ml-2" :node="node" @select="emit('select', $event)" /> <Tree class="ml-2" :node="node" :selected-node @select="emit('select', $event)" />
</li> </li>
</ol> </ol>
</div> </div>

View File

@ -16,13 +16,13 @@ export type SearchResult = z.infer<typeof searchResultTypeSchema>;
export type PartialSearchResult = DeepPartial<SearchResult>; export type PartialSearchResult = DeepPartial<SearchResult>;
export type ResearchStep = export type ResearchStep =
| { type: 'generating_query'; result: PartialSearchQuery; depth: number; breadth: number; nodeIndex: number; nodeId: string } | { type: 'generating_query'; result: PartialSearchQuery; nodeId: string }
| { type: 'generated_query'; query: string; result: PartialSearchQuery; depth: number; breadth: number; nodeIndex: number; nodeId: string } | { type: 'generated_query'; query: string; result: PartialSearchQuery; nodeId: string }
| { type: 'searching'; query: string; depth: number; breadth: number; nodeIndex: number; nodeId: string } | { type: 'searching'; query: string; nodeId: string }
| { type: 'search_complete'; query: string; urls: string[]; depth: number; breadth: number; nodeIndex: number; nodeId: string } | { type: 'search_complete'; urls: string[]; nodeId: string }
| { type: 'processing_serach_result'; query: string; result: PartialSearchResult; depth: number; breadth: number; nodeIndex: number; nodeId: string } | { type: 'processing_serach_result'; query: string; result: PartialSearchResult; nodeId: string }
| { type: 'processed_search_result'; query: string; result: SearchResult; depth: number; breadth: number; nodeIndex: number; nodeId: string } | { type: 'processed_search_result'; query: string; result: SearchResult; nodeId: string }
| { type: 'error'; message: string; depth: number; nodeId: string } | { type: 'error'; message: string; nodeId: string }
| { type: 'complete' }; | { type: 'complete' };
// increase this if you have higher API rate limits // increase this if you have higher API rate limits
@ -104,7 +104,6 @@ function processSearchResult({
numFollowUpQuestions = 3, numFollowUpQuestions = 3,
}: { }: {
query: string; query: string;
// result: SearchResponse;
result: TavilySearchResponse result: TavilySearchResponse
numLearnings?: number; numLearnings?: number;
numFollowUpQuestions?: number; numFollowUpQuestions?: number;
@ -214,9 +213,6 @@ export async function deepResearch({
onProgress({ onProgress({
type: 'generating_query', type: 'generating_query',
result: searchQueries[i], result: searchQueries[i],
depth: currentDepth,
breadth,
nodeIndex: i,
nodeId: childNodeId(nodeId, i) nodeId: childNodeId(nodeId, i)
}); });
} }
@ -229,24 +225,18 @@ export async function deepResearch({
type: 'generated_query', type: 'generated_query',
query, query,
result: searchQueries[i], result: searchQueries[i],
depth: currentDepth,
breadth,
nodeIndex: i,
nodeId: childNodeId(nodeId, i) nodeId: childNodeId(nodeId, i)
}); });
} }
await Promise.all( await Promise.all(
searchQueries.map((searchQuery, nodeIndex) => searchQueries.map((searchQuery, i) =>
limit(async () => { limit(async () => {
if (!searchQuery?.query) return if (!searchQuery?.query) return
onProgress({ onProgress({
type: 'searching', type: 'searching',
query: searchQuery.query, query: searchQuery.query,
depth: currentDepth, nodeId: childNodeId(nodeId, i)
breadth,
nodeIndex,
nodeId: childNodeId(nodeId, nodeIndex)
}) })
try { try {
// const result = await firecrawl.search(searchQuery.query, { // const result = await firecrawl.search(searchQuery.query, {
@ -261,6 +251,11 @@ export async function deepResearch({
// Collect URLs from this search // Collect URLs from this search
const newUrls = compact(result.results.map(item => item.url)); const newUrls = compact(result.results.map(item => item.url));
onProgress({
type: 'search_complete',
urls: newUrls,
nodeId: childNodeId(nodeId, i),
})
// Breadth for the next search is half of the current breadth // Breadth for the next search is half of the current breadth
const nextBreadth = Math.ceil(breadth / 2); const nextBreadth = Math.ceil(breadth / 2);
@ -280,11 +275,8 @@ export async function deepResearch({
onProgress({ onProgress({
type: 'processing_serach_result', type: 'processing_serach_result',
result: parsedLearnings, result: parsedLearnings,
depth: currentDepth,
breadth: breadth,
query: searchQuery.query, query: searchQuery.query,
nodeIndex: nodeIndex, nodeId: childNodeId(nodeId, i)
nodeId: childNodeId(nodeId, nodeIndex)
}); });
} }
console.log(`Processed search result for ${searchQuery.query}`, searchResult); console.log(`Processed search result for ${searchQuery.query}`, searchResult);
@ -298,11 +290,8 @@ export async function deepResearch({
learnings: allLearnings, learnings: allLearnings,
followUpQuestions: searchResult.followUpQuestions ?? [], followUpQuestions: searchResult.followUpQuestions ?? [],
}, },
depth: currentDepth,
breadth,
query: searchQuery.query, query: searchQuery.query,
nodeIndex: nodeIndex, nodeId: childNodeId(nodeId, i)
nodeId: childNodeId(nodeId, nodeIndex)
}) })
if (nextDepth < maxDepth && searchResult.followUpQuestions?.length) { if (nextDepth < maxDepth && searchResult.followUpQuestions?.length) {
@ -323,7 +312,7 @@ export async function deepResearch({
visitedUrls: allUrls, visitedUrls: allUrls,
onProgress, onProgress,
currentDepth: nextDepth, currentDepth: nextDepth,
nodeId: childNodeId(nodeId, nodeIndex), nodeId: childNodeId(nodeId, i),
}); });
} else { } else {
return { return {
@ -342,7 +331,6 @@ export async function deepResearch({
onProgress({ onProgress({
type: 'error', type: 'error',
message: error?.message ?? 'Something went wrong', message: error?.message ?? 'Something went wrong',
depth: currentDepth,
nodeId, nodeId,
}) })
} }