refactor: make requests purely client-side

This commit is contained in:
AnotiaWang
2025-02-11 22:28:58 +08:00
parent 066aafa9b2
commit 84f63abb3d
4 changed files with 43 additions and 66 deletions

View File

@ -1,7 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { parsePartialJson } from '@ai-sdk/ui-utils' import { generateFeedback } from '~/lib/feedback'
import { useChat } from '@ai-sdk/vue'
import { isObject } from '@vueuse/core'
export interface ResearchFeedbackResult { export interface ResearchFeedbackResult {
assistantQuestion: string assistantQuestion: string
@ -18,9 +16,8 @@
const feedback = ref<ResearchFeedbackResult[]>([]) const feedback = ref<ResearchFeedbackResult[]>([])
const { messages, input, error, handleSubmit, isLoading } = useChat({ const isLoading = ref(false)
api: '/api/generate-feedback', const error = ref('')
})
const isSubmitButtonDisabled = computed( const isSubmitButtonDisabled = computed(
() => () =>
@ -34,46 +31,13 @@
async function getFeedback(query: string, numQuestions = 3) { async function getFeedback(query: string, numQuestions = 3) {
clear() clear()
// Set input value. (This only makes sure that the library sends the request) isLoading.value = true
input.value = query try {
handleSubmit( for await (const f of generateFeedback({
{},
{
body: {
query, query,
numQuestions, numQuestions,
}, })) {
}, const questions = f.questions!.filter((s) => typeof s === 'string')
)
}
function clear() {
messages.value = []
input.value = ''
error.value = undefined
feedback.value = []
}
watch(messages, (m) => {
const assistantMessage = m[m.length - 1]
if (assistantMessage?.role !== 'assistant') {
return {
value: undefined,
state: 'undefined-input',
}
}
const content = removeJsonMarkdown(assistantMessage.content)
// Write the questions into modelValue
const parseResult = parsePartialJson(content)
if (parseResult.state === 'repaired-parse' || parseResult.state === 'successful-parse') {
if (!isObject(parseResult.value) || Array.isArray(parseResult.value)) {
return (feedback.value = [])
}
const unsafeQuestions = parseResult.value.questions
if (!unsafeQuestions || !Array.isArray(unsafeQuestions)) return (feedback.value = [])
const questions = unsafeQuestions.filter((s) => typeof s === 'string')
// Incrementally update modelValue // Incrementally update modelValue
for (let i = 0; i < questions.length; i += 1) { for (let i = 0; i < questions.length; i += 1) {
if (feedback.value[i]) { if (feedback.value[i]) {
@ -85,16 +49,19 @@
}) })
} }
} }
} else {
feedback.value = []
} }
}) } catch (e: any) {
console.error('Error getting feedback:', e)
error.value = e.message
} finally {
isLoading.value = false
}
}
watch(error, (e) => { function clear() {
if (e) { feedback.value = []
console.error(`ResearchFeedback error,`, e) error.value = ''
} }
})
defineExpose({ defineExpose({
getFeedback, getFeedback,
@ -111,6 +78,7 @@
</template> </template>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<p v-if="error" class="text-red-500">{{ error }}</p>
<div v-if="!feedback.length && !error">Waiting for model feedback...</div> <div v-if="!feedback.length && !error">Waiting for model feedback...</div>
<template v-else> <template v-else>
<div v-if="error" class="text-red-500">{{ error }}</div> <div v-if="error" class="text-red-500">{{ error }}</div>

View File

@ -19,7 +19,7 @@ export interface WriteFinalReportParams {
prompt: string; prompt: string;
learnings: string[]; learnings: string[];
} }
// useRuntimeConfig()
// Used for streaming response // Used for streaming response
export type SearchQuery = z.infer<typeof searchQueriesTypeSchema>['queries'][0]; export type SearchQuery = z.infer<typeof searchQueriesTypeSchema>['queries'][0];
export type PartialSearchQuery = DeepPartial<SearchQuery>; export type PartialSearchQuery = DeepPartial<SearchQuery>;

View File

@ -5,6 +5,8 @@ import { zodToJsonSchema } from 'zod-to-json-schema'
import { o3MiniModel } from './ai/providers'; import { o3MiniModel } from './ai/providers';
import { systemPrompt } from './prompt'; import { systemPrompt } from './prompt';
type PartialFeedback = DeepPartial<z.infer<typeof feedbackTypeSchema>>
export const feedbackTypeSchema = z.object({ export const feedbackTypeSchema = z.object({
questions: z.array(z.string()) questions: z.array(z.string())
}) })
@ -28,10 +30,16 @@ export function generateFeedback({
`Given the following query from the user, ask some follow up questions to clarify the research direction. Return a maximum of ${numQuestions} questions, but feel free to return less if the original query is clear: <query>${query}</query>`, `Given the following query from the user, ask some follow up questions to clarify the research direction. Return a maximum of ${numQuestions} questions, but feel free to return less if the original query is clear: <query>${query}</query>`,
`You MUST respond in JSON with the following schema: ${jsonSchema}`, `You MUST respond in JSON with the following schema: ${jsonSchema}`,
].join('\n\n'); ].join('\n\n');
return streamText({
const stream = streamText({
model: o3MiniModel, model: o3MiniModel,
system: systemPrompt(), system: systemPrompt(),
prompt, prompt,
}); });
// return userFeedback.object.questions.slice(0, numQuestions);
return parseStreamingJson(
stream.textStream,
feedbackTypeSchema,
(value: PartialFeedback) => !!value.questions && value.questions.length > 0
)
} }

View File

@ -1,3 +1,4 @@
// This file is currently unused
import { generateFeedback } from "~/lib/feedback"; import { generateFeedback } from "~/lib/feedback";
export default defineEventHandler(async event => { export default defineEventHandler(async event => {