feat: add ai chat example and update flags structure

This commit is contained in:
Aman Varshney
2025-03-31 22:52:21 +05:30
parent d6c4127bf5
commit a6ac5dc86c
32 changed files with 485 additions and 263 deletions

View File

@@ -1,17 +1,19 @@
import path from "node:path";
import fs from "fs-extra";
import { PKG_ROOT } from "../constants";
import type { ProjectFrontend, ProjectOrm } from "../types";
import type { ProjectBackend, ProjectFrontend, ProjectOrm } from "../types";
import { addPackageDependency } from "../utils/add-package-deps";
export async function setupExamples(
projectDir: string,
examples: string[],
orm: ProjectOrm,
auth: boolean,
backend: ProjectBackend,
frontend: ProjectFrontend[] = ["web"],
): Promise<void> {
console.log("EXAMPLEs:", examples);
const hasWebFrontend = frontend.includes("web");
const webAppExists = await fs.pathExists(path.join(projectDir, "apps/web"));
if (examples.includes("todo") && hasWebFrontend && webAppExists) {
@@ -19,6 +21,135 @@ export async function setupExamples(
} else {
await cleanupTodoFiles(projectDir, orm);
}
if (
examples.includes("ai") &&
backend === "hono" &&
hasWebFrontend &&
webAppExists
) {
await setupAIExample(projectDir);
}
}
async function setupAIExample(projectDir: string): Promise<void> {
const aiExampleDir = path.join(PKG_ROOT, "template/examples/ai");
if (await fs.pathExists(aiExampleDir)) {
await fs.copy(aiExampleDir, projectDir);
await updateHeaderWithAILink(projectDir);
const clientDir = path.join(projectDir, "apps/web");
addPackageDependency({
dependencies: ["ai"],
projectDir: clientDir,
});
const serverDir = path.join(projectDir, "apps/server");
addPackageDependency({
dependencies: ["ai", "@ai-sdk/google"],
projectDir: serverDir,
});
await updateServerIndexWithAIRoute(projectDir);
}
}
async function updateServerIndexWithAIRoute(projectDir: string): Promise<void> {
const serverIndexPath = path.join(projectDir, "apps/server/src/index.ts");
if (await fs.pathExists(serverIndexPath)) {
let indexContent = await fs.readFile(serverIndexPath, "utf8");
const isHono = indexContent.includes("hono");
if (isHono) {
const importSection = `import { streamText } from "ai";\nimport { google } from "@ai-sdk/google";\nimport { stream } from "hono/streaming";`;
const aiRouteHandler = `
app.post("/ai", async (c) => {
const body = await c.req.json();
const messages = body.messages || [];
const result = streamText({
model: google("gemini-2.0-flash-exp"),
messages,
});
c.header("X-Vercel-AI-Data-Stream", "v1");
c.header("Content-Type", "text/plain; charset=utf-8");
return stream(c, (stream) => stream.pipe(result.toDataStream()));
});`;
// Add the import section
if (indexContent.includes("import {")) {
const lastImportIndex = indexContent.lastIndexOf("import");
const endOfLastImport = indexContent.indexOf("\n", lastImportIndex);
indexContent = `${indexContent.substring(0, endOfLastImport + 1)}
${importSection}
${indexContent.substring(endOfLastImport + 1)}`;
} else {
indexContent = `${importSection}
${indexContent}`;
}
// Add the route handler
const trpcHandlerIndex =
indexContent.indexOf('app.use("/trpc"') ||
indexContent.indexOf("app.use(trpc(");
if (trpcHandlerIndex !== -1) {
indexContent = `${indexContent.substring(0, trpcHandlerIndex)}${aiRouteHandler}
${indexContent.substring(trpcHandlerIndex)}`;
} else {
// Add it near the end before export
const exportIndex = indexContent.indexOf("export default");
if (exportIndex !== -1) {
indexContent = `${indexContent.substring(0, exportIndex)}${aiRouteHandler}
${indexContent.substring(exportIndex)}`;
} else {
indexContent = `${indexContent}
${aiRouteHandler}`;
}
}
await fs.writeFile(serverIndexPath, indexContent);
}
}
}
async function updateHeaderWithAILink(projectDir: string): Promise<void> {
const headerPath = path.join(
projectDir,
"apps/web/src/components/header.tsx",
);
if (await fs.pathExists(headerPath)) {
let headerContent = await fs.readFile(headerPath, "utf8");
if (headerContent.includes('{ to: "/todos"')) {
headerContent = headerContent.replace(
/{ to: "\/todos", label: "Todos" },/,
`{ to: "/todos", label: "Todos" },\n { to: "/ai", label: "AI Chat" },`,
);
} else if (headerContent.includes('{ to: "/dashboard"')) {
headerContent = headerContent.replace(
/{ to: "\/dashboard", label: "Dashboard" },/,
`{ to: "/dashboard", label: "Dashboard" },\n { to: "/ai", label: "AI Chat" },`,
);
} else {
headerContent = headerContent.replace(
/const links = \[\s*{ to: "\/", label: "Home" },/,
`const links = [\n { to: "/", label: "Home" },\n { to: "/ai", label: "AI Chat" },`,
);
}
await fs.writeFile(headerPath, headerContent);
}
}
async function setupTodoExample(