mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-19 18:43:34 -04:00
docs(next): ask ai using llamacloud (#1400)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@llamaindex/core": patch
|
||||
"llamaindex": patch
|
||||
---
|
||||
|
||||
refactor: move `RetrieverQueryEngine` into core module
|
||||
+17
-3
@@ -6,35 +6,49 @@
|
||||
"build": "pnpm run build:docs && next build",
|
||||
"dev": "next dev",
|
||||
"start": "next start",
|
||||
"postinstall": "fumadocs-mdx",
|
||||
"postdev": "fumadocs-mdx",
|
||||
"postbuild": "fumadocs-mdx",
|
||||
"build:docs": "node ./scripts/generate-docs.mjs"
|
||||
},
|
||||
"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",
|
||||
|
||||
+111
-1
@@ -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],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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>,
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,11 +1,36 @@
|
||||
import { baseOptions } from "@/app/layout.config";
|
||||
import { AITrigger } from "@/components/ai-chat";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { source } from "@/lib/source";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { DocsLayout } from "fumadocs-ui/layouts/docs";
|
||||
import { MessageCircle } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<DocsLayout tree={source.pageTree} {...baseOptions}>
|
||||
<DocsLayout
|
||||
tree={source.pageTree}
|
||||
{...baseOptions}
|
||||
nav={{
|
||||
...baseOptions.nav,
|
||||
children: (
|
||||
<AITrigger
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: "secondary",
|
||||
size: "xs",
|
||||
className:
|
||||
"md:flex-1 px-2 ms-2 gap-1.5 text-fd-muted-foreground rounded-full",
|
||||
}),
|
||||
)}
|
||||
>
|
||||
<MessageCircle className="size-3" />
|
||||
Ask LlamaCloud
|
||||
</AITrigger>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import { DOCUMENT_URL } from "@/lib/const";
|
||||
import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
|
||||
import Image from "next/image";
|
||||
|
||||
const logo = (
|
||||
<Image
|
||||
src="/logo-large.png"
|
||||
alt="Logo"
|
||||
className="size-8"
|
||||
width={147}
|
||||
height={147}
|
||||
/>
|
||||
);
|
||||
|
||||
/**
|
||||
* Shared layout configurations
|
||||
@@ -10,7 +21,8 @@ import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared";
|
||||
*/
|
||||
export const baseOptions: BaseLayoutProps = {
|
||||
nav: {
|
||||
title: "LlamaIndex.TS",
|
||||
title: logo,
|
||||
transparentMode: "top",
|
||||
},
|
||||
githubUrl: "https://github.com/run-llama/LlamaIndexTS",
|
||||
links: [
|
||||
|
||||
@@ -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";
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
"use client";
|
||||
import type { AIProvider } from "@/actions";
|
||||
import { UserMessage } from "@/components/message";
|
||||
import { useActions, useUIState } from "ai/rsc";
|
||||
import { Info } from "lucide-react";
|
||||
import { ButtonHTMLAttributes, useState } from "react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
||||
import { Button } from "./ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "./ui/dialog";
|
||||
import { Textarea } from "./ui/textarea";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
|
||||
|
||||
type AITriggerProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
function ChatList({ messages }: { messages: any[] }) {
|
||||
if (messages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative mx-auto w-full px-4">
|
||||
{messages.map((message, index) => (
|
||||
<div key={index} className="pb-4">
|
||||
{message.display}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const AITrigger = (props: AITriggerProps) => {
|
||||
const [{ messages }, setUIState] = useUIState<typeof AIProvider>();
|
||||
const { query } = useActions<typeof AIProvider>();
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger {...props} />
|
||||
<DialogPortal>
|
||||
<DialogOverlay className="fixed inset-0 z-50 bg-fd-background/50 backdrop-blur-sm data-[state=closed]:animate-fd-fade-out data-[state=open]:animate-fd-fade-in" />
|
||||
<DialogContent
|
||||
onOpenAutoFocus={(e) => {
|
||||
document.getElementById("nd-ai-input")?.focus();
|
||||
e.preventDefault();
|
||||
}}
|
||||
className="fixed left-1/2 z-50 my-[5vh] flex max-h-[90dvh] w-[98vw] max-w-[860px] origin-left -translate-x-1/2 flex-col rounded-lg border bg-fd-popover text-fd-popover-foreground shadow-lg focus-visible:outline-none data-[state=closed]:animate-fd-dialog-out data-[state=open]:animate-fd-dialog-in"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="sr-only">Search AI</DialogTitle>
|
||||
<DialogDescription className="sr-only">
|
||||
Ask AI some questions.
|
||||
</DialogDescription>
|
||||
<Alert>
|
||||
<Info className="size-4" />
|
||||
<AlertTitle>Heads up!</AlertTitle>
|
||||
<AlertDescription>
|
||||
Answers from LlamaCloud may be inaccurate, please use with
|
||||
discretion.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</DialogHeader>
|
||||
<div className="overflow-scroll flex-grow mt-4">
|
||||
<ChatList messages={messages} />
|
||||
</div>
|
||||
<form
|
||||
className="px-4 py-2 space-y-4"
|
||||
action={async () => {
|
||||
const value = inputValue.trim();
|
||||
setInputValue("");
|
||||
if (!value) return;
|
||||
|
||||
// Add user message UI
|
||||
setUIState((state) => ({
|
||||
...state,
|
||||
messages: [
|
||||
...state.messages,
|
||||
{
|
||||
id: Date.now(),
|
||||
display: <UserMessage>{value}</UserMessage>,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
try {
|
||||
// Submit and get response message
|
||||
const responseMessage = await query(value);
|
||||
setUIState((state) => ({
|
||||
...state,
|
||||
messages: [...state.messages, responseMessage],
|
||||
}));
|
||||
} catch (error) {
|
||||
// You may want to show a toast or trigger an error state.
|
||||
console.error(error);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row w-full items-center gap-2">
|
||||
<Textarea
|
||||
tabIndex={0}
|
||||
placeholder="Ask AI about documentation."
|
||||
className="w-full resize-none bg-transparent px-4 focus-within:outline-none sm:text-sm"
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
event.currentTarget.form?.requestSubmit(null);
|
||||
}
|
||||
}}
|
||||
autoFocus
|
||||
spellCheck={false}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
name="message"
|
||||
rows={1}
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
/>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="submit"
|
||||
size="icon"
|
||||
disabled={inputValue === ""}
|
||||
>
|
||||
<span className="sr-only">Send message</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Send message</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
"use client";
|
||||
import { createProcessor, run } from "@mdx-js/mdx";
|
||||
import defaultMdxComponents from "fumadocs-ui/mdx";
|
||||
import { ReactNode, useDeferredValue } from "react";
|
||||
import * as runtime from "react/jsx-runtime";
|
||||
import useSWR from "swr";
|
||||
|
||||
const processor = createProcessor({
|
||||
outputFormat: "function-body",
|
||||
});
|
||||
|
||||
export function ClientMDXContent({
|
||||
content,
|
||||
id,
|
||||
}: {
|
||||
content: string;
|
||||
id: number;
|
||||
}): ReactNode {
|
||||
const deferredContent = useDeferredValue(content);
|
||||
const { data: jsx } = useSWR(["mdx", id, deferredContent], {
|
||||
fetcher: async () => {
|
||||
const code = await processor
|
||||
.process(deferredContent)
|
||||
.then((vfile) => vfile.value);
|
||||
const { default: Content } = await run(code, {
|
||||
...runtime,
|
||||
baseUrl: import.meta.url,
|
||||
});
|
||||
return (
|
||||
<Content
|
||||
components={{
|
||||
...defaultMdxComponents,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
suspense: true,
|
||||
});
|
||||
|
||||
return jsx;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import Image from "next/image";
|
||||
import { ReactNode } from "react";
|
||||
import { IconAI, IconUser } from "./ui/icons";
|
||||
|
||||
export function UserMessage({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="group relative flex items-start">
|
||||
<div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow-sm bg-background">
|
||||
<IconUser />
|
||||
</div>
|
||||
<div className="ml-4 flex-1 space-y-2 overflow-hidden px-1">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function BotMessage({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn("group relative flex items-start", className)}>
|
||||
<div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow-sm">
|
||||
<Image
|
||||
src="/logo-large.png"
|
||||
alt="Logo"
|
||||
className="size-4"
|
||||
width={147}
|
||||
height={147}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-4 flex-1 space-y-2 overflow-hidden px-1">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function BotCard({
|
||||
children,
|
||||
showAvatar = true,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
showAvatar?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="group relative flex items-start md:-ml-12">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow-sm bg-primary text-primary-foreground",
|
||||
!showAvatar && "invisible",
|
||||
)}
|
||||
>
|
||||
<IconAI />
|
||||
</div>
|
||||
<div className="ml-4 flex-1 px-1">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SystemMessage({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
"mt-2 flex items-center justify-center gap-2 text-xs text-gray-500"
|
||||
}
|
||||
>
|
||||
<div className={"max-w-[600px] flex-initial px-2 py-2"}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Alert.displayName = "Alert";
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AlertTitle.displayName = "AlertTitle";
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AlertDescription.displayName = "AlertDescription";
|
||||
|
||||
export { Alert, AlertDescription, AlertTitle };
|
||||
@@ -24,6 +24,7 @@ const buttonVariants = cva(
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
xs: "px-1.5 py-0.5 text-xs",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Dialog = DialogPrimitive.Root;
|
||||
|
||||
const DialogTrigger = DialogPrimitive.Trigger;
|
||||
|
||||
const DialogPortal = DialogPrimitive.Portal;
|
||||
|
||||
const DialogClose = DialogPrimitive.Close;
|
||||
|
||||
const DialogOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
));
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
const DialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DialogHeader.displayName = "DialogHeader";
|
||||
|
||||
const DialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DialogFooter.displayName = "DialogFooter";
|
||||
|
||||
const DialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
||||
|
||||
const DialogDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function IconAI({ className, ...props }: React.ComponentProps<"svg">) {
|
||||
return (
|
||||
<svg
|
||||
fill="currentColor"
|
||||
viewBox="0 0 256 256"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={cn("h-4 w-4", className)}
|
||||
{...props}
|
||||
>
|
||||
<path d="M197.58,129.06l-51.61-19-19-51.65a15.92,15.92,0,0,0-29.88,0L78.07,110l-51.65,19a15.92,15.92,0,0,0,0,29.88L78,178l19,51.62a15.92,15.92,0,0,0,29.88,0l19-51.61,51.65-19a15.92,15.92,0,0,0,0-29.88ZM140.39,163a15.87,15.87,0,0,0-9.43,9.43l-19,51.46L93,172.39A15.87,15.87,0,0,0,83.61,163h0L32.15,144l51.46-19A15.87,15.87,0,0,0,93,115.61l19-51.46,19,51.46a15.87,15.87,0,0,0,9.43,9.43l51.46,19ZM144,40a8,8,0,0,1,8-8h16V16a8,8,0,0,1,16,0V32h16a8,8,0,0,1,0,16H184V64a8,8,0,0,1-16,0V48H152A8,8,0,0,1,144,40ZM248,88a8,8,0,0,1-8,8h-8v8a8,8,0,0,1-16,0V96h-8a8,8,0,0,1,0-16h8V72a8,8,0,0,1,16,0v8h8A8,8,0,0,1,248,88Z"></path>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IconUser({ className, ...props }: React.ComponentProps<"svg">) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 256 256"
|
||||
fill="currentColor"
|
||||
className={cn("h-4 w-4", className)}
|
||||
{...props}
|
||||
>
|
||||
<path d="M230.92 212c-15.23-26.33-38.7-45.21-66.09-54.16a72 72 0 1 0-73.66 0c-27.39 8.94-50.86 27.82-66.09 54.16a8 8 0 1 0 13.85 8c18.84-32.56 52.14-52 89.07-52s70.23 19.44 89.07 52a8 8 0 1 0 13.85-8ZM72 96a56 56 0 1 1 56 56 56.06 56.06 0 0 1-56-56Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
Textarea.displayName = "Textarea";
|
||||
|
||||
export { Textarea };
|
||||
@@ -0,0 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider;
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root;
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</TooltipPrimitive.Portal>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
||||
@@ -0,0 +1,96 @@
|
||||
import {
|
||||
type MetadataFilter,
|
||||
type MetadataFilters,
|
||||
PipelinesService,
|
||||
type RetrievalParams,
|
||||
type TextNodeWithScore,
|
||||
} from "@llamaindex/cloud/api";
|
||||
import { QueryBundle } from "@llamaindex/core/query-engine";
|
||||
import { BaseRetriever } from "@llamaindex/core/retriever";
|
||||
import { jsonToNode, NodeWithScore, ObjectType } from "@llamaindex/core/schema";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
|
||||
export type CloudRetrieveParams = Omit<
|
||||
RetrievalParams,
|
||||
"query" | "search_filters" | "dense_similarity_top_k"
|
||||
> & { similarityTopK?: number; filters?: MetadataFilters };
|
||||
|
||||
export type LlamaCloudRetrieverParams = {
|
||||
apiKey: string;
|
||||
baseUrl: string;
|
||||
pipelineId: string;
|
||||
} & CloudRetrieveParams;
|
||||
|
||||
export class LlamaCloudRetriever extends BaseRetriever {
|
||||
baseUrl: string;
|
||||
apiKey: string;
|
||||
|
||||
retrieveParams: CloudRetrieveParams;
|
||||
organizationId?: string;
|
||||
pipelineId: string;
|
||||
|
||||
private resultNodesToNodeWithScore(
|
||||
nodes: TextNodeWithScore[],
|
||||
): NodeWithScore[] {
|
||||
return nodes.map((node: TextNodeWithScore) => {
|
||||
const textNode = jsonToNode(node.node, ObjectType.TEXT);
|
||||
textNode.metadata = {
|
||||
...textNode.metadata,
|
||||
...node.node.extra_info,
|
||||
};
|
||||
return {
|
||||
node: textNode,
|
||||
score: node.score ?? undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private convertFilter(filters?: MetadataFilters): MetadataFilters | null {
|
||||
if (!filters) return null;
|
||||
|
||||
const processFilter = (
|
||||
filter: MetadataFilter | MetadataFilters,
|
||||
): MetadataFilter | MetadataFilters => {
|
||||
if ("filters" in filter) {
|
||||
// type MetadataFilters
|
||||
return { ...filter, filters: filter.filters.map(processFilter) };
|
||||
}
|
||||
return { ...filter, value: filter.value ?? null };
|
||||
};
|
||||
|
||||
return { ...filters, filters: filters.filters.map(processFilter) };
|
||||
}
|
||||
|
||||
constructor(params: LlamaCloudRetrieverParams) {
|
||||
super();
|
||||
this.baseUrl = params.baseUrl;
|
||||
this.apiKey = params.apiKey;
|
||||
this.retrieveParams = params;
|
||||
this.pipelineId = params.pipelineId;
|
||||
}
|
||||
|
||||
override async _retrieve(query: QueryBundle): Promise<NodeWithScore[]> {
|
||||
const filters = this.convertFilter(this.retrieveParams.filters);
|
||||
const pipelineId = this.pipelineId;
|
||||
|
||||
const { data: results } =
|
||||
await PipelinesService.runSearchApiV1PipelinesPipelineIdRetrievePost({
|
||||
throwOnError: true,
|
||||
path: {
|
||||
pipeline_id: pipelineId,
|
||||
},
|
||||
baseUrl: this.baseUrl,
|
||||
body: {
|
||||
...this.retrieveParams,
|
||||
query: extractText(query),
|
||||
search_filters: filters,
|
||||
dense_similarity_top_k: this.retrieveParams.similarityTopK!,
|
||||
},
|
||||
headers: {
|
||||
authorization: `Bearer ${this.apiKey}`,
|
||||
},
|
||||
});
|
||||
|
||||
return this.resultNodesToNodeWithScore(results.retrieval_nodes);
|
||||
}
|
||||
}
|
||||
@@ -297,6 +297,20 @@
|
||||
"types": "./data-structs/dist/index.d.ts",
|
||||
"default": "./data-structs/dist/index.js"
|
||||
}
|
||||
},
|
||||
"./postprocessor": {
|
||||
"require": {
|
||||
"types": "./postprocessor/dist/index.d.cts",
|
||||
"default": "./postprocessor/dist/index.cjs"
|
||||
},
|
||||
"import": {
|
||||
"types": "./postprocessor/dist/index.d.ts",
|
||||
"default": "./postprocessor/dist/index.js"
|
||||
},
|
||||
"default": {
|
||||
"types": "./postprocessor/dist/index.d.ts",
|
||||
"default": "./postprocessor/dist/index.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
@@ -320,7 +334,8 @@
|
||||
"./retriever",
|
||||
"./vector-store",
|
||||
"./tools",
|
||||
"./data-structs"
|
||||
"./data-structs",
|
||||
"./postprocessor"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "bunchee --watch",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type { BaseNodePostprocessor } from "./type";
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import type { MessageContent } from "@llamaindex/core/llms";
|
||||
import type { NodeWithScore } from "@llamaindex/core/schema";
|
||||
import type { MessageContent } from "../llms";
|
||||
import type { NodeWithScore } from "../schema";
|
||||
|
||||
export interface BaseNodePostprocessor {
|
||||
/**
|
||||
@@ -36,9 +36,10 @@ export type QueryFn = (
|
||||
) => Promise<AsyncIterable<EngineResponse> | EngineResponse>;
|
||||
|
||||
export abstract class BaseQueryEngine extends PromptMixin {
|
||||
protected constructor(protected readonly _query: QueryFn) {
|
||||
super();
|
||||
}
|
||||
abstract _query(
|
||||
strOrQueryBundle: QueryType,
|
||||
stream?: boolean,
|
||||
): Promise<AsyncIterable<EngineResponse> | EngineResponse>;
|
||||
|
||||
async retrieve(params: QueryType): Promise<NodeWithScore[]> {
|
||||
throw new Error(
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export { BaseQueryEngine, type QueryBundle, type QueryType } from "./base";
|
||||
export { RetrieverQueryEngine } from "./retriever";
|
||||
export type { QueryEndEvent, QueryStartEvent } from "./type";
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import type { MessageContent } from "../llms";
|
||||
import type { BaseNodePostprocessor } from "../postprocessor";
|
||||
import { BaseQueryEngine, type QueryType } from "../query-engine";
|
||||
import {
|
||||
type BaseSynthesizer,
|
||||
getResponseSynthesizer,
|
||||
} from "../response-synthesizers";
|
||||
import { BaseRetriever } from "../retriever";
|
||||
import type { NodeWithScore } from "../schema";
|
||||
import { extractText } from "../utils";
|
||||
|
||||
export class RetrieverQueryEngine extends BaseQueryEngine {
|
||||
retriever: BaseRetriever;
|
||||
responseSynthesizer: BaseSynthesizer;
|
||||
nodePostprocessors: BaseNodePostprocessor[];
|
||||
|
||||
constructor(
|
||||
retriever: BaseRetriever,
|
||||
responseSynthesizer?: BaseSynthesizer,
|
||||
nodePostprocessors?: BaseNodePostprocessor[],
|
||||
) {
|
||||
super();
|
||||
|
||||
this.retriever = retriever;
|
||||
this.responseSynthesizer =
|
||||
responseSynthesizer || getResponseSynthesizer("compact");
|
||||
this.nodePostprocessors = nodePostprocessors || [];
|
||||
}
|
||||
|
||||
override async _query(strOrQueryBundle: QueryType, stream?: boolean) {
|
||||
const nodesWithScore = await this.retrieve(
|
||||
typeof strOrQueryBundle === "string"
|
||||
? strOrQueryBundle
|
||||
: extractText(strOrQueryBundle),
|
||||
);
|
||||
if (stream) {
|
||||
return this.responseSynthesizer.synthesize(
|
||||
{
|
||||
query:
|
||||
typeof strOrQueryBundle === "string"
|
||||
? { query: strOrQueryBundle }
|
||||
: strOrQueryBundle,
|
||||
nodes: nodesWithScore,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
return this.responseSynthesizer.synthesize({
|
||||
query:
|
||||
typeof strOrQueryBundle === "string"
|
||||
? { query: strOrQueryBundle }
|
||||
: strOrQueryBundle,
|
||||
nodes: nodesWithScore,
|
||||
});
|
||||
}
|
||||
|
||||
protected _getPrompts() {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected _updatePrompts() {}
|
||||
|
||||
_getPromptModules() {
|
||||
return {
|
||||
responseSynthesizer: this.responseSynthesizer,
|
||||
};
|
||||
}
|
||||
|
||||
private async applyNodePostprocessors(
|
||||
nodes: NodeWithScore[],
|
||||
query: MessageContent,
|
||||
) {
|
||||
let nodesWithScore = nodes;
|
||||
|
||||
for (const postprocessor of this.nodePostprocessors) {
|
||||
nodesWithScore = await postprocessor.postprocessNodes(
|
||||
nodesWithScore,
|
||||
query,
|
||||
);
|
||||
}
|
||||
|
||||
return nodesWithScore;
|
||||
}
|
||||
|
||||
override async retrieve(query: QueryType) {
|
||||
const nodes = await this.retriever.retrieve(query);
|
||||
|
||||
const messageContent = typeof query === "string" ? query : query.query;
|
||||
return await this.applyNodePostprocessors(nodes, messageContent);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import type { BaseQueryEngine } from "@llamaindex/core/query-engine";
|
||||
import type { BaseSynthesizer } from "@llamaindex/core/response-synthesizers";
|
||||
import type { Document } from "@llamaindex/core/schema";
|
||||
import { RetrieverQueryEngine } from "../engines/query/RetrieverQueryEngine.js";
|
||||
import type { BaseNodePostprocessor } from "../postprocessors/types.js";
|
||||
import type { CloudRetrieveParams } from "./LlamaCloudRetriever.js";
|
||||
import { LlamaCloudRetriever } from "./LlamaCloudRetriever.js";
|
||||
import type { CloudConstructorParams } from "./type.js";
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
MessageType,
|
||||
} from "@llamaindex/core/llms";
|
||||
import { BaseMemory, ChatMemoryBuffer } from "@llamaindex/core/memory";
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import {
|
||||
type ContextSystemPrompt,
|
||||
type ModuleRecord,
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
streamReducer,
|
||||
} from "@llamaindex/core/utils";
|
||||
import { Settings } from "../../Settings.js";
|
||||
import type { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import { DefaultContextGenerator } from "./DefaultContextGenerator.js";
|
||||
import type { ContextGenerator } from "./types.js";
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MessageContent, MessageType } from "@llamaindex/core/llms";
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import {
|
||||
type ContextSystemPrompt,
|
||||
defaultContextSystemPrompt,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
import { createMessageContent } from "@llamaindex/core/response-synthesizers";
|
||||
import type { BaseRetriever } from "@llamaindex/core/retriever";
|
||||
import { MetadataMode, type NodeWithScore } from "@llamaindex/core/schema";
|
||||
import type { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import type { Context, ContextGenerator } from "./types.js";
|
||||
|
||||
export class DefaultContextGenerator
|
||||
|
||||
@@ -1,90 +1,4 @@
|
||||
import type { MessageContent } from "@llamaindex/core/llms";
|
||||
import { BaseQueryEngine, type QueryType } from "@llamaindex/core/query-engine";
|
||||
import type { BaseSynthesizer } from "@llamaindex/core/response-synthesizers";
|
||||
import { getResponseSynthesizer } from "@llamaindex/core/response-synthesizers";
|
||||
import { BaseRetriever } from "@llamaindex/core/retriever";
|
||||
import { type NodeWithScore } from "@llamaindex/core/schema";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
import type { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
|
||||
/**
|
||||
* A query engine that uses a retriever to query an index and then synthesizes the response.
|
||||
* todo: this file should be removed in the major release
|
||||
*/
|
||||
export class RetrieverQueryEngine extends BaseQueryEngine {
|
||||
retriever: BaseRetriever;
|
||||
responseSynthesizer: BaseSynthesizer;
|
||||
nodePostprocessors: BaseNodePostprocessor[];
|
||||
|
||||
constructor(
|
||||
retriever: BaseRetriever,
|
||||
responseSynthesizer?: BaseSynthesizer,
|
||||
nodePostprocessors?: BaseNodePostprocessor[],
|
||||
) {
|
||||
super(async (strOrQueryBundle, stream) => {
|
||||
const nodesWithScore = await this.retrieve(
|
||||
typeof strOrQueryBundle === "string"
|
||||
? strOrQueryBundle
|
||||
: extractText(strOrQueryBundle),
|
||||
);
|
||||
if (stream) {
|
||||
return this.responseSynthesizer.synthesize(
|
||||
{
|
||||
query:
|
||||
typeof strOrQueryBundle === "string"
|
||||
? { query: strOrQueryBundle }
|
||||
: strOrQueryBundle,
|
||||
nodes: nodesWithScore,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
return this.responseSynthesizer.synthesize({
|
||||
query:
|
||||
typeof strOrQueryBundle === "string"
|
||||
? { query: strOrQueryBundle }
|
||||
: strOrQueryBundle,
|
||||
nodes: nodesWithScore,
|
||||
});
|
||||
});
|
||||
|
||||
this.retriever = retriever;
|
||||
this.responseSynthesizer =
|
||||
responseSynthesizer || getResponseSynthesizer("compact");
|
||||
this.nodePostprocessors = nodePostprocessors || [];
|
||||
}
|
||||
|
||||
protected _getPrompts() {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected _updatePrompts() {}
|
||||
|
||||
_getPromptModules() {
|
||||
return {
|
||||
responseSynthesizer: this.responseSynthesizer,
|
||||
};
|
||||
}
|
||||
|
||||
private async applyNodePostprocessors(
|
||||
nodes: NodeWithScore[],
|
||||
query: MessageContent,
|
||||
) {
|
||||
let nodesWithScore = nodes;
|
||||
|
||||
for (const postprocessor of this.nodePostprocessors) {
|
||||
nodesWithScore = await postprocessor.postprocessNodes(
|
||||
nodesWithScore,
|
||||
query,
|
||||
);
|
||||
}
|
||||
|
||||
return nodesWithScore;
|
||||
}
|
||||
|
||||
override async retrieve(query: QueryType) {
|
||||
const nodes = await this.retriever.retrieve(query);
|
||||
|
||||
const messageContent = typeof query === "string" ? query : query.query;
|
||||
return await this.applyNodePostprocessors(nodes, messageContent);
|
||||
}
|
||||
}
|
||||
export { RetrieverQueryEngine } from "@llamaindex/core/query-engine";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
BaseQueryEngine,
|
||||
type QueryBundle,
|
||||
type QueryType,
|
||||
} from "@llamaindex/core/query-engine";
|
||||
import {
|
||||
BaseSynthesizer,
|
||||
@@ -63,19 +64,7 @@ export class RouterQueryEngine extends BaseQueryEngine {
|
||||
summarizer?: BaseSynthesizer | undefined;
|
||||
verbose?: boolean | undefined;
|
||||
}) {
|
||||
super(async (strOrQueryBundle, stream) => {
|
||||
const response = await this.queryRoute(
|
||||
typeof strOrQueryBundle === "string"
|
||||
? { query: strOrQueryBundle }
|
||||
: strOrQueryBundle,
|
||||
);
|
||||
|
||||
if (stream) {
|
||||
throw new Error("Streaming is not supported yet.");
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
super();
|
||||
|
||||
this.selector = init.selector;
|
||||
this.queryEngines = init.queryEngineTools.map((tool) => tool.queryEngine);
|
||||
@@ -87,6 +76,20 @@ export class RouterQueryEngine extends BaseQueryEngine {
|
||||
this.verbose = init.verbose ?? false;
|
||||
}
|
||||
|
||||
override async _query(strOrQueryBundle: QueryType, stream?: boolean) {
|
||||
const response = await this.queryRoute(
|
||||
typeof strOrQueryBundle === "string"
|
||||
? { query: strOrQueryBundle }
|
||||
: strOrQueryBundle,
|
||||
);
|
||||
|
||||
if (stream) {
|
||||
throw new Error("Streaming is not supported yet.");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected _getPrompts() {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { PromptsRecord } from "@llamaindex/core/prompts";
|
||||
import {
|
||||
BaseQueryEngine,
|
||||
type QueryBundle,
|
||||
type QueryType,
|
||||
} from "@llamaindex/core/query-engine";
|
||||
import type { BaseQuestionGenerator, SubQuestion } from "./types.js";
|
||||
|
||||
@@ -26,44 +27,7 @@ export class SubQuestionQueryEngine extends BaseQueryEngine {
|
||||
responseSynthesizer: BaseSynthesizer;
|
||||
queryEngineTools: BaseTool[];
|
||||
}) {
|
||||
super(async (strOrQueryBundle, stream) => {
|
||||
let query: QueryBundle;
|
||||
if (typeof strOrQueryBundle === "string") {
|
||||
query = {
|
||||
query: strOrQueryBundle,
|
||||
};
|
||||
} else {
|
||||
query = strOrQueryBundle;
|
||||
}
|
||||
const subQuestions = await this.questionGen.generate(
|
||||
this.metadatas,
|
||||
strOrQueryBundle,
|
||||
);
|
||||
|
||||
const subQNodes = await Promise.all(
|
||||
subQuestions.map((subQ) => this.querySubQ(subQ)),
|
||||
);
|
||||
|
||||
const nodesWithScore: NodeWithScore[] = subQNodes.filter(
|
||||
(node) => node !== null,
|
||||
);
|
||||
if (stream) {
|
||||
return this.responseSynthesizer.synthesize(
|
||||
{
|
||||
query,
|
||||
nodes: nodesWithScore,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
return this.responseSynthesizer.synthesize(
|
||||
{
|
||||
query,
|
||||
nodes: nodesWithScore,
|
||||
},
|
||||
false,
|
||||
);
|
||||
});
|
||||
super();
|
||||
|
||||
this.questionGen = init.questionGen;
|
||||
this.responseSynthesizer =
|
||||
@@ -72,6 +36,45 @@ export class SubQuestionQueryEngine extends BaseQueryEngine {
|
||||
this.metadatas = init.queryEngineTools.map((tool) => tool.metadata);
|
||||
}
|
||||
|
||||
override async _query(strOrQueryBundle: QueryType, stream?: boolean) {
|
||||
let query: QueryBundle;
|
||||
if (typeof strOrQueryBundle === "string") {
|
||||
query = {
|
||||
query: strOrQueryBundle,
|
||||
};
|
||||
} else {
|
||||
query = strOrQueryBundle;
|
||||
}
|
||||
const subQuestions = await this.questionGen.generate(
|
||||
this.metadatas,
|
||||
strOrQueryBundle,
|
||||
);
|
||||
|
||||
const subQNodes = await Promise.all(
|
||||
subQuestions.map((subQ) => this.querySubQ(subQ)),
|
||||
);
|
||||
|
||||
const nodesWithScore: NodeWithScore[] = subQNodes.filter(
|
||||
(node) => node !== null,
|
||||
);
|
||||
if (stream) {
|
||||
return this.responseSynthesizer.synthesize(
|
||||
{
|
||||
query,
|
||||
nodes: nodesWithScore,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
return this.responseSynthesizer.synthesize(
|
||||
{
|
||||
query,
|
||||
nodes: nodesWithScore,
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
protected _getPrompts(): PromptsRecord {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { MetadataMode } from "@llamaindex/core/schema";
|
||||
import type { ServiceContext } from "../../ServiceContext.js";
|
||||
import { serviceContextFromDefaults } from "../../ServiceContext.js";
|
||||
import { RetrieverQueryEngine } from "../../engines/query/index.js";
|
||||
import type { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import type { StorageContext } from "../../storage/StorageContext.js";
|
||||
import { storageContextFromDefaults } from "../../storage/StorageContext.js";
|
||||
import type { BaseDocumentStore } from "../../storage/docStore/types.js";
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
|
||||
import { KeywordTable } from "@llamaindex/core/data-structs";
|
||||
import type { LLM } from "@llamaindex/core/llms";
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import {
|
||||
defaultKeywordExtractPrompt,
|
||||
defaultQueryKeywordExtractPrompt,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import {
|
||||
type ChoiceSelectPrompt,
|
||||
defaultChoiceSelectPrompt,
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
nodeParserFromSettingsOrContext,
|
||||
} from "../../Settings.js";
|
||||
import { RetrieverQueryEngine } from "../../engines/query/index.js";
|
||||
import type { BaseNodePostprocessor } from "../../postprocessors/index.js";
|
||||
import type { StorageContext } from "../../storage/StorageContext.js";
|
||||
import { storageContextFromDefaults } from "../../storage/StorageContext.js";
|
||||
import type {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
type BaseEmbedding,
|
||||
} from "@llamaindex/core/embeddings";
|
||||
import type { MessageContent } from "@llamaindex/core/llms";
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import type { QueryBundle } from "@llamaindex/core/query-engine";
|
||||
import type { BaseSynthesizer } from "@llamaindex/core/response-synthesizers";
|
||||
import { BaseRetriever } from "@llamaindex/core/retriever";
|
||||
@@ -26,7 +27,6 @@ import {
|
||||
DocStoreStrategy,
|
||||
createDocStoreStrategy,
|
||||
} from "../../ingestion/strategies/index.js";
|
||||
import type { BaseNodePostprocessor } from "../../postprocessors/types.js";
|
||||
import type { StorageContext } from "../../storage/StorageContext.js";
|
||||
import { storageContextFromDefaults } from "../../storage/StorageContext.js";
|
||||
import type { BaseIndexStore } from "../../storage/indexStore/types.js";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import type { NodeWithScore } from "@llamaindex/core/schema";
|
||||
import { MetadataMode } from "@llamaindex/core/schema";
|
||||
import type { BaseNodePostprocessor } from "./types.js";
|
||||
|
||||
export class MetadataReplacementPostProcessor implements BaseNodePostprocessor {
|
||||
targetMetadataKey: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import type { NodeWithScore } from "@llamaindex/core/schema";
|
||||
import type { BaseNodePostprocessor } from "./types.js";
|
||||
|
||||
export class SimilarityPostprocessor implements BaseNodePostprocessor {
|
||||
similarityCutoff?: number | undefined;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from "./MetadataReplacementPostProcessor.js";
|
||||
export * from "./rerankers/index.js";
|
||||
export * from "./SimilarityPostprocessor.js";
|
||||
export * from "./types.js";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { CohereClient } from "cohere-ai";
|
||||
|
||||
import type { MessageContent } from "@llamaindex/core/llms";
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import type { NodeWithScore } from "@llamaindex/core/schema";
|
||||
import { MetadataMode } from "@llamaindex/core/schema";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
import type { BaseNodePostprocessor } from "../types.js";
|
||||
|
||||
type CohereRerankOptions = {
|
||||
topN?: number;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { MessageContent } from "@llamaindex/core/llms";
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import type { NodeWithScore } from "@llamaindex/core/schema";
|
||||
import { MetadataMode } from "@llamaindex/core/schema";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import type { BaseNodePostprocessor } from "../types.js";
|
||||
|
||||
interface JinaAIRerankerResult {
|
||||
index: number;
|
||||
|
||||
@@ -5,8 +5,8 @@ import { MetadataMode } from "@llamaindex/core/schema";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
|
||||
import type { MessageContent } from "@llamaindex/core/llms";
|
||||
import type { BaseNodePostprocessor } from "@llamaindex/core/postprocessor";
|
||||
import type { BaseNode, NodeWithScore } from "@llamaindex/core/schema";
|
||||
import type { BaseNodePostprocessor } from "../types.js";
|
||||
|
||||
type RerankingRequestWithoutInput = Omit<
|
||||
MixedbreadAI.RerankingRequest,
|
||||
|
||||
Generated
+780
-291
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user