Skip to content

Commit

Permalink
feat: integrate with llama cloud
Browse files Browse the repository at this point in the history
  • Loading branch information
himself65 committed Oct 27, 2024
1 parent 00a7459 commit fedc766
Show file tree
Hide file tree
Showing 35 changed files with 1,643 additions and 472 deletions.
15 changes: 13 additions & 2 deletions apps/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,43 @@
},
"dependencies": {
"@icons-pack/react-simple-icons": "^10.1.0",
"@llamaindex/cloud": "workspace:*",
"@llamaindex/core": "workspace:*",
"@llamaindex/openai": "workspace:*",
"@mdx-js/mdx": "^3.1.0",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.3",
"ai": "^3.3.21",
"class-variance-authority": "^0.7.0",
"clsx": "2.1.1",
"foxact": "^0.2.39",
"framer-motion": "^11.11.10",
"fumadocs-core": "14.0.2",
"fumadocs-docgen": "^1.3.0",
"fumadocs-mdx": "11.0.0",
"fumadocs-openapi": "^5.5.3",
"fumadocs-twoslash": "^2.0.0",
"fumadocs-ui": "14.0.2",
"hast-util-to-jsx-runtime": "^2.3.2",
"lucide-react": "^0.436.0",
"next": "15.0.0",
"next": "15.0.1",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-text-transition": "^3.1.0",
"react-use-measure": "^2.1.1",
"rehype-katex": "^7.0.1",
"remark-math": "^6.0.0",
"rimraf": "^6.0.1",
"shiki": "^1.22.0",
"shiki-magic-move": "^0.5.0",
"swr": "^2.2.5",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/mdx": "^2.0.13",
Expand Down
112 changes: 111 additions & 1 deletion apps/next/source.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,117 @@
import { PipelinesService } from "@llamaindex/cloud/api";
import { rehypeCodeDefaultOptions } from "fumadocs-core/mdx-plugins";
import { fileGenerator, remarkDocGen, remarkInstall } from "fumadocs-docgen";
import { defineConfig, defineDocs } from "fumadocs-mdx/config";
import { transformerTwoslash } from "fumadocs-twoslash";
import { relative } from "node:path";
import { fileURLToPath } from "node:url";
import rehypeKatex from "rehype-katex";
import remarkMath from "remark-math";

const baseDir = fileURLToPath(new URL("../src/content", import.meta.url));

export const { docs, meta } = defineDocs({
dir: "./src/content/docs",
});

export default defineConfig();
export default defineConfig({
lastModifiedTime: "git",
mdxOptions: {
rehypeCodeOptions: {
inline: "tailing-curly-colon",
themes: {
light: "catppuccin-latte",
dark: "catppuccin-mocha",
},
transformers: [
...(rehypeCodeDefaultOptions.transformers ?? []),
transformerTwoslash(),
{
name: "transformers:remove-notation-escape",
code(hast) {
for (const line of hast.children) {
if (line.type !== "element") continue;

const lastSpan = line.children.findLast(
(v) => v.type === "element",
);

const head = lastSpan?.children[0];
if (head?.type !== "text") return;

head.value = head.value.replace(/\[\\!code/g, "[!code");
}
},
},
],
},
remarkPlugins: [
remarkMath,
[remarkInstall, { persist: { id: "package-manager" } }],
[remarkDocGen, { generators: [fileGenerator()] }],
() => {
return (_, file, next) => {
const metadata = file.data.frontmatter as Record<string, unknown>;
const title = metadata.title as string;
const description = metadata.description as string;
let content: string;
if (file.value instanceof Uint8Array) {
content = file.value.toString();
} else {
content = file.value;
}
if (file.path.includes("content/docs/cloud/api")) {
// skip cloud api docs
return next();
}
// eslint-disable-next-line turbo/no-undeclared-env-vars
if (process.env.NODE_ENV === "development") {
// skip development
return next();
}
if (!title || !description) {
throw new Error(`Missing title or description in ${file.path}`);
}
const id = relative(baseDir, file.path);

if (
// eslint-disable-next-line turbo/no-undeclared-env-vars
process.env.LLAMA_CLOUD_UPSERT_PIPELINE_DOCUMENTS === "true" &&
// eslint-disable-next-line turbo/no-undeclared-env-vars
process.env.LLAMA_CLOUD_PIPELINE_ID !== undefined
) {
PipelinesService.upsertBatchPipelineDocumentsApiV1PipelinesPipelineIdDocumentsPut(
{
baseUrl: "https://api.cloud.llamaindex.ai/",
body: [
{
metadata: {
title,
description,
documentUrl: id,
},
text: content,
id,
},
],
path: {
// eslint-disable-next-line turbo/no-undeclared-env-vars
pipeline_id: process.env.LLAMA_CLOUD_PIPELINE_ID,
},
throwOnError: true,
headers: {
// eslint-disable-next-line turbo/no-undeclared-env-vars
Authorization: `Bearer ${process.env.LLAMA_CLOUD_API_KEY}`,
},
},
).catch((error) => {
console.error(error);
});
}
return next();
};
},
],
rehypePlugins: (v) => [rehypeKatex, ...v],
},
});
107 changes: 107 additions & 0 deletions apps/next/src/actions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { ClientMDXContent } from "@/components/mdx";
import { BotMessage } from "@/components/message";
import { Skeleton } from "@/components/ui/skeleton";
import { LlamaCloudRetriever } from "@/deps/cloud";
import { Settings } from "@llamaindex/core/global";
import { ChatMessage } from "@llamaindex/core/llms";
import { RetrieverQueryEngine } from "@llamaindex/core/query-engine";
import { OpenAI } from "@llamaindex/openai";
import { createAI, createStreamableUI, getMutableAIState } from "ai/rsc";
import { ReactNode } from "react";

Settings.llm = new OpenAI({
model: "gpt-4o",
});

const retriever = new LlamaCloudRetriever({
// eslint-disable-next-line turbo/no-undeclared-env-vars
apiKey: process.env.LLAMA_CLOUD_API_KEY!,
baseUrl: "https://api.cloud.llamaindex.ai/",
// eslint-disable-next-line turbo/no-undeclared-env-vars
pipelineId: process.env.LLAMA_CLOUD_PIPELINE_ID!,
});

const initialAIState = {
messages: [],
} as {
messages: ChatMessage[];
};

type UIMessage = {
id: number;
display: ReactNode;
};

const initialUIState = {
messages: [],
} as {
messages: UIMessage[];
};

const runAsyncFnWithoutBlocking = (fn: (...args: any) => Promise<any>) => {
fn().catch((error) => {
console.error(error);
});
};

export const AIProvider = createAI({
initialAIState,
initialUIState,
actions: {
query: async (message: string): Promise<UIMessage> => {
"use server";
const queryEngine = new RetrieverQueryEngine(retriever);

const id = Date.now();
const aiState = getMutableAIState<typeof AIProvider>();
aiState.update({
...aiState.get(),
messages: [
...aiState.get().messages,
{
role: "user",
content: message,
},
],
});

const ui = createStreamableUI(
<div className="space-y-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
</div>,
);

runAsyncFnWithoutBlocking(async () => {
const response = await queryEngine.query({
query: message,
stream: true,
});
let content = "";

for await (const { delta } of response) {
content += delta;
ui.update(<ClientMDXContent id={id} content={content} />);
}

ui.done();

aiState.done({
...aiState.get(),
messages: [
...aiState.get().messages,
{
role: "assistant",
content,
},
],
});
});

return {
id,
display: <BotMessage>{ui.value}</BotMessage>,
};
},
},
});
8 changes: 7 additions & 1 deletion apps/next/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { AIProvider } from "@/actions";
import { TooltipProvider } from "@/components/ui/tooltip";
import { RootProvider } from "fumadocs-ui/provider";
import { Inter } from "next/font/google";
import type { ReactNode } from "react";
Expand Down Expand Up @@ -31,7 +33,11 @@ export default function Layout({ children }: { children: ReactNode }) {
/>
</head>
<body className="flex flex-col min-h-screen">
<RootProvider>{children}</RootProvider>
<TooltipProvider>
<AIProvider>
<RootProvider>{children}</RootProvider>
</AIProvider>
</TooltipProvider>
</body>
</html>
);
Expand Down
Loading

0 comments on commit fedc766

Please sign in to comment.