Files
Hayden Bleasel 30cbcfec97 Migrate to Oxlint (#357)
* Migrate to oxlint / oxfmt

* Initial fixes

* Misc fixes

* Temporary fixes

* Update Ultracite

* Update .oxlintrc.json

* Update .oxlintrc.json

* Start running manual fixes

* Update .oxlintrc.json

* AI Fixes Part 2

* AI Fixes Part 3

* AI Fixes Part 4

* AI Fixes Part 5

* AI Fixes Part 6

* AI Fixes Part 7

* AI Fixes Part 8

* AI Fixes Part 9

* AI Fixes Part 10

* AI Fixes Part 11

* AI Fixes Part 12

* Fix tests

* Lint / test fixes

* Update chat.tsx

* Fix AI SDK React version

* Misc fixes

* Build fixes
2026-02-04 08:00:41 -08:00

212 lines
6.3 KiB
TypeScript

import type { ToolSet, UIMessageStreamWriter } from "ai";
import { tool } from "ai";
import { initAdvancedSearch } from "fumadocs-core/search/server";
import z from "zod";
import { i18n } from "@/lib/geistdocs/i18n";
import { getAllPages, getPageByHref } from "@/lib/geistdocs/source";
const createSearchServer = (lang: string) => {
const pages = getAllPages(lang);
return initAdvancedSearch({
indexes: pages.map((page) => ({
description: page.data.description,
id: page.url,
structuredData: page.data.structuredData,
title: page.data.title ?? "",
url: page.url,
})),
});
};
const log = (message: string) => {
console.log(`🤖 [Geistdocs] ${message}`);
};
const search_docs = (writer: UIMessageStreamWriter) =>
tool({
description: "Search through documentation content by query",
execute: async ({ query, lang }) => {
try {
log(`Creating search server for language: ${lang}`);
const searchServer = createSearchServer(lang);
log(`Searching docs for ${query}`);
const results = await searchServer.search(query);
log(`Found ${results.length} results`);
if (results.length === 0) {
return `No documentation found for query: "${query}"`;
}
log(`Processing ${results.length} results...`);
const promises = results.map(({ url }) => {
const segments = url.split("#").at(0)?.split("/") ?? [];
if (segments.length === 0) {
log(`🤖 [Geistdocs] No segments found for ${url}, skipping...`);
return null;
}
log(`Getting page for ${url}`);
const result = getPageByHref(url, { language: lang });
if (!result?.page) {
log(`No page found for ${url}`);
return null;
}
const { page } = result;
log(
`Found page for ${url}: ${page.data.title}, ${page.data.description}`
);
return {
content: JSON.stringify(page.data.structuredData.contents),
description: page.data.description,
slug: page.url,
title: page.data.title,
};
});
log(`Running ${promises.length} promises...`);
const promiseResults = await Promise.all(promises);
log(`${promiseResults.length} promises resolved.`);
// Collect results, then filter to ensure unique slugs
const formattedResultsRaw = promiseResults.filter(Boolean) as {
title: string;
description: string;
content: string;
slug: string;
}[];
log(`Formatted ${formattedResultsRaw.length} results.`);
// Ensure slugs are unique
const seenSlugs = new Set<string>();
const formattedResults = formattedResultsRaw.filter((doc) => {
if (seenSlugs.has(doc.slug)) {
return false;
}
seenSlugs.add(doc.slug);
return true;
});
log(`Filtered ${formattedResults.length} results.`);
const trimmedResults = formattedResults.slice(0, 8);
log(`Trimmed ${trimmedResults.length} results.`);
for (const [index, doc] of trimmedResults.entries()) {
log(`Writing doc: ${doc.title}, ${doc.slug}`);
writer.write({
sourceId: `doc-${index}-${doc.slug}`,
title: doc.title,
type: "source-url",
url: doc.slug,
});
}
const formattedResultsString = trimmedResults
.map(
(doc) =>
`**${doc.title}**\nURL: ${doc.slug}\n${
doc.description || ""
}\n\n${doc.content.slice(0, 1500)}${
doc.content.length > 1500 ? "..." : ""
}\n\n---\n`
)
.join("\n");
return `Found ${trimmedResults.length} documentation pages for "${query}":\n\n${formattedResultsString}`;
} catch (error) {
const message =
error instanceof Error ? error.message : "Unknown error";
return `Error processing results: ${message}`;
}
},
inputSchema: z.object({
lang: z
.string()
.describe("The two letter language code of the documentation to search")
.optional()
.default(i18n.defaultLanguage),
query: z.string().describe("Search query to find relevant documentation"),
}),
});
const get_doc_page = tool({
description:
'Get the full content of a specific documentation page or guide by slug. Use the exact URL path from search results (e.g., "/docs/vercel-blob/client-upload" or "/guides/how-to-build-ai-app")',
inputSchema: z.object({
lang: z
.string()
.describe("The two letter language code of the documentation to search")
.optional()
.default(i18n.defaultLanguage),
slug: z
.string()
.describe("The slug/path of the documentation page or guide to retrieve"),
}),
// biome-ignore lint/suspicious/useAwait: "tool calls must be async"
execute: async ({ slug, lang }) => {
log(`Getting all pages for language: ${lang}`);
const pages = getAllPages(lang);
log(`Getting doc page for ${slug} in language: ${lang}`);
const doc = pages.find((d) => d.url === slug || d.url.endsWith(slug));
if (!doc) {
return `Documentation page not found: "${slug}"`;
}
return `# ${doc.data.title}\n\n${
doc.data.description ? `${doc.data.description}\n\n` : ""
}${doc.data.structuredData.contents}`;
},
});
const list_docs = tool({
description: "Get a list of all available documentation pages",
inputSchema: z.object({
lang: z
.string()
.describe("The two letter language code of the documentation to search")
.optional()
.default(i18n.defaultLanguage),
}),
// biome-ignore lint/suspicious/useAwait: "tool calls must be async"
execute: async ({ lang }) => {
log(`Getting all pages for language: ${lang}`);
const pages = getAllPages(lang);
const docsList = pages
.map(
(doc) => `- **${doc.data.title}** (${doc.url}): ${doc.data.description}`
)
.join("\n");
return `Available Documentation Pages (${pages.length} total):\n\n${docsList}`;
},
});
export const createTools = (writer: UIMessageStreamWriter) =>
({
get_doc_page,
list_docs,
search_docs: search_docs(writer),
}) satisfies ToolSet;