Compare commits

...

5 Commits

Author SHA1 Message Date
github-actions[bot] 9cf88e9f3f Release 0.9.13 (#1783)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-26 10:31:29 +02:00
Thuc Pham 75d6e29187 feat: response source nodes in query tool output (#1784) 2025-03-26 15:24:53 +07:00
Parham Saidi 132517877e fix: stringify all tool results for anthropic on bedrock (#1786) 2025-03-25 21:16:44 +07:00
Thuc Pham 299008b34f feat: copy create-llama to @llamaindex/servers (#1780) 2025-03-25 11:55:44 +02:00
Thuc Pham 482ed67690 fix: document deployment fail due to static generation timed out (#1779) 2025-03-24 11:12:09 +02:00
74 changed files with 2011 additions and 197 deletions
+7
View File
@@ -1,5 +1,12 @@
# @llamaindex/doc
## 0.2.1
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.2.0
### Minor Changes
+2
View File
@@ -4,6 +4,8 @@ const withMDX = createMDX();
/** @type {import('next').NextConfig} */
const config = {
// default timeout for static generation is 60s, but we need to increase it to 10 minutes due to the large number of document pages
staticPageGenerationTimeout: 600,
reactStrictMode: true,
eslint: {
ignoreDuringBuilds: true,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/doc",
"version": "0.2.0",
"version": "0.2.1",
"private": true,
"scripts": {
"postinstall": "fumadocs-mdx",
@@ -7,9 +7,59 @@ A tool can be called to perform custom actions, or retrieve extra information ba
A result from a tool call can be used by subsequent steps in a workflow, or to compute a final answer.
For example, a "weather tool" could fetch some live weather information from a geographical location.
## Tool Function
The `tool` function is a utility provided to define a tool that can be used by an agent. It takes a function and a configuration object as arguments. The configuration object includes the tool's name, description, and parameters.
### Parameters with Zod
The `parameters` field in the tool configuration is defined using `zod`, a TypeScript-first schema declaration and validation library. `zod` allows you to specify the expected structure and types of the input parameters, ensuring that the data passed to the tool is valid.
Example:
```ts
import { agent, tool } from "llamaindex";
import { z } from "zod";
// first arg is LLM input, second is bound arg
const queryKnowledgeBase = async ({ question }, { userToken }) => {
const response = await fetch(`https://knowledge-base.com?token=${userToken}&query=${question}`);
// ...
};
// define tool with zod validation
const kbTool = tool(queryKnowledgeBase, {
name: 'queryKnowledgeBase',
description: 'Query knowledge base',
parameters: z.object({
question: z.string({
description: 'The user question',
}),
}),
});
```
In this example, `z.object` is used to define a schema for the `parameters` where `question` is expected to be a string. This ensures that any input to the tool adheres to the specified structure, providing a layer of type safety and validation.
## Built-in tools
You can import built-in tools from the `@llamaindex/tools` package.
```ts
import { agent } from "llamaindex";
import { wiki } from "@llamaindex/tools";
const researchAgent = agent({
name: "WikiAgent",
description: "Gathering information from the internet",
systemPrompt: `You are a research agent. Your role is to gather information from the internet using the provided tools.`,
tools: [wiki()],
});
```
## Function tool
Function tools are implemented with the `FunctionTool` class.
You can still use the `FunctionTool` class to define a tool.
A `FunctionTool` is constructed from a function with signature
```ts
(input: T, additionalArg?: AdditionalToolArgument) => R
@@ -1,5 +1,12 @@
# @llamaindex/cloudflare-worker-agent-test
## 0.0.147
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.0.146
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloudflare-worker-agent-test",
"version": "0.0.146",
"version": "0.0.147",
"type": "module",
"private": true,
"scripts": {
+7
View File
@@ -1,5 +1,12 @@
# @llamaindex/next-agent-test
## 0.1.147
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.1.146
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-agent-test",
"version": "0.1.146",
"version": "0.1.147",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,12 @@
# test-edge-runtime
## 0.1.146
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.1.145
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/nextjs-edge-runtime-test",
"version": "0.1.145",
"version": "0.1.146",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,12 @@
# @llamaindex/next-node-runtime
## 0.1.13
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.1.12
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-node-runtime-test",
"version": "0.1.12",
"version": "0.1.13",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,12 @@
# vite-import-llamaindex
## 0.0.13
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.0.12
### Patch Changes
@@ -1,7 +1,7 @@
{
"name": "vite-import-llamaindex",
"private": true,
"version": "0.0.12",
"version": "0.0.13",
"type": "module",
"scripts": {
"build": "vite build",
@@ -1,5 +1,12 @@
# @llamaindex/waku-query-engine-test
## 0.0.147
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.0.146
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/waku-query-engine-test",
"version": "0.0.146",
"version": "0.0.147",
"type": "module",
"private": true,
"scripts": {
+51
View File
@@ -0,0 +1,51 @@
import {
AgentStream,
AgentToolCallResult,
Document,
VectorStoreIndex,
agent,
openai,
} from "llamaindex";
async function main() {
const index = await VectorStoreIndex.fromDocuments([
new Document({
text: "Cats have a specialized collarbone that allows them to always land on their feet when they fall.",
}),
new Document({
text: "Dogs have a sense of smell that is 10,000 to 100,000 times more acute than humans.",
}),
new Document({
text: "Cats are known for their agility and ability to jump high.",
}),
]);
const myAgent = agent({
llm: openai({ model: "gpt-4o" }),
tools: [
index.queryTool({
options: { similarityTopK: 2 },
includeSourceNodes: true,
}),
],
});
const context = myAgent.run("The fact about cats");
for await (const event of context) {
if (event instanceof AgentToolCallResult) {
console.log(
"Using these retrieved information to answer the question:\n",
event.data.toolOutput.result,
);
} else if (event instanceof AgentStream) {
for (const chunk of event.data.delta) {
process.stdout.write(chunk);
}
}
}
}
void main().then(() => {
console.log("Done");
});
+7
View File
@@ -1,5 +1,12 @@
# @llamaindex/autotool
## 6.0.13
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 6.0.12
### Patch Changes
@@ -1,5 +1,13 @@
# @llamaindex/autotool-01-node-example
## 0.0.94
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
- @llamaindex/autotool@6.0.13
## 0.0.93
### Patch Changes
@@ -13,5 +13,5 @@
"scripts": {
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
},
"version": "0.0.93"
"version": "0.0.94"
}
+1 -1
View File
@@ -6,7 +6,7 @@
"url": "git+https://github.com/run-llama/LlamaIndexTS.git",
"directory": "packages/autotool"
},
"version": "6.0.12",
"version": "6.0.13",
"description": "auto transpile your JS function to LLM Agent compatible",
"files": [
"dist",
+6
View File
@@ -1,5 +1,11 @@
# @llamaindex/community
## 0.0.92
### Patch Changes
- 1325178: fix: stringify all tool results for anthropic on bedrock
## 0.0.91
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/community",
"description": "Community package for LlamaIndexTS",
"version": "0.0.91",
"version": "0.0.92",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
@@ -111,7 +111,7 @@ export const mapChatMessagesToAnthropicMessages = <
{
type: "tool_result",
tool_use_id: msg.options.toolResult.id,
content: msg.options.toolResult.result,
content: JSON.stringify(msg.options.toolResult.result),
},
],
},
+7
View File
@@ -1,5 +1,12 @@
# @llamaindex/experimental
## 0.0.163
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.0.162
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/experimental",
"description": "Experimental package for LlamaIndexTS",
"version": "0.0.162",
"version": "0.0.163",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
+6
View File
@@ -1,5 +1,11 @@
# llamaindex
## 0.9.13
### Patch Changes
- 75d6e29: feat: response source nodes in query tool output
## 0.9.12
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "llamaindex",
"version": "0.9.12",
"version": "0.9.13",
"license": "MIT",
"type": "module",
"keywords": [
@@ -41,6 +41,7 @@ export type QueryToolParams = (
) & {
responseSynthesizer?: BaseSynthesizer;
metadata?: ToolMetadata<JSONSchemaType<QueryEngineParam>> | undefined;
includeSourceNodes?: boolean;
};
/**
@@ -98,6 +99,7 @@ export abstract class BaseIndex<T> {
return new QueryEngineTool({
queryEngine: this.asQueryEngine(params),
metadata: params?.metadata,
includeSourceNodes: params?.includeSourceNodes ?? false,
});
}
@@ -1,3 +1,4 @@
import type { JSONValue } from "@llamaindex/core/global";
import type { BaseTool, ToolMetadata } from "@llamaindex/core/llms";
import type { BaseQueryEngine } from "@llamaindex/core/query-engine";
import type { JSONSchemaType } from "ajv";
@@ -20,6 +21,7 @@ const DEFAULT_PARAMETERS: JSONSchemaType<QueryEngineParam> = {
export type QueryEngineToolParams = {
queryEngine: BaseQueryEngine;
metadata?: ToolMetadata<JSONSchemaType<QueryEngineParam>> | undefined;
includeSourceNodes?: boolean;
};
export type QueryEngineParam = {
@@ -29,19 +31,32 @@ export type QueryEngineParam = {
export class QueryEngineTool implements BaseTool<QueryEngineParam> {
private queryEngine: BaseQueryEngine;
metadata: ToolMetadata<JSONSchemaType<QueryEngineParam>>;
includeSourceNodes: boolean;
constructor({ queryEngine, metadata }: QueryEngineToolParams) {
constructor({
queryEngine,
metadata,
includeSourceNodes,
}: QueryEngineToolParams) {
this.queryEngine = queryEngine;
this.metadata = {
name: metadata?.name ?? DEFAULT_NAME,
description: metadata?.description ?? DEFAULT_DESCRIPTION,
parameters: metadata?.parameters ?? DEFAULT_PARAMETERS,
};
this.includeSourceNodes = includeSourceNodes ?? false;
}
async call({ query }: QueryEngineParam) {
const response = await this.queryEngine.query({ query });
return response.message.content;
if (!this.includeSourceNodes) {
return { content: response.message.content };
}
return {
content: response.message.content,
sourceNodes: response.sourceNodes,
} as unknown as JSONValue;
}
}
+9
View File
@@ -1,5 +1,14 @@
# @llamaindex/server
## 0.0.3
### Patch Changes
- 299008b: feat: copy create-llama to @llamaindex/servers
- 75d6e29: feat: response source nodes in query tool output
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.0.2
### Patch Changes
+7 -9
View File
@@ -3,12 +3,7 @@
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
.pnp.js
# testing
/coverage
@@ -28,10 +23,9 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# local env files
.env*.local
# vercel
.vercel
@@ -39,3 +33,7 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
output/
!lib/
-3
View File
@@ -1,3 +0,0 @@
{
"trailingComma": "all"
}
@@ -0,0 +1,31 @@
"use client";
import { ChatSection as ChatSectionUI } from "@llamaindex/chat-ui";
import "@llamaindex/chat-ui/styles/markdown.css";
import "@llamaindex/chat-ui/styles/pdf.css";
import { useChat } from "ai/react";
import CustomChatInput from "./ui/chat/chat-input";
import CustomChatMessages from "./ui/chat/chat-messages";
import { getConfig } from "./ui/lib/utils";
export default function ChatSection() {
const handler = useChat({
api: getConfig("CHAT_API"),
onError: (error: unknown) => {
if (!(error instanceof Error)) throw error;
let errorMessage: string;
try {
errorMessage = JSON.parse(error.message).detail;
} catch (e) {
errorMessage = error.message;
}
alert(errorMessage);
},
});
return (
<ChatSectionUI handler={handler} className="h-full w-full">
<CustomChatMessages />
<CustomChatInput />
</ChatSectionUI>
);
}
@@ -0,0 +1,23 @@
export default function Header() {
return (
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
<p className="bg-linear-to-b fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 from-zinc-200 pb-6 pt-8 backdrop-blur-2xl lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:dark:bg-zinc-800/30">
Get started by editing&nbsp;
<code className="font-mono font-bold">app/page.tsx</code>
</p>
<div className="bg-linear-to-t fixed bottom-0 left-0 mb-4 flex h-auto w-full items-end justify-center from-white via-white lg:static lg:mb-0 lg:w-auto lg:bg-none dark:from-black dark:via-black">
<a
href="https://www.llamaindex.ai/"
className="font-nunito flex items-center justify-center gap-2 text-lg font-bold"
>
<span>Built by LlamaIndex</span>
<img
className="h-[40px] w-[40px] rounded-xl"
src="/llama.png"
alt="Llama Logo"
/>
</a>
</div>
</div>
);
}
@@ -0,0 +1,56 @@
"use client";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDown } from "lucide-react";
import * as React from "react";
import { cn } from "./lib/utils";
const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-left text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className,
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 shrink-0 text-neutral-500 transition-transform duration-200 dark:text-neutral-400" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
@@ -0,0 +1,56 @@
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react";
import { cn } from "./lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
},
);
Button.displayName = "Button";
export { Button, buttonVariants };
@@ -0,0 +1,82 @@
import * as React from "react";
import { cn } from "./lib/utils";
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border border-neutral-200 bg-white text-neutral-950 shadow-sm dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
className,
)}
{...props}
/>
));
Card.displayName = "Card";
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
));
CardHeader.displayName = "CardHeader";
const CardTitle = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
));
CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm text-neutral-500 dark:text-neutral-400", className)}
{...props}
/>
));
CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
));
CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
));
CardFooter.displayName = "CardFooter";
export {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
};
@@ -0,0 +1,23 @@
import { useChatMessage } from "@llamaindex/chat-ui";
import { User2 } from "lucide-react";
export function ChatMessageAvatar() {
const { message } = useChatMessage();
if (message.role === "user") {
return (
<div className="bg-background flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow-sm">
<User2 className="h-4 w-4" />
</div>
);
}
return (
<div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border bg-black text-white shadow-sm">
<img
className="h-[40px] w-[40px] rounded-xl object-contain"
src="/llama.png"
alt="Llama Logo"
/>
</div>
);
}
@@ -0,0 +1,84 @@
"use client";
import { ChatInput, useChatUI, useFile } from "@llamaindex/chat-ui";
import { DocumentInfo, ImagePreview } from "@llamaindex/chat-ui/widgets";
import { getConfig } from "../lib/utils";
import { LlamaCloudSelector } from "./custom/llama-cloud-selector";
export default function CustomChatInput() {
const { requestData, isLoading, input } = useChatUI();
const uploadAPI = getConfig("UPLOAD_API") ?? "";
const llamaCloudAPI = getConfig("LLAMA_CLOUD_API") ?? "";
const {
imageUrl,
setImageUrl,
uploadFile,
files,
removeDoc,
reset,
getAnnotations,
} = useFile({ uploadAPI });
/**
* Handles file uploads. Overwrite to hook into the file upload behavior.
* @param file The file to upload
*/
const handleUploadFile = async (file: File) => {
// There's already an image uploaded, only allow one image at a time
if (imageUrl) {
alert("You can only upload one image at a time.");
return;
}
try {
// Upload the file and send with it the current request data
await uploadFile(file, requestData);
} catch (error: unknown) {
// Show error message if upload fails
alert(
error instanceof Error ? error.message : "An unknown error occurred",
);
}
};
// Get references to the upload files in message annotations format, see https://github.com/run-llama/chat-ui/blob/main/packages/chat-ui/src/hook/use-file.tsx#L56
const annotations = getAnnotations();
return (
<ChatInput
className="rounded-xl shadow-xl"
resetUploadedFiles={reset}
annotations={annotations}
>
<div>
{/* Image preview section */}
{imageUrl && (
<ImagePreview url={imageUrl} onRemove={() => setImageUrl(null)} />
)}
{/* Document previews section */}
{files.length > 0 && (
<div className="flex w-full gap-4 overflow-auto py-2">
{files.map((file) => (
<DocumentInfo
key={file.id}
document={{ url: file.url, sources: [] }}
className="mb-2 mt-2"
onRemove={() => removeDoc(file)}
/>
))}
</div>
)}
</div>
<ChatInput.Form>
<ChatInput.Field />
{uploadAPI && <ChatInput.Upload onUpload={handleUploadFile} />}
{llamaCloudAPI && <LlamaCloudSelector />}
<ChatInput.Submit
disabled={
isLoading || (!input.trim() && files.length === 0 && !imageUrl)
}
/>
</ChatInput.Form>
</ChatInput>
);
}
@@ -0,0 +1,19 @@
import { ChatMessage } from "@llamaindex/chat-ui";
import { DeepResearchCard } from "./custom/deep-research-card";
import { ToolAnnotations } from "./tools/chat-tools";
export function ChatMessageContent() {
return (
<ChatMessage.Content>
<ChatMessage.Content.Event />
<ChatMessage.Content.AgentEvent />
<DeepResearchCard />
<ToolAnnotations />
<ChatMessage.Content.Image />
<ChatMessage.Content.Markdown />
<ChatMessage.Content.DocumentFile />
<ChatMessage.Content.Source />
<ChatMessage.Content.SuggestedQuestions />
</ChatMessage.Content>
);
}
@@ -0,0 +1,30 @@
"use client";
import { ChatMessage, ChatMessages, useChatUI } from "@llamaindex/chat-ui";
import { ChatMessageAvatar } from "./chat-avatar";
import { ChatMessageContent } from "./chat-message-content";
import { ChatStarter } from "./chat-starter";
export default function CustomChatMessages() {
const { messages } = useChatUI();
return (
<ChatMessages className="rounded-xl shadow-xl">
<ChatMessages.List>
{messages.map((message, index) => (
<ChatMessage
key={index}
message={message}
isLast={index === messages.length - 1}
>
<ChatMessageAvatar />
<ChatMessageContent />
<ChatMessage.Actions />
</ChatMessage>
))}
<ChatMessages.Loading />
</ChatMessages.List>
<ChatMessages.Actions />
<ChatStarter />
</ChatMessages>
);
}
@@ -0,0 +1,11 @@
import { useChatUI } from "@llamaindex/chat-ui";
import { StarterQuestions } from "@llamaindex/chat-ui/widgets";
import { getConfig } from "../lib/utils";
export function ChatStarter() {
const { append, messages } = useChatUI();
const starterQuestions = getConfig("STARTER_QUESTIONS") ?? [];
if (starterQuestions.length === 0 || messages.length > 0) return null;
return <StarterQuestions append={append} questions={starterQuestions} />;
}
@@ -0,0 +1,209 @@
"use client";
import { getCustomAnnotation, useChatMessage } from "@llamaindex/chat-ui";
import {
AlertCircle,
CheckCircle2,
CircleDashed,
Clock,
NotebookPen,
Search,
} from "lucide-react";
import { useMemo } from "react";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../../accordion";
import { Card, CardContent, CardHeader, CardTitle } from "../../card";
import { cn } from "../../lib/utils";
import { Markdown } from "./markdown";
// Streaming event types
type EventState = "pending" | "inprogress" | "done" | "error";
type DeepResearchEvent = {
type: "deep_research_event";
data: {
event: "retrieve" | "analyze" | "answer";
state: EventState;
id?: string;
question?: string;
answer?: string | null;
};
};
// UI state types
type QuestionState = {
id: string;
question: string;
answer: string | null;
state: EventState;
isOpen: boolean;
};
type DeepResearchCardState = {
retrieve: {
state: EventState | null;
};
analyze: {
state: EventState | null;
questions: QuestionState[];
};
};
interface DeepResearchCardProps {
className?: string;
}
const stateIcon: Record<EventState, React.ReactNode> = {
pending: <Clock className="h-4 w-4 text-yellow-500" />,
inprogress: <CircleDashed className="h-4 w-4 animate-spin text-blue-500" />,
done: <CheckCircle2 className="h-4 w-4 text-green-500" />,
error: <AlertCircle className="h-4 w-4 text-red-500" />,
};
// Transform the state based on the event without mutations
const transformState = (
state: DeepResearchCardState,
event: DeepResearchEvent,
): DeepResearchCardState => {
switch (event.data.event) {
case "answer": {
const { id, question, answer } = event.data;
if (!id || !question) return state;
const updatedQuestions = state.analyze.questions.map((q) => {
if (q.id !== id) return q;
return {
...q,
state: event.data.state,
answer: answer ?? q.answer,
};
});
const newQuestion = !state.analyze.questions.some((q) => q.id === id)
? [
{
id,
question,
answer: answer ?? null,
state: event.data.state,
isOpen: false,
},
]
: [];
return {
...state,
analyze: {
...state.analyze,
questions: [...updatedQuestions, ...newQuestion],
},
};
}
case "retrieve":
case "analyze":
return {
...state,
[event.data.event]: {
...state[event.data.event],
state: event.data.state,
},
};
default:
return state;
}
};
// Convert deep research events to state
const deepResearchEventsToState = (
events: DeepResearchEvent[] | undefined,
): DeepResearchCardState => {
if (!events?.length) {
return {
retrieve: { state: null },
analyze: { state: null, questions: [] },
};
}
const initialState: DeepResearchCardState = {
retrieve: { state: null },
analyze: { state: null, questions: [] },
};
return events.reduce(
(acc: DeepResearchCardState, event: DeepResearchEvent) =>
transformState(acc, event),
initialState,
);
};
export function DeepResearchCard({ className }: DeepResearchCardProps) {
const { message } = useChatMessage();
const state = useMemo(() => {
const deepResearchEvents = getCustomAnnotation<DeepResearchEvent>(
message.annotations,
(annotation) => annotation?.type === "deep_research_event",
);
if (!deepResearchEvents.length) return null;
return deepResearchEventsToState(deepResearchEvents);
}, [message.annotations]);
if (!state) return null;
return (
<Card className={cn("w-full", className)}>
<CardHeader className="space-y-4">
{state.retrieve.state !== null && (
<CardTitle className="flex items-center gap-2">
<Search className="h-5 w-5" />
{state.retrieve.state === "inprogress"
? "Searching..."
: "Search completed"}
</CardTitle>
)}
{state.analyze.state !== null && (
<CardTitle className="flex items-center gap-2 border-t pt-4">
<NotebookPen className="h-5 w-5" />
{state.analyze.state === "inprogress" ? "Analyzing..." : "Analysis"}
</CardTitle>
)}
</CardHeader>
<CardContent>
{state.analyze.questions.length > 0 && (
<Accordion type="single" collapsible className="space-y-2">
{state.analyze.questions.map((question: QuestionState) => (
<AccordionItem
key={question.id}
value={question.id}
className="rounded-lg border [&[data-state=open]>div]:rounded-b-none"
>
<AccordionTrigger className="hover:bg-accent gap-2 px-3 py-3 hover:no-underline">
<div className="flex w-full items-center gap-2">
<div className="flex-shrink-0">
{stateIcon[question.state]}
</div>
<span className="flex-1 text-left font-medium">
{question.question}
</span>
</div>
</AccordionTrigger>
{question.answer && (
<AccordionContent className="border-t px-3 py-3">
<Markdown content={question.answer} />
</AccordionContent>
)}
</AccordionItem>
))}
</Accordion>
)}
</CardContent>
</Card>
);
}
@@ -0,0 +1,187 @@
import { useChatUI } from "@llamaindex/chat-ui";
import { Loader2 } from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import { getConfig } from "../../lib/utils";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "../../select";
type LLamaCloudPipeline = {
id: string;
name: string;
};
type LLamaCloudProject = {
id: string;
organization_id: string;
name: string;
is_default: boolean;
pipelines: Array<LLamaCloudPipeline>;
};
type PipelineConfig = {
project: string; // project name
pipeline: string; // pipeline name
};
type LlamaCloudConfig = {
projects?: LLamaCloudProject[];
pipeline?: PipelineConfig;
};
export interface LlamaCloudSelectorProps {
onSelect?: (pipeline: PipelineConfig | undefined) => void;
defaultPipeline?: PipelineConfig;
shouldCheckValid?: boolean;
}
export function LlamaCloudSelector({
onSelect,
defaultPipeline,
shouldCheckValid = false,
}: LlamaCloudSelectorProps) {
const { setRequestData } = useChatUI();
const [config, setConfig] = useState<LlamaCloudConfig>();
const updateRequestParams = useCallback(
(pipeline?: PipelineConfig) => {
if (setRequestData) {
setRequestData({
llamaCloudPipeline: pipeline,
});
} else {
onSelect?.(pipeline);
}
},
[onSelect, setRequestData],
);
useEffect(() => {
if (process.env.NEXT_PUBLIC_USE_LLAMACLOUD === "true" && !config) {
fetch(getConfig("LLAMA_CLOUD_API"))
.then((response) => {
if (!response.ok) {
return response.json().then((errorData) => {
window.alert(
`Error: ${JSON.stringify(errorData) || "Unknown error occurred"}`,
);
});
}
return response.json();
})
.then((data) => {
const pipeline = defaultPipeline ?? data.pipeline; // defaultPipeline will override pipeline in .env
setConfig({ ...data, pipeline });
updateRequestParams(pipeline);
})
.catch((error) => console.error("Error fetching config", error));
}
}, [config, defaultPipeline, updateRequestParams]);
const setPipeline = (pipelineConfig?: PipelineConfig) => {
setConfig((prevConfig) => ({
...prevConfig,
pipeline: pipelineConfig,
}));
updateRequestParams(pipelineConfig);
};
const handlePipelineSelect = async (value: string) => {
setPipeline(JSON.parse(value) as PipelineConfig);
};
if (process.env.NEXT_PUBLIC_USE_LLAMACLOUD !== "true") {
return null;
}
if (!config) {
return (
<div className="flex items-center justify-center p-3">
<Loader2 className="h-4 w-4 animate-spin" />
</div>
);
}
if (shouldCheckValid && !isValid(config.projects, config.pipeline)) {
return (
<p className="text-red-500">
Invalid LlamaCloud configuration. Check console logs.
</p>
);
}
const { projects, pipeline } = config;
return (
<Select
onValueChange={handlePipelineSelect}
defaultValue={
isValid(projects, pipeline, false)
? JSON.stringify(pipeline)
: undefined
}
>
<SelectTrigger className="w-[200px]">
<SelectValue placeholder="Select a pipeline" />
</SelectTrigger>
<SelectContent>
{projects!.map((project: LLamaCloudProject) => (
<SelectGroup key={project.id}>
<SelectLabel className="capitalize">
Project: {project.name}
</SelectLabel>
{project.pipelines.map((pipeline) => (
<SelectItem
key={pipeline.id}
className="last:border-b"
value={JSON.stringify({
pipeline: pipeline.name,
project: project.name,
})}
>
<span className="pl-2">{pipeline.name}</span>
</SelectItem>
))}
</SelectGroup>
))}
</SelectContent>
</Select>
);
}
function isValid(
projects: LLamaCloudProject[] | undefined,
pipeline: PipelineConfig | undefined,
logErrors: boolean = true,
): boolean {
if (!projects?.length) return false;
if (!pipeline) return false;
const matchedProject = projects.find(
(project: LLamaCloudProject) => project.name === pipeline.project,
);
if (!matchedProject) {
if (logErrors) {
console.error(
`LlamaCloud project ${pipeline.project} not found. Check LLAMA_CLOUD_PROJECT_NAME variable`,
);
}
return false;
}
const pipelineExists = matchedProject.pipelines.some(
(p) => p.name === pipeline.pipeline,
);
if (!pipelineExists) {
if (logErrors) {
console.error(
`LlamaCloud pipeline ${pipeline.pipeline} not found. Check LLAMA_CLOUD_INDEX_NAME variable`,
);
}
return false;
}
return true;
}
@@ -0,0 +1,25 @@
import { SourceData } from "@llamaindex/chat-ui";
import { Markdown as MarkdownUI } from "@llamaindex/chat-ui/widgets";
import { getConfig } from "../../lib/utils";
const preprocessMedia = (content: string) => {
// Remove `sandbox:` from the beginning of the URL before rendering markdown
// OpenAI models sometimes prepend `sandbox:` to relative URLs - this fixes it
return content.replace(/(sandbox|attachment|snt):/g, "");
};
export function Markdown({
content,
sources,
}: {
content: string;
sources?: SourceData;
}) {
const processedContent = preprocessMedia(content);
return (
<MarkdownUI
content={processedContent}
backend={getConfig("BACKEND")}
sources={sources}
/>
);
}
@@ -0,0 +1,33 @@
"use client";
import * as React from "react";
export interface useCopyToClipboardProps {
timeout?: number;
}
export function useCopyToClipboard({
timeout = 2000,
}: useCopyToClipboardProps) {
const [isCopied, setIsCopied] = React.useState(false);
const copyToClipboard = (value: string) => {
if (typeof window === "undefined" || !navigator.clipboard?.writeText) {
return;
}
if (!value) {
return;
}
navigator.clipboard.writeText(value).then(() => {
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, timeout);
});
};
return { isCopied, copyToClipboard };
}
@@ -0,0 +1,96 @@
import {
Message,
MessageAnnotation,
getChatUIAnnotation,
useChatMessage,
useChatUI,
} from "@llamaindex/chat-ui";
import { JSONValue } from "ai";
import { useMemo } from "react";
import { WeatherCard, WeatherData } from "./weather-card";
export function ToolAnnotations() {
// TODO: This is a bit of a hack to get the artifact version. better to generate the version in the tool call and
// store it in CodeArtifact
const { messages } = useChatUI();
const { message } = useChatMessage();
const artifactVersion = useMemo(
() => getArtifactVersion(messages, message),
[messages, message],
);
// Get the tool data from the message annotations
const annotations = message.annotations as MessageAnnotation[] | undefined;
const toolData = annotations
? (getChatUIAnnotation(annotations, "tools") as unknown as ToolData[])
: null;
return toolData?.[0] ? (
<ChatTools data={toolData[0]} artifactVersion={artifactVersion} />
) : null;
}
// TODO: Used to render outputs of tools. If needed, add more renderers here.
function ChatTools({
data,
artifactVersion,
}: {
data: ToolData;
artifactVersion: number | undefined;
}) {
if (!data) return null;
const { toolCall, toolOutput } = data;
if (toolOutput.isError) {
return (
<div className="border-l-2 border-red-400 pl-2">
There was an error when calling the tool {toolCall.name} with input:{" "}
<br />
{JSON.stringify(toolCall.input)}
</div>
);
}
switch (toolCall.name) {
case "get_weather_information": {
const weatherData = toolOutput.output as unknown as WeatherData;
return <WeatherCard data={weatherData} />;
}
default:
return null;
}
}
type ToolData = {
toolCall: {
id: string;
name: string;
input: {
[key: string]: JSONValue;
};
};
toolOutput: {
output: JSONValue;
isError: boolean;
};
};
function getArtifactVersion(
messages: Message[],
message: Message,
): number | undefined {
const messageId = "id" in message ? message.id : undefined;
if (!messageId) return undefined;
let versionIndex = 1;
for (const m of messages) {
const toolData = m.annotations
? (getChatUIAnnotation(m.annotations, "tools") as unknown as ToolData[])
: null;
if (toolData?.some((t) => t.toolCall.name === "artifact")) {
if ("id" in m && m.id === messageId) {
return versionIndex;
}
versionIndex++;
}
}
return undefined;
}
@@ -0,0 +1,213 @@
export interface WeatherData {
latitude: number;
longitude: number;
generationtime_ms: number;
utc_offset_seconds: number;
timezone: string;
timezone_abbreviation: string;
elevation: number;
current_units: {
time: string;
interval: string;
temperature_2m: string;
weather_code: string;
};
current: {
time: string;
interval: number;
temperature_2m: number;
weather_code: number;
};
hourly_units: {
time: string;
temperature_2m: string;
weather_code: string;
};
hourly: {
time: string[];
temperature_2m: number[];
weather_code: number[];
};
daily_units: {
time: string;
weather_code: string;
};
daily: {
time: string[];
weather_code: number[];
};
}
// Follow WMO Weather interpretation codes (WW)
const weatherCodeDisplayMap: Record<
string,
{
icon: React.ReactNode;
status: string;
}
> = {
"0": {
icon: <span></span>,
status: "Clear sky",
},
"1": {
icon: <span>🌤</span>,
status: "Mainly clear",
},
"2": {
icon: <span></span>,
status: "Partly cloudy",
},
"3": {
icon: <span></span>,
status: "Overcast",
},
"45": {
icon: <span>🌫</span>,
status: "Fog",
},
"48": {
icon: <span>🌫</span>,
status: "Depositing rime fog",
},
"51": {
icon: <span>🌧</span>,
status: "Drizzle",
},
"53": {
icon: <span>🌧</span>,
status: "Drizzle",
},
"55": {
icon: <span>🌧</span>,
status: "Drizzle",
},
"56": {
icon: <span>🌧</span>,
status: "Freezing Drizzle",
},
"57": {
icon: <span>🌧</span>,
status: "Freezing Drizzle",
},
"61": {
icon: <span>🌧</span>,
status: "Rain",
},
"63": {
icon: <span>🌧</span>,
status: "Rain",
},
"65": {
icon: <span>🌧</span>,
status: "Rain",
},
"66": {
icon: <span>🌧</span>,
status: "Freezing Rain",
},
"67": {
icon: <span>🌧</span>,
status: "Freezing Rain",
},
"71": {
icon: <span></span>,
status: "Snow fall",
},
"73": {
icon: <span></span>,
status: "Snow fall",
},
"75": {
icon: <span></span>,
status: "Snow fall",
},
"77": {
icon: <span></span>,
status: "Snow grains",
},
"80": {
icon: <span>🌧</span>,
status: "Rain showers",
},
"81": {
icon: <span>🌧</span>,
status: "Rain showers",
},
"82": {
icon: <span>🌧</span>,
status: "Rain showers",
},
"85": {
icon: <span></span>,
status: "Snow showers",
},
"86": {
icon: <span></span>,
status: "Snow showers",
},
"95": {
icon: <span></span>,
status: "Thunderstorm",
},
"96": {
icon: <span></span>,
status: "Thunderstorm",
},
"99": {
icon: <span></span>,
status: "Thunderstorm",
},
};
const displayDay = (time: string) => {
return new Date(time).toLocaleDateString("en-US", {
weekday: "long",
});
};
export function WeatherCard({ data }: { data: WeatherData }) {
const currentDayString = new Date(data.current.time).toLocaleDateString(
"en-US",
{
weekday: "long",
month: "long",
day: "numeric",
},
);
return (
<div className="w-fit space-y-4 rounded-2xl bg-[#61B9F2] p-5 text-white shadow-xl">
<div className="flex justify-between">
<div className="space-y-2">
<div className="text-xl">{currentDayString}</div>
<div className="flex gap-4 text-5xl font-semibold">
<span>
{data.current.temperature_2m} {data.current_units.temperature_2m}
</span>
{weatherCodeDisplayMap[data.current.weather_code].icon}
</div>
</div>
<span className="text-xl">
{weatherCodeDisplayMap[data.current.weather_code].status}
</span>
</div>
<div className="grid grid-cols-6 gap-2">
{data.daily.time.map((time, index) => {
if (index === 0) return null; // skip the current day
return (
<div key={time} className="flex flex-col items-center gap-4">
<span>{displayDay(time)}</span>
<div className="text-4xl">
{weatherCodeDisplayMap[data.daily.weather_code[index]].icon}
</div>
<span className="text-sm">
{weatherCodeDisplayMap[data.daily.weather_code[index]].status}
</span>
</div>
);
})}
</div>
</div>
);
}
@@ -0,0 +1,11 @@
"use client";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
const Collapsible = CollapsiblePrimitive.Root;
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
@@ -0,0 +1,13 @@
"use client";
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export const getConfig = (name: string) => {
if (typeof window === "undefined") return undefined;
return (window as any).LLAMAINDEX?.[name];
};
@@ -0,0 +1,159 @@
"use client";
import * as SelectPrimitive from "@radix-ui/react-select";
import { Check, ChevronDown, ChevronUp } from "lucide-react";
import * as React from "react";
import { cn } from "./lib/utils";
const Select = SelectPrimitive.Root;
const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"bg-popover text-popover-foreground 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-[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 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-md",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm",
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("bg-muted -mx-1 my-1 h-px", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectScrollDownButton,
SelectScrollUpButton,
SelectSeparator,
SelectTrigger,
SelectValue,
};
@@ -0,0 +1,54 @@
"use client";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import * as React from "react";
import { cn } from "./lib/utils";
const Tabs = TabsPrimitive.Root;
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"bg-muted text-muted-foreground inline-flex h-9 items-center justify-center rounded-lg p-1",
className,
)}
{...props}
/>
));
TabsList.displayName = TabsPrimitive.List.displayName;
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"ring-offset-background focus-visible:outline-hidden focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm",
className,
)}
{...props}
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"ring-offset-background focus-visible:outline-hidden focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2",
className,
)}
{...props}
/>
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsContent, TabsList, TabsTrigger };
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+112 -53
View File
@@ -45,12 +45,6 @@
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--background-image-glow-conic:
radial-gradient(at 21% 11%, rgba(186, 186, 233, 0.53) 0, transparent 50%),
radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%),
radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%),
radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%);
@keyframes accordion-down {
from {
height: 0;
@@ -69,6 +63,17 @@
}
}
@utility container {
margin-inline: auto;
padding-inline: 2rem;
@media (width >= --theme(--breakpoint-sm)) {
max-width: none;
}
@media (width >= 1400px) {
max-width: 1400px;
}
}
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
@@ -90,57 +95,68 @@
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 10% 3.9%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
--radius: 0.5rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%;
--radius: 0.5rem;
}
}
@@ -148,7 +164,50 @@
* {
@apply border-border;
}
html {
@apply h-full;
}
body {
@apply bg-background text-foreground;
@apply bg-background text-foreground h-full;
font-feature-settings:
"rlig" 1,
"calt" 1;
}
.background-gradient {
background-color: #fff;
background-image:
radial-gradient(at 21% 11%, rgba(186, 186, 233, 0.53) 0, transparent 50%),
radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%),
radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%),
radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%);
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.fadein-agent {
animation-name: fadeIn;
animation-duration: 1.5s;
}
@keyframes slideIn {
from {
transform: translateX(10%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.animate-slideIn {
animation: slideIn 0.5s ease-out;
}
}
+8 -5
View File
@@ -1,19 +1,22 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "LlamaIndex Server",
description: "LlamaIndex Server UI",
title: "Create Llama App",
description: "Generated by create-llama",
};
export default function RootLayout({
children,
}: Readonly<{
}: {
children: React.ReactNode;
}>) {
}) {
return (
<html lang="en">
<body>{children}</body>
<body className={inter.className}>{children}</body>
<script async src="./config.js"></script>
</html>
);
+22 -24
View File
@@ -1,29 +1,27 @@
"use client";
import { ChatSection } from "@llamaindex/chat-ui";
import { useChat } from "ai/react";
import { getConfig } from "./utils";
export default function Page() {
const handler = useChat({
api: getConfig("CHAT_API"),
onError: (error: unknown) => {
if (!(error instanceof Error)) throw error;
let errorMessage: string;
try {
errorMessage = JSON.parse(error.message).detail;
} catch (e) {
console.error(e);
errorMessage = error.message;
}
alert(errorMessage);
},
});
return (
<div className="flex h-screen items-center justify-center">
<ChatSection
className="h-[72vh] w-[72vw] rounded-2xl shadow-2xl"
handler={handler}
/>
import Header from "@/app/components/header";
import { Loader2 } from "lucide-react";
import dynamic from "next/dynamic";
const ChatSection = dynamic(() => import("./components/chat-section"), {
ssr: false,
loading: () => (
<div className="flex h-full w-full items-center justify-center">
<Loader2 className="text-muted-foreground animate-spin" />
</div>
),
});
export default function Home() {
return (
<main className="background-gradient flex h-screen w-screen items-center justify-center">
<div className="w-[90%] space-y-2 lg:w-[60rem] lg:space-y-10">
<Header />
<div className="flex h-[65vh]">
<ChatSection />
</div>
</div>
</main>
);
}
-7
View File
@@ -1,7 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";
export const getConfig = (name: string) => {
if (typeof window === "undefined") return undefined;
return (window as any).LLAMAINDEX?.[name];
};
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/app/components",
"utils": "@/app/components/ui/lib/utils",
"ui": "@/app/components/ui",
"lib": "@/app/components/ui/lib",
"hooks": "@/app/components/ui/hooks"
},
"iconLibrary": "lucide"
}
-16
View File
@@ -1,16 +0,0 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;
+5
View File
@@ -0,0 +1,5 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
-5
View File
@@ -1,5 +0,0 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
+4
View File
@@ -2,4 +2,8 @@
window.LLAMAINDEX = {
CHAT_API: "/api/chat",
// BACKEND: "http://localhost:3000",
// UPLOAD_API: "/api/chat/upload",
// LLAMA_CLOUD_API: "/api/chat/config/llamacloud",
// STARTER_QUESTIONS: [],
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+3 -9
View File
@@ -1,6 +1,5 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
@@ -20,14 +19,9 @@
],
"paths": {
"@/*": ["./*"]
}
},
"target": "ES2017"
},
"include": [
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"next-env.d.ts",
"../.next/types/**/*.ts"
],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
+13 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/server",
"description": "LlamaIndex Server",
"version": "0.0.2",
"version": "0.0.3",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
@@ -50,12 +50,20 @@
"tsx": "^4.19.3"
},
"dependencies": {
"@llamaindex/workflow": "workspace:*",
"@llamaindex/core": "workspace:*",
"@llamaindex/chat-ui": "0.3.1",
"ai": "^4.2.0",
"llamaindex": "workspace:*",
"next": "15.2.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.2.3"
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.0.2",
"@llamaindex/chat-ui": "0.3.2",
"class-variance-authority": "^0.7.1",
"tailwind-merge": "^2.6.0",
"clsx": "^2.1.1",
"lucide-react": "^0.460.0"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
import type { ChatMessage } from "@llamaindex/core/llms";
import { type Message } from "ai";
import { IncomingMessage, ServerResponse } from "http";
import type { ChatMessage } from "llamaindex";
import type { WorkflowFactory } from "../types";
import {
parseRequestBody,
+21 -3
View File
@@ -1,3 +1,4 @@
import fs from "fs";
import { createServer } from "http";
import next from "next";
import path from "path";
@@ -6,17 +7,34 @@ import { handleChat } from "./handlers/chat";
import { handleServeFiles } from "./handlers/files";
import type { LlamaIndexServerOptions, ServerWorkflow } from "./types";
const nextDir = path.join(__dirname, "..", "server");
const configFile = path.join(__dirname, "..", "server", "public", "config.js");
const dev = process.env.NODE_ENV !== "production";
export class LlamaIndexServer {
port: number;
app: ReturnType<typeof next>;
workflowFactory: () => Promise<ServerWorkflow> | ServerWorkflow;
constructor({ workflow, ...nextAppOptions }: LlamaIndexServerOptions) {
const nextDir = path.join(__dirname, "../server");
const dev = process.env.NODE_ENV !== "production";
this.app = next({ ...nextAppOptions, dev, dir: nextDir });
this.app = next({ dev, dir: nextDir, ...nextAppOptions });
this.port = nextAppOptions.port ?? 3000;
this.workflowFactory = workflow;
this.modifyConfig(nextAppOptions);
}
private modifyConfig(
options: Pick<LlamaIndexServerOptions, "starterQuestions">,
) {
// content in javascript format
const content = `
window.LLAMAINDEX = {
CHAT_API: '/api/chat',
STARTER_QUESTIONS: ${JSON.stringify(options.starterQuestions ?? [])}
}
`;
fs.writeFileSync(configFile, content);
}
async start() {
+8 -3
View File
@@ -1,5 +1,9 @@
import { Workflow, type AgentWorkflowContext } from "@llamaindex/workflow";
import type { AgentInputData, AgentWorkflow } from "@llamaindex/workflow/agent";
import type {
AgentInputData,
AgentWorkflow,
AgentWorkflowContext,
Workflow,
} from "llamaindex";
import type next from "next";
/**
@@ -18,8 +22,9 @@ export type WorkflowFactory = (
requestBody?: any,
) => Promise<ServerWorkflow> | ServerWorkflow;
export type NextAppOptions = Omit<Parameters<typeof next>[0], "dir">;
export type NextAppOptions = Parameters<typeof next>[0];
export type LlamaIndexServerOptions = NextAppOptions & {
workflow: WorkflowFactory;
starterQuestions?: string[];
};
+62
View File
@@ -0,0 +1,62 @@
import type { StreamData } from "ai";
import { type ChatMessage, Settings } from "llamaindex";
const NEXT_QUESTION_PROMPT = `You're a helpful assistant! Your task is to suggest the next question that user might ask.
Here is the conversation history
---------------------
{conversation}
---------------------
Given the conversation history, please give me 3 questions that user might ask next!
Your answer should be wrapped in three sticks which follows the following format:
\`\`\`
<question 1>
<question 2>
<question 3>
\`\`\`
`;
export const sendSuggestedQuestionsEvent = async (
dataStream: StreamData,
chatHistory: ChatMessage[] = [],
) => {
const questions = await generateNextQuestions(chatHistory);
if (questions.length > 0) {
dataStream.appendMessageAnnotation({
type: "suggested_questions",
data: questions,
});
}
};
async function generateNextQuestions(conversation: ChatMessage[]) {
const conversationText = conversation
.map((message) => `${message.role}: ${message.content}`)
.join("\n");
const message = NEXT_QUESTION_PROMPT.replace(
"{conversation}",
conversationText,
);
try {
const response = await Settings.llm.complete({ prompt: message });
const questions = extractQuestions(response.text);
return questions;
} catch (error) {
console.error("Error when generating the next questions: ", error);
return [];
}
}
function extractQuestions(text: string): string[] {
// Extract the text inside the triple backticks
const contentMatch = text.match(/```(.*?)```/s);
const content = contentMatch?.[1] ?? "";
// Split the content by newlines to get each question
const questions = content
.split("\n")
.map((question) => question.trim())
.filter((question) => question !== "");
return questions;
}
+34 -14
View File
@@ -1,18 +1,19 @@
import type { ChatResponseChunk } from "@llamaindex/core/llms";
import type { EngineResponse } from "@llamaindex/core/schema";
import {
StopEvent,
Workflow,
type AgentWorkflowContext,
} from "@llamaindex/workflow";
import { LlamaIndexAdapter, StreamData, type JSONValue } from "ai";
import type {
AgentInputData,
ChatResponseChunk,
EngineResponse,
} from "llamaindex";
import {
AgentStream,
AgentWorkflow,
type AgentInputData,
} from "@llamaindex/workflow/agent";
import { LlamaIndexAdapter, StreamData, type JSONValue } from "ai";
StopEvent,
Workflow,
type AgentWorkflowContext,
} from "llamaindex";
import { ReadableStream } from "stream/web";
import type { ServerWorkflow } from "../types";
import { sendSuggestedQuestionsEvent } from "./suggestion";
export async function runWorkflow(
workflow: ServerWorkflow,
@@ -47,11 +48,19 @@ async function runAgentWorkflow(
}
}
controller.close();
dataStream.close();
},
});
return LlamaIndexAdapter.toDataStreamResponse(stream, { data: dataStream });
return LlamaIndexAdapter.toDataStreamResponse(stream, {
data: dataStream,
callbacks: {
onFinal: async (content: string) => {
const history = chatHistory.concat({ role: "assistant", content });
await sendSuggestedQuestionsEvent(dataStream, history);
dataStream.close();
},
},
});
}
async function runCustomWorkflow(
@@ -77,9 +86,20 @@ async function runCustomWorkflow(
}
}
controller.close();
dataStream.close();
},
});
return LlamaIndexAdapter.toDataStreamResponse(stream, { data: dataStream });
return LlamaIndexAdapter.toDataStreamResponse(stream, {
data: dataStream,
callbacks: {
onFinal: async (content: string) => {
const history = agentInput.chatHistory?.concat({
role: "assistant",
content,
});
await sendSuggestedQuestionsEvent(dataStream, history);
dataStream.close();
},
},
});
}
+39 -23
View File
@@ -1767,17 +1767,38 @@ importers:
packages/server:
dependencies:
'@llamaindex/chat-ui':
specifier: 0.3.1
version: 0.3.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@llamaindex/core':
specifier: workspace:*
version: link:../core
'@llamaindex/workflow':
specifier: workspace:*
version: link:../workflow
specifier: 0.3.2
version: 0.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-accordion':
specifier: ^1.2.2
version: 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-collapsible':
specifier: ^1.0.3
version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-select':
specifier: ^2.1.1
version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-slot':
specifier: ^1.0.2
version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
'@radix-ui/react-tabs':
specifier: ^1.1.0
version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
ai:
specifier: ^4.2.0
version: 4.2.0(react@19.0.0)(zod@3.24.2)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
clsx:
specifier: ^2.1.1
version: 2.1.1
llamaindex:
specifier: workspace:*
version: link:../llamaindex
lucide-react:
specifier: ^0.460.0
version: 0.460.0(react@19.0.0)
next:
specifier: 15.2.3
version: 15.2.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -1787,6 +1808,9 @@ importers:
react-dom:
specifier: ^19.0.0
version: 19.0.0(react@19.0.0)
tailwind-merge:
specifier: ^2.6.0
version: 2.6.0
devDependencies:
'@eslint/eslintrc':
specifier: ^3
@@ -2421,10 +2445,6 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/runtime@7.26.7':
resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.26.9':
resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==}
engines: {node: '>=6.9.0'}
@@ -3714,8 +3734,8 @@ packages:
peerDependencies:
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
'@llamaindex/chat-ui@0.3.1':
resolution: {integrity: sha512-sF6axN9LviewAxvBbqkF3u3K0yvIt74prio7uiVruFVT/AYkRlIk721QXTPBscf+ZvyzAqjh0Nx0BoGiZUzBCw==}
'@llamaindex/chat-ui@0.3.2':
resolution: {integrity: sha512-YcQOghcxutqHK9KO2CRSws0inDR5bbMZkmpUFJtC2aWcHjWi8wYbzVZjRVl1vrb3VCk+VInKOhFUTW9hEkzydA==}
peerDependencies:
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
@@ -13676,10 +13696,6 @@ snapshots:
'@babel/core': 7.26.8
'@babel/helper-plugin-utils': 7.26.5
'@babel/runtime@7.26.7':
dependencies:
regenerator-runtime: 0.14.1
'@babel/runtime@7.26.9':
dependencies:
regenerator-runtime: 0.14.1
@@ -14934,7 +14950,7 @@ snapshots:
- react-dom
- supports-color
'@llamaindex/chat-ui@0.3.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
'@llamaindex/chat-ui@0.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@llamaindex/pdf-viewer': 1.3.0(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-collapsible': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -14981,14 +14997,14 @@ snapshots:
'@manypkg/find-root@1.1.0':
dependencies:
'@babel/runtime': 7.26.7
'@babel/runtime': 7.26.9
'@types/node': 12.20.55
find-up: 4.1.0
fs-extra: 8.1.0
'@manypkg/get-packages@1.1.3':
dependencies:
'@babel/runtime': 7.26.7
'@babel/runtime': 7.26.9
'@changesets/types': 4.1.0
'@manypkg/find-root': 1.1.0
fs-extra: 8.1.0
@@ -19416,7 +19432,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -19467,7 +19483,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.22.0(jiti@2.4.2)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
+7
View File
@@ -1,5 +1,12 @@
# @llamaindex/unit-test
## 0.1.13
### Patch Changes
- Updated dependencies [75d6e29]
- llamaindex@0.9.13
## 0.1.12
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/unit-test",
"private": true,
"version": "0.1.12",
"version": "0.1.13",
"type": "module",
"scripts": {
"test": "vitest run"