diff --git a/.changeset/weak-bobcats-trade.md b/.changeset/weak-bobcats-trade.md new file mode 100644 index 000000000..82cf959de --- /dev/null +++ b/.changeset/weak-bobcats-trade.md @@ -0,0 +1,5 @@ +--- +"create-llama": patch +--- + +Display PDF files in source nodes diff --git a/templates/types/streaming/express/index.ts b/templates/types/streaming/express/index.ts index 150dbf598..5940c09d0 100644 --- a/templates/types/streaming/express/index.ts +++ b/templates/types/streaming/express/index.ts @@ -31,6 +31,7 @@ if (isDevelopment) { console.warn("Production CORS origin not set, defaulting to no CORS."); } +app.use("/api/data", express.static("data")); app.use(express.text()); app.get("/", (req: Request, res: Response) => { diff --git a/templates/types/streaming/fastapi/main.py b/templates/types/streaming/fastapi/main.py index 1a4e58beb..c053fd6d2 100644 --- a/templates/types/streaming/fastapi/main.py +++ b/templates/types/streaming/fastapi/main.py @@ -11,6 +11,7 @@ from app.api.routers.chat import chat_router from app.settings import init_settings from app.observability import init_observability +from fastapi.staticfiles import StaticFiles app = FastAPI() @@ -20,7 +21,6 @@ environment = os.getenv("ENVIRONMENT", "dev") # Default to 'development' if not set - if environment == "dev": logger = logging.getLogger("uvicorn") logger.warning("Running in development mode - allowing CORS for all origins") @@ -38,6 +38,8 @@ async def redirect_to_docs(): return RedirectResponse(url="/docs") +if os.path.exists("data"): + app.mount("/api/data", StaticFiles(directory="data"), name="static") app.include_router(chat_router, prefix="/api/chat") diff --git a/templates/types/streaming/nextjs/app/api/data/[path]/route.ts b/templates/types/streaming/nextjs/app/api/data/[path]/route.ts new file mode 100644 index 000000000..8e5fb9271 --- /dev/null +++ b/templates/types/streaming/nextjs/app/api/data/[path]/route.ts @@ -0,0 +1,38 @@ +import { readFile } from "fs/promises"; +import { NextRequest, NextResponse } from "next/server"; +import path from "path"; + +/** + * This API is to get file data from ./data folder + * It receives path slug and response file data like serve static file + */ +export async function GET( + _request: NextRequest, + { params }: { params: { path: string } }, +) { + const slug = params.path; + + if (!slug) { + return NextResponse.json({ detail: "Missing file slug" }, { status: 400 }); + } + + if (slug.includes("..") || path.isAbsolute(slug)) { + return NextResponse.json({ detail: "Invalid file path" }, { status: 400 }); + } + + try { + const filePath = path.join(process.cwd(), "data", slug); + const blob = await readFile(filePath); + + return new NextResponse(blob, { + status: 200, + statusText: "OK", + headers: { + "Content-Length": blob.byteLength.toString(), + }, + }); + } catch (error) { + console.error(error); + return NextResponse.json({ detail: "File not found" }, { status: 404 }); + } +} diff --git a/templates/types/streaming/nextjs/app/components/ui/chat/chat-sources.tsx b/templates/types/streaming/nextjs/app/components/ui/chat/chat-sources.tsx index de8c3edb0..a492eebc5 100644 --- a/templates/types/streaming/nextjs/app/components/ui/chat/chat-sources.tsx +++ b/templates/types/streaming/nextjs/app/components/ui/chat/chat-sources.tsx @@ -1,20 +1,78 @@ -import { ArrowUpRightSquare, Check, Copy } from "lucide-react"; +import { Check, Copy } from "lucide-react"; import { useMemo } from "react"; import { Button } from "../button"; import { HoverCard, HoverCardContent, HoverCardTrigger } from "../hover-card"; +import { getStaticFileDataUrl } from "../lib/url"; import { SourceData, SourceNode } from "./index"; import { useCopyToClipboard } from "./use-copy-to-clipboard"; +import PdfDialog from "./widgets/PdfDialog"; -const SCORE_THRESHOLD = 0.5; +const SCORE_THRESHOLD = 0.3; + +function SourceNumberButton({ index }: { index: number }) { + return ( +