From c63c53ae8d7e32024a61efbba98ca7914ae14251 Mon Sep 17 00:00:00 2001 From: Yoshiki Miura Date: Wed, 24 Apr 2024 15:34:22 +0900 Subject: [PATCH 1/2] Refactor action.tsx and researcher.tsx --- app/action.tsx | 3 +-- components/search-section.tsx | 44 +++++++++++++++++++++++++++++++++++ lib/agents/researcher.tsx | 40 ++++++++++--------------------- lib/types/index.ts | 11 +++++++++ 4 files changed, 68 insertions(+), 30 deletions(-) create mode 100644 components/search-section.tsx create mode 100644 lib/types/index.ts diff --git a/app/action.tsx b/app/action.tsx index ea19eba8..6b1260a3 100644 --- a/app/action.tsx +++ b/app/action.tsx @@ -42,8 +42,6 @@ async function submit(formData?: FormData, skip?: boolean) { } async function processEvents() { - uiStream.update() - let action: any = { object: { next: 'proceed' } } // If the user skips the task, we proceed to the search if (!skip) action = (await taskManager(messages)) ?? action @@ -70,6 +68,7 @@ async function submit(formData?: FormData, skip?: boolean) { let toolOutputs = [] let errorOccurred = false const streamText = createStreamableValue() + uiStream.update() // If useSpecificAPI is enabled, only function calls will be made // If not using a tool, this model generates the answer diff --git a/components/search-section.tsx b/components/search-section.tsx new file mode 100644 index 00000000..db961c44 --- /dev/null +++ b/components/search-section.tsx @@ -0,0 +1,44 @@ +'use client' + +import { SearchResults } from './search-results' +import { SearchSkeleton } from './search-skeleton' +import { SearchResultsImageSection } from './search-results-image' +import { Section } from './section' +import { ToolBadge } from './tool-badge' +import type { SearchResults as TypeSearchResults } from '@/lib/types' +import { StreamableValue, useStreamableValue } from 'ai/rsc' + +export type SearchSectionProps = { + result?: StreamableValue +} + +export function SearchSection({ result }: SearchSectionProps) { + const [data, error, pending] = useStreamableValue(result) + const results: TypeSearchResults = data ? JSON.parse(data) : undefined + return ( +
+ {!pending && data ? ( + <> +
+ {`${results.query}`} +
+ {results.images && results.images.length > 0 && ( +
+ +
+ )} +
+ +
+ + ) : ( +
+ +
+ )} +
+ ) +} diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 924782dd..1c317a7a 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -8,13 +8,11 @@ import { import { searchSchema } from '@/lib/schema/search' import { Section } from '@/components/section' import { OpenAI } from '@ai-sdk/openai' -import { ToolBadge } from '@/components/tool-badge' -import { SearchSkeleton } from '@/components/search-skeleton' -import { SearchResults } from '@/components/search-results' import { BotMessage } from '@/components/message' import Exa from 'exa-js' -import { SearchResultsImageSection } from '@/components/search-results-image' import { Card } from '@/components/ui/card' +import { SearchResults } from '../types' +import { SearchSection } from '@/components/search-section' export async function researcher( uiStream: ReturnType, @@ -38,6 +36,7 @@ export async function researcher( ) + let isFirstToolResponse = true const result = await experimental_streamText({ model: openai.chat(process.env.OPENAI_API_MODEL || 'gpt-4-turbo'), maxTokens: 2500, @@ -61,17 +60,14 @@ export async function researcher( max_results: number search_depth: 'basic' | 'advanced' }) => { - uiStream.update( -
- {`${query}`} -
- ) - - uiStream.append( -
- -
- ) + // If this is the first tool response, remove spinner + if (isFirstToolResponse) { + isFirstToolResponse = false + uiStream.update(null) + } + // Append the search section + const streamResults = createStreamableValue() + uiStream.append() // Tavily API requires a minimum of 5 characters in the query const filledQuery = @@ -97,19 +93,7 @@ export async function researcher( return searchResult } - uiStream.update( -
- -
- ) - uiStream.append( -
- -
- ) + streamResults.done(JSON.stringify(searchResult)) // Append the answer section if the specific model is not used if (!useSpecificModel) { diff --git a/lib/types/index.ts b/lib/types/index.ts new file mode 100644 index 00000000..43cb522a --- /dev/null +++ b/lib/types/index.ts @@ -0,0 +1,11 @@ +export type SearchResults = { + images: string[] + results: SearchResultItem[] + query: string +} + +export type SearchResultItem = { + title: string + url: string + content: string +} From 0db3e862a0aae249e45c85141cb6a1cf34a81b8a Mon Sep 17 00:00:00 2001 From: Yoshiki Miura Date: Wed, 24 Apr 2024 15:47:44 +0900 Subject: [PATCH 2/2] Refactor researcher function to append answer section only when necessary --- lib/agents/researcher.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/agents/researcher.tsx b/lib/agents/researcher.tsx index 1c317a7a..ca3b7824 100644 --- a/lib/agents/researcher.tsx +++ b/lib/agents/researcher.tsx @@ -95,11 +95,6 @@ export async function researcher( streamResults.done(JSON.stringify(searchResult)) - // Append the answer section if the specific model is not used - if (!useSpecificModel) { - uiStream.append(answerSection) - } - return searchResult } } @@ -126,6 +121,10 @@ export async function researcher( toolCalls.push(delta) break case 'tool-result': + // Append the answer section if the specific model is not used + if (!useSpecificModel && toolResponses.length === 0) { + uiStream.append(answerSection) + } toolResponses.push(delta) break case 'error':