|
import { authCondition } from "$lib/server/auth"; |
|
import { collections } from "$lib/server/database"; |
|
import { defaultModel } from "$lib/server/models"; |
|
import { searchWeb } from "$lib/server/websearch/searchWeb"; |
|
import type { Message } from "$lib/types/Message"; |
|
import { error } from "@sveltejs/kit"; |
|
import { ObjectId } from "mongodb"; |
|
import { z } from "zod"; |
|
import type { WebSearch } from "$lib/types/WebSearch"; |
|
import { generateQuery } from "$lib/server/websearch/generateQuery"; |
|
import { parseWeb } from "$lib/server/websearch/parseWeb"; |
|
import { summarizeWeb } from "$lib/server/websearch/summarizeWeb"; |
|
|
|
interface GenericObject { |
|
[key: string]: GenericObject | unknown; |
|
} |
|
|
|
function removeLinks(obj: GenericObject) { |
|
for (const prop in obj) { |
|
if (prop.endsWith("link")) delete obj[prop]; |
|
else if (typeof obj[prop] === "object") removeLinks(obj[prop] as GenericObject); |
|
} |
|
return obj; |
|
} |
|
export async function GET({ params, locals, url }) { |
|
const model = defaultModel; |
|
const convId = new ObjectId(params.id); |
|
const searchId = new ObjectId(); |
|
|
|
const conv = await collections.conversations.findOne({ |
|
_id: convId, |
|
...authCondition(locals), |
|
}); |
|
|
|
if (!conv) { |
|
throw error(404, "Conversation not found"); |
|
} |
|
|
|
const prompt = z.string().trim().min(1).parse(url.searchParams.get("prompt")); |
|
|
|
const messages = (() => { |
|
return [...conv.messages, { content: prompt, from: "user", id: crypto.randomUUID() }]; |
|
})() satisfies Message[]; |
|
|
|
const stream = new ReadableStream({ |
|
async start(controller) { |
|
const webSearch: WebSearch = { |
|
_id: searchId, |
|
convId: convId, |
|
prompt: prompt, |
|
searchQuery: "", |
|
knowledgeGraph: "", |
|
answerBox: "", |
|
results: [], |
|
summary: "", |
|
messages: [], |
|
createdAt: new Date(), |
|
updatedAt: new Date(), |
|
}; |
|
|
|
function appendUpdate(message: string, args?: string[], type?: "error" | "update") { |
|
webSearch.messages.push({ |
|
type: type ?? "update", |
|
message, |
|
args, |
|
}); |
|
controller.enqueue(JSON.stringify({ messages: webSearch.messages })); |
|
} |
|
|
|
try { |
|
appendUpdate("Generating search query"); |
|
webSearch.searchQuery = await generateQuery(messages, model); |
|
|
|
appendUpdate("Searching Google", [webSearch.searchQuery]); |
|
const results = await searchWeb(webSearch.searchQuery); |
|
|
|
let text = ""; |
|
webSearch.results = |
|
(results.organic_results && |
|
results.organic_results.map((el: { link: string }) => el.link)) ?? |
|
[]; |
|
|
|
if (results.answer_box) { |
|
|
|
webSearch.answerBox = JSON.stringify(removeLinks(results.answer_box)); |
|
text = webSearch.answerBox; |
|
appendUpdate("Found a Google answer box"); |
|
} else if (results.knowledge_graph) { |
|
|
|
webSearch.knowledgeGraph = JSON.stringify(removeLinks(results.knowledge_graph)); |
|
text = webSearch.knowledgeGraph; |
|
appendUpdate("Found a Google knowledge page"); |
|
} else if (webSearch.results.length > 0) { |
|
let tries = 0; |
|
|
|
while (!text && tries < 3) { |
|
const searchUrl = webSearch.results[tries]; |
|
appendUpdate("Browsing result", [JSON.stringify(searchUrl)]); |
|
try { |
|
text = await parseWeb(searchUrl); |
|
if (!text) throw new Error("text of the webpage is null"); |
|
} catch (e) { |
|
appendUpdate("Error parsing webpage", [], "error"); |
|
tries++; |
|
} |
|
} |
|
if (!text) throw new Error("No text found on the first 3 results"); |
|
} else { |
|
throw new Error("No results found for this search query"); |
|
} |
|
|
|
appendUpdate("Creating summary"); |
|
webSearch.summary = await summarizeWeb(text, webSearch.searchQuery, model); |
|
appendUpdate("Injecting summary", [JSON.stringify(webSearch.summary)]); |
|
} catch (searchError) { |
|
if (searchError instanceof Error) { |
|
webSearch.messages.push({ |
|
type: "error", |
|
message: "An error occurred with the web search", |
|
args: [JSON.stringify(searchError.message)], |
|
}); |
|
} |
|
} |
|
|
|
const res = await collections.webSearches.insertOne(webSearch); |
|
webSearch.messages.push({ |
|
type: "result", |
|
id: res.insertedId.toString(), |
|
}); |
|
controller.enqueue(JSON.stringify({ messages: webSearch.messages })); |
|
}, |
|
}); |
|
|
|
return new Response(stream, { headers: { "Content-Type": "application/json" } }); |
|
} |
|
|