Compare commits

...

33 Commits

Author SHA1 Message Date
github-actions[bot] 85039a5360 Release @llamaindex/tools@0.1.0 (#2034) 2025-06-24 12:32:24 +07:00
Marcus Schiesser d7305edb53 fix changesets 2025-06-24 12:26:09 +07:00
Huu Le 096bf2bda1 feat: Add support for StreamableHTTP MCP Client (#2032) 2025-06-24 11:40:34 +07:00
jerinthomascarmel c5846bd7dc feat(readers): add XMLReader for parsing XML files (#1846) (#2031)
Co-authored-by: Marcus Schiesser <marcus.schiesser@googlemail.com>
2025-06-24 10:46:32 +07:00
github-actions[bot] 97bbce6e13 Release 0.11.9 (#2023)
Co-authored-by: marcusschiesser <17126+marcusschiesser@users.noreply.github.com>
2025-06-20 12:28:01 +07:00
Marcus Schiesser 62699b7497 chore: improve performance of sentence splitter (#2030) 2025-06-20 12:16:24 +07:00
Broda Noel a89e187796 Add extraAbbreviations on sentence-splitter (#2029)
Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
2025-06-20 11:27:06 +07:00
ANKIT VARSHNEY d8ac8d385d feat: add openai realtime api (#2006)
Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
2025-06-20 10:22:04 +07:00
Marcus Schiesser a6cef9c6be chore: no core in examples (#2024) 2025-06-18 09:39:32 +07:00
Broda Noel c5b2691302 Add more Acronyms on SentenceSplitter (#2022)
Co-authored-by: Marcus Schiesser <marcus.schiesser@googlemail.com>
2025-06-17 10:43:36 +07:00
github-actions[bot] 8122c7245e Release 0.11.8 (#2018)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: marcusschiesser <17126+marcusschiesser@users.noreply.github.com>
2025-06-12 16:20:58 +07:00
Huu Le 8a51c167f8 feat: use agent to handle a workflow step (#2014)
Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
2025-06-12 16:06:13 +07:00
Marcus Schiesser 1b5af1402d fix: jsonToNode for image nodes (#2017) 2025-06-12 11:59:05 +07:00
github-actions[bot] fffe93fac8 Release 0.11.7 (#2013)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: marcusschiesser <17126+marcusschiesser@users.noreply.github.com>
2025-06-12 10:34:24 +07:00
Marcus Schiesser dbd857f6b5 chore: add changeset 2025-06-11 16:20:32 +07:00
정물결 a4d394f727 fix: correct SimpleDirectoryReader import path (#2011) 2025-06-10 12:43:01 +07:00
Marcus Schiesser 3c857f4132 chore: move ajv to dev deps (#2012) 2025-06-10 12:20:54 +07:00
Thuc Pham 36cfb93eb2 feat: export snapshot apis from llama-flow (#2009) 2025-06-10 11:56:33 +07:00
github-actions[bot] ab4762f026 Release (#2005)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-06 14:45:39 +07:00
Peter Goldstein 56763dc57d Update to the latest Gemini 2.5 Pro Preview key (#2004) 2025-06-06 11:25:41 +07:00
github-actions[bot] 5375fdd704 Release (#2003)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: marcusschiesser <17126+marcusschiesser@users.noreply.github.com>
2025-06-05 09:57:35 +07:00
Marcus Schiesser e7484efca5 feat: weaviate: Add metadata sanitization before adding node. Add err… (#2001) 2025-06-04 11:48:18 +07:00
Marcus Schiesser c958a1645a docs: update chat-ui (#2002) 2025-06-03 17:01:07 +07:00
github-actions[bot] 0140a257c4 Release 0.11.6 (#1999)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: marcusschiesser <17126+marcusschiesser@users.noreply.github.com>
2025-06-02 18:03:31 +07:00
GhosT 40161fe8d2 chore: Bump @llama-flow/core package version (#1998)
Co-authored-by: Marcus Schiesser <marcus.schiesser@googlemail.com>
2025-06-02 17:28:47 +07:00
github-actions[bot] d883fe7351 Release @llamaindex/google@0.3.7 (#1994)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-05-31 14:04:14 +07:00
Parham Saidi 2bc6914784 fix: ignore empty parts for gemini which confuses agent (#1993) 2025-05-30 22:47:21 +07:00
github-actions[bot] 78fbec17a6 Release 0.11.5 (#1986)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: marcusschiesser <17126+marcusschiesser@users.noreply.github.com>
2025-05-30 22:37:26 +07:00
Marcus Schiesser 8b10a2e880 docs: add chat-ui docs (#1992) 2025-05-30 16:56:47 +07:00
ANKIT VARSHNEY 534662368f fix(google): use api key provided by the user in the session store (#1989) 2025-05-30 11:53:54 +07:00
Marcus Schiesser b370bd59f1 docs: fix agent docs (#1988) 2025-05-29 11:38:11 +07:00
Huu Le 766054ba67 chore: remove log input to avoid confusing (#1987) 2025-05-28 17:40:03 +07:00
ANKIT VARSHNEY 71598f86d7 feat: add support for interrupted and other server content event in live api (#1980)
Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
2025-05-28 15:18:56 +07:00
210 changed files with 6499 additions and 2714 deletions
+70
View File
@@ -1,5 +1,75 @@
# @llamaindex/doc
## 0.2.29
### Patch Changes
- Updated dependencies [c5846bd]
- @llamaindex/readers@3.1.10
## 0.2.28
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
- @llamaindex/openai@0.4.5
- @llamaindex/cloud@4.0.15
- llamaindex@0.11.9
- @llamaindex/node-parser@2.0.11
- @llamaindex/readers@3.1.9
- @llamaindex/workflow@1.1.10
## 0.2.27
### Patch Changes
- 8a51c16: Add natural language agent page
- Updated dependencies [8a51c16]
- Updated dependencies [1b5af14]
- @llamaindex/workflow@1.1.9
- @llamaindex/core@0.6.10
- llamaindex@0.11.8
- @llamaindex/cloud@4.0.14
- @llamaindex/node-parser@2.0.10
- @llamaindex/openai@0.4.4
- @llamaindex/readers@3.1.8
## 0.2.26
### Patch Changes
- a4d394f: fix: correct SimpleDirectoryReader import path in documentation example
- Updated dependencies [dbd857f]
- Updated dependencies [3c857f4]
- @llamaindex/workflow@1.1.8
- llamaindex@0.11.7
## 0.2.25
### Patch Changes
- Updated dependencies [40161fe]
- @llamaindex/workflow@1.1.7
- llamaindex@0.11.6
## 0.2.24
### Patch Changes
- Updated dependencies [766054b]
- Updated dependencies [71598f8]
- @llamaindex/workflow@1.1.6
- @llamaindex/core@0.6.9
- llamaindex@0.11.5
- @llamaindex/cloud@4.0.13
- @llamaindex/node-parser@2.0.9
- @llamaindex/openai@0.4.3
- @llamaindex/readers@3.1.7
## 0.2.23
### Patch Changes
+14
View File
@@ -15,6 +15,20 @@ const config = {
"twoslash",
"typescript",
],
async redirects() {
return [
{
source: "/docs/chat-ui/:path*.mdx",
destination: "/docs/chat-ui/:path*",
permanent: true,
},
{
source: "/docs/llamaflow/:path*.mdx",
destination: "/docs/llamaflow/:path*",
permanent: true,
},
];
},
turbopack: {
resolveAlias: {
fs: { browser: "./fallback.js" },
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/doc",
"version": "0.2.23",
"version": "0.2.29",
"private": true,
"scripts": {
"postinstall": "fumadocs-mdx",
@@ -16,7 +16,7 @@
"@huggingface/transformers": "^3.5.0",
"@icons-pack/react-simple-icons": "^10.1.0",
"@llama-flow/docs": "0.0.8",
"@llamaindex/chat-ui": "0.2.0",
"@llamaindex/chat-ui-docs": "^0.0.5",
"@llamaindex/cloud": "workspace:*",
"@llamaindex/core": "workspace:*",
"@llamaindex/node-parser": "workspace:*",
@@ -39,13 +39,13 @@
"clsx": "2.1.1",
"foxact": "^0.2.41",
"framer-motion": "^11.11.17",
"fumadocs-core": "^15.2.7",
"fumadocs-core": "^15.5.0",
"fumadocs-docgen": "^2.0.0",
"fumadocs-mdx": "^11.6.0",
"fumadocs-openapi": "^8.0.1",
"fumadocs-twoslash": "^3.1.1",
"fumadocs-typescript": "^4.0.2",
"fumadocs-ui": "^15.2.7",
"fumadocs-mdx": "^11.6.6",
"fumadocs-openapi": "^9.0.5",
"fumadocs-twoslash": "^3.1.3",
"fumadocs-typescript": "^4.0.5",
"fumadocs-ui": "^15.5.0",
"hast-util-to-jsx-runtime": "^2.3.2",
"llamaindex": "workspace:*",
"lucide-react": "^0.460.0",
+1 -1
View File
@@ -13,7 +13,7 @@ const INTERNAL_LINK_REGEX = /(?:(?:\]\(|\bhref=["'])\/docs\/([^")]+))/g;
// This captures relative links like [text](./path) or ![alt](../images/image.png)
const RELATIVE_LINK_REGEX = /(?:\]\()(?:\s*)(?:\.\.?)\//g;
const ALLOWED_LINKS = ["/docs/llamaflow"];
const ALLOWED_LINKS = ["/docs/llamaflow", "/docs/chat-ui"];
interface LinkValidationResult {
file: string;
+10 -1
View File
@@ -9,7 +9,16 @@ import rehypeKatex from "rehype-katex";
import remarkMath from "remark-math";
export const docs = defineDocs({
dir: ["./src/content/docs", "./node_modules/@llama-flow/docs"],
dir: [
"./src/content/docs",
"./node_modules/@llama-flow/docs",
"./node_modules/@llamaindex/chat-ui-docs",
// NOTE: When adding external docs (like chat-ui or llama-flow above),
// make sure to also update:
// 1. scripts/validate-links.mts - add to ALLOWED_LINKS array
// 2. next.config.mjs - add redirect for .mdx files
// 3. src/content/docs/meta.json - add to pages array
],
docs: {
async: true,
},
+2 -1
View File
@@ -113,7 +113,8 @@ export default function HomePage() {
description="Truly powerful retrieval-augmented generation applications use agentic techniques, and LlamaIndex.TS makes it easy to build them."
>
<CodeBlock
code={`import { SimpleDirectoryReader, VectorStoreIndex } from "llamaindex";
code={`import { VectorStoreIndex } from "llamaindex";
import { SimpleDirectoryReader } from "@llamaindex/readers/directory";
import { openai } from "@llamaindex/openai";
import { agent } from "@llamaindex/workflow";
@@ -1,4 +1,3 @@
import { ChatDemoRSC } from "@/components/demo/chat/rsc/demo";
import * as demos from "@/components/demo/lazy";
import { createMetadata, metadataImage } from "@/lib/metadata";
import { openapi, source } from "@/lib/source";
@@ -51,7 +50,6 @@ export default async function Page(props: {
...Icons,
...defaultMdxComponents,
...demos,
ChatDemoRSC,
Accordion,
Accordions,
APIPage: (props) => <APIPage {...openapi.getAPIPageProps(props)} />,
@@ -1,21 +0,0 @@
"use client";
import {
ChatHandler,
ChatInput,
ChatMessages,
ChatSection,
} from "@llamaindex/chat-ui";
import { useChat } from "ai/react";
export const ChatDemo = () => {
const handler = useChat();
return (
<ChatSection handler={handler as ChatHandler}>
<ChatMessages>
<ChatMessages.List className="h-auto max-h-[400px]" />
<ChatMessages.Actions />
</ChatMessages>
<ChatInput />
</ChatSection>
);
};
@@ -1,57 +0,0 @@
import { Markdown } from "@llamaindex/chat-ui/widgets";
import { MockLLM } from "@llamaindex/core/utils";
import { generateId, Message } from "ai";
import { createAI, createStreamableUI, getMutableAIState } from "ai/rsc";
import { type ChatMessage, Settings, SimpleChatEngine } from "llamaindex";
import { ReactNode } from "react";
type ServerState = Message[];
type FrontendState = Array<Message & { display: ReactNode }>;
type Actions = {
chat: (message: Message) => Promise<Message & { display: ReactNode }>;
};
Settings.llm = new MockLLM(); // config your LLM here
export const AI = createAI<ServerState, FrontendState, Actions>({
initialAIState: [],
initialUIState: [],
actions: {
chat: async (message: Message) => {
"use server";
const aiState = getMutableAIState<typeof AI>();
aiState.update((prev) => [...prev, message]);
const uiStream = createStreamableUI();
const chatEngine = new SimpleChatEngine();
const assistantMessage: Message = {
id: generateId(),
role: "assistant",
content: "",
};
// run the async function without blocking
(async () => {
const chatResponse = await chatEngine.chat({
stream: true,
message: message.content,
chatHistory: aiState.get() as ChatMessage[],
});
for await (const chunk of chatResponse) {
assistantMessage.content += chunk.delta;
uiStream.update(<Markdown content={assistantMessage.content} />);
}
aiState.done([...aiState.get(), assistantMessage]);
uiStream.done();
})();
return {
...assistantMessage,
display: uiStream.value,
};
},
},
});
@@ -1,35 +0,0 @@
"use client";
import {
ChatHandler,
ChatInput,
ChatMessage,
ChatMessages,
ChatSection as ChatSectionUI,
Message,
} from "@llamaindex/chat-ui";
import { useChatRSC } from "./use-chat-rsc";
export const ChatSectionRSC = () => {
const handler = useChatRSC();
return (
<ChatSectionUI handler={handler as ChatHandler}>
<ChatMessages>
<ChatMessages.List className="h-auto max-h-[400px]">
{handler.messages.map((message, index) => (
<ChatMessage
key={index}
message={message as Message}
isLast={index === handler.messages.length - 1}
>
<ChatMessage.Avatar />
<ChatMessage.Content>{message.display}</ChatMessage.Content>
</ChatMessage>
))}
<ChatMessages.Loading />
</ChatMessages.List>
</ChatMessages>
<ChatInput />
</ChatSectionUI>
);
};
@@ -1,8 +0,0 @@
import { AI } from "./ai-action";
import { ChatSectionRSC } from "./chat-section";
export const ChatDemoRSC = () => (
<AI>
<ChatSectionRSC />
</AI>
);
@@ -1,41 +0,0 @@
"use client";
import { useActions } from "ai/rsc";
import { generateId, Message } from "ai";
import { useUIState } from "ai/rsc";
import { useState } from "react";
import { AI } from "./ai-action";
export function useChatRSC() {
const [input, setInput] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [messages, setMessages] = useUIState<typeof AI>();
const { chat } = useActions<typeof AI>();
const append = async (message: Omit<Message, "id">) => {
const newMsg: Message = { ...message, id: generateId() };
setIsLoading(true);
try {
setMessages((prev) => [...prev, { ...newMsg, display: message.content }]);
const assistantMsg = await chat(newMsg);
setMessages((prev) => [...prev, assistantMsg]);
} catch (error) {
console.error(error);
}
setIsLoading(false);
setInput("");
return message.content;
};
return {
input,
setInput,
isLoading,
messages,
setMessages,
append,
};
}
-5
View File
@@ -1,11 +1,6 @@
"use client";
import dynamic from "next/dynamic";
// lazy load client components
export const ChatDemo = dynamic(() =>
import("@/components/demo/chat/api/demo").then((mod) => mod.ChatDemo),
);
export const CodeNodeParserDemo = dynamic(() =>
import("@/components/demo/code-node-parser").then(
(mod) => mod.CodeNodeParserDemo,
@@ -33,7 +33,7 @@ const jokeAgent = agent({
// Run the workflow
const result = await jokeAgent.run("Tell me something funny");
console.log(result); // Baby Llama is called cria
console.log(result.data.result); // Baby Llama is called cria
```
### Event Streaming
@@ -44,7 +44,7 @@ Agent Workflows provide a unified interface for event streaming, making it easy
import { agentToolCallEvent, agentStreamEvent } from "@llamaindex/workflow";
// Get the workflow execution context
const events = workflow.runStream("Tell me something funny");
const events = jokeAgent.runStream("Tell me something funny");
// Stream and handle events
for await (const event of events) {
@@ -112,6 +112,7 @@ const agents = multiAgent({
const result = await agents.run(
"Give me a morning greeting with a joke and the weather in San Francisco"
);
console.log(result.data.result);
```
The workflow will coordinate between agents, allowing them to handle different aspects of the request and hand off tasks when appropriate.
@@ -1,4 +1,4 @@
{
"title": "Agents",
"pages": ["tool", "agent_workflow", "workflows"]
"pages": ["tool", "agent_workflow", "workflows", "natural_language_workflow"]
}
@@ -0,0 +1,103 @@
---
title: Define workflows using natural language
---
When working with Workflows, you have to write code to handle an event in the workflow.
Often, the logic of the handler is not too complex so that it can be expressed using natural language and executed by an LLM.
Besides the instructions, we just need the expected result event of the step, possible tool calls and optionally other events that can be emitted.
## Usage
Let's take an example of a workflow that generates a joke, gets a critique for it, and then improves it.
### Define the events
First, we define the events for our workflow. We need one for writing the joke, one for critiquing it, and one for the final result:
```typescript
import { z } from "zod";
import { zodEvent } from "@llamaindex/workflow";
const writeJokeSchema = z.object({
description: z
.string()
.describe("The topic to write a joke or describe the joke to improve."),
writtenJoke: z.optional(z.string()).describe("The written joke."),
retriedTimes: z
.number()
.default(0)
.describe(
"The retried times for writing the joke. Always increase this from the input retriedTimes.",
),
});
const critiqueSchema = z.object({
joke: z.string().describe("The joke to critique"),
retriedTimes: z.number().describe("The retried times for writing the joke."),
});
const finalResultSchema = z.object({
joke: z.string().describe("The joke to critique"),
critique: z.string().describe("The critique of the joke"),
});
const writeJokeEvent = zodEvent(writeJokeSchema, {
debugLabel: "writeJokeEvent",
});
const critiqueEvent = zodEvent(critiqueSchema, {
debugLabel: "critiqueEvent",
});
const finalResultEvent = zodEvent(finalResultSchema, {
debugLabel: "finalResultEvent",
});
```
Note that your natural language workflows the events need to be created by the `zodEvent` function passing the zod schema as an argument. The agent needs the schema of the event data to correctly generate events.
Also, we need a `debugLabel` so the LLM can identify the event to emit in the workflow.
### Define the workflow
As usual you first create the workflow:
```typescript
import { agentHandler, createWorkflow } from "@llamaindex/workflow";
const jokeFlow = createWorkflow();
```
Then you need to handle the events. For the handlers, instead of code, you're now going to use natural language by calling the `agentHandler` function.
It only requires two parameters:
- `instructions`: A prompt to guide the agent how to handle the steps.
- `results`: The output events that the agent should return after handling the step.
Then you will have a simple code to handle the step:
```typescript
jokeFlow.handle(
[writeJokeEvent],
agentHandler({
instructions: `You are a joke writer. You are given a topic and you need to write a joke about it.`,
results: [critiqueEvent],
}),
);
jokeFlow.handle(
[critiqueEvent],
agentHandler({
instructions: `
You are given a joke and you need to critique it. Follow the following guidelines:
1. You have maximum 3 times to improve the joke.
2. If the joke is not good, increase the retriedTimes, describe how to improve the joke and send a writeJokeEvent.
3. If the joke is good, trigger the finalResultEvent event.
`,
results: [writeJokeEvent, finalResultEvent],
}),
);
```
For advanced usage, you can add more functionality to `agentHandler` by using these parameters:
- `events`: A list of additional events that the agent can emit to the workflow. E.g., your agent can emit a `uiEvent` to update the UI during the execution.
- `tools`: A list of tools that the agent can use to handle the step. E.g., your agent can use a `search` tool to search the web.
You can find more code examples in the [examples](https://github.com/run-llama/LlamaIndexTS/tree/main/examples/agents/natural) folder.
@@ -74,12 +74,21 @@ const server = mcp({
args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
verbose: true,
});
// or by SSE
// or by StreamableHTTP transport
const server = mcp({
url: "http://localhost:8000/mcp",
verbose: true,
});
// if your MCP server is not using StreamableHTTP transport, you can also use SSE transport
// by setting useSSETransport to true.
// See: https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse-deprecated
const server = mcp({
url: "http://localhost:8000/mcp",
useSSETransport: true,
verbose: true,
});
// 3. Get tools from MCP server
const tools = await server.tools();
@@ -378,3 +378,186 @@ async function main() {
## API Reference
- [OpenAI](/docs/api/classes/OpenAI)
# OpenAI Live LLM
The OpenAI Live LLM integration in LlamaIndex provides real-time chat capabilities with support for audio streaming and tool calling.
## Basic Usage
```typescript
import { openai } from "@llamaindex/openai";
import { tool, ModalityType } from "llamaindex";
// Get the ephimeral key on the server
const serverllm = openai({
apiKey: "your-api-key",
model: "gpt-4o-realtime-preview-2025-06-03",
});
// Get an ephemeral key
// Usually this code is run on the server and the ephemeral key is passed to the
// client - the ephemeral key can be securely used on the client side
const ephemeralKey = await serverllm.live.getEphemeralKey();
// Create a client-side LLM instance with the ephemeral key
const llm = openai({
apiKey: ephemeralKey,
model: "gpt-4o-realtime-preview-2025-06-03"
});
// Create a live sessionimport { tool } from "llamaindex";
const session = await llm.live.connect({
systemInstruction: "You are a helpful assistant.",
});
// Send a message
session.sendMessage({
content: "Hello!",
role: "user",
});
```
## Tool Integration
Tools are handled server-side, making it simple to pass them to the live session:
```typescript
// Define your tools
const weatherTool = tool({
name: "weather",
description: "Get the weather for a location",
parameters: z.object({
location: z.string().describe("The location to get weather for"),
}),
execute: async ({ location }) => {
return `The weather in ${location} is sunny`;
},
});
// Create session with tools
const session = await llm.live.connect({
systemInstruction: "You are a helpful assistant.",
tools: [weatherTool],
});
```
## Audio Support
For audio capabilities:
```typescript
// Get microphone access
const userStream = await navigator.mediaDevices.getUserMedia({
audio: true,
});
// Create session with audio
const session = await llm.live.connect({
audioConfig: {
stream: userStream,
onTrack: (remoteStream) => {
// Handle incoming audio
audioElement.srcObject = remoteStream;
},
},
});
```
## Event Handling
Listen to events from the session:
```typescript
for await (const event of session.streamEvents()) {
if (liveEvents.open.include(event)) {
// Connection established
console.log("Connected!");
} else if (liveEvents.text.include(event)) {
// Received text response
console.log("Assistant:", event.text);
}
}
```
## Capabilities
The OpenAI Live LLM supports:
- Real-time text chat
- Audio streaming (if configured)
- Tool calling (server-side execution)
- Ephemeral key generation for secure sessions
## API Reference
### LiveLLM Methods
// Get an ephemeral key
// Usually this code is run on the server and the ephemeral key is passed to the
// client - the ephemeral key can be securely used on the client side
#### `connect(config?: LiveConnectConfig)`
Creates a new live session.
```typescript
interface LiveConnectConfig {
systemInstruction?: string;
tools?: BaseTool[];
audioConfig?: AudioConfig;
responseModality?: ModalityType[];
}
```
#### `getEphemeralKey()`
Gets a temporary key for the session.
### LiveLLMSession Methods
#### `sendMessage(message: ChatMessage)`
Sends a message to the assistant.
```typescript
interface ChatMessage {
content: string | MessageContentDetail[];
role: "user" | "assistant";
}
```
#### `disconnect()`
Closes the session and cleans up resources.
## Error Handling
```typescript
try {
const session = await llm.live.connect();
} catch (error) {
if (error instanceof Error) {
console.error("Connection failed:", error.message);
}
}
```
## Best Practices
1. **Tool Definition**
- Keep tool implementations server-side
- Use clear descriptions for tools
- Handle tool errors gracefully
2. **Session Management**
- Always disconnect sessions when done
- Clean up audio resources
- Handle reconnection scenarios
3. **Security**
- Use ephemeral keys for sessions
- Validate tool inputs
- Secure API key handling
@@ -1,44 +0,0 @@
---
title: Using API Route
description: Chat interface for your LlamaIndexTS application using API Route
---
Using [chat-ui](https://github.com/run-llama/chat-ui), it's easy to add a chat interface to your LlamaIndexTS application.
You just need to create an API route that provides an `api/chat` endpoint and a chat component to consume the API.
## API route
As an example, this is an API route for the Next.js App Router. Copy the following code into your `app/api/chat/route.ts` file to get started:
```json doc-gen:file
{
"file": "./src/app/api/chat/route.ts",
"codeblock": true
}
```
## Chat UI
This is the simplest way to add a chat interface to your application. Copy the following code into your application to consume the API:
```json doc-gen:file
{
"file": "./src/components/demo/chat/api/demo.tsx",
"codeblock": true
}
```
## Try it out ⬇️
Combining both, you're getting a fully functional chat interface:
<ChatDemo />
## Next Steps
The steps above are the bare minimum to get a chat interface working. From here, you can go two ways:
1. Use [create-llama](https://github.com/run-llama/create-llama) to scaffold a new LlamaIndexTS project including complex API routes and chat interfaces or
2. Learn more about [chat-ui](https://github.com/run-llama/chat-ui) and [LlamaIndexTS](https://github.com/run-llama/llamaindex-ts) to customize the chat interface and API routes to your needs.
@@ -0,0 +1,8 @@
---
title: Using @llamaindex/chat-ui
description: Chat UI components for your LlamaIndexTS application
---
@llamaindex/chat-ui is a library that provides a set of components for building chat user interfaces. It is built on top of [Shadcn UI](https://ui.shadcn.com).
Check out our [chat-ui](/docs/chat-ui) documentation or try running examples on the [ui.llamaindex.ai](https://ui.llamaindex.ai) website.
@@ -1,22 +0,0 @@
---
title: Install @llamaindex/chat
description: Chat interface for your LlamaIndexTS application
---
## Quick Start
You can quickly add a chatbot to your project by using Shadcn CLI command:
```sh
npx shadcn@latest add https://ui.llamaindex.ai/r/chat.json
```
## Manual Installation
To install the package, run the following command in your project directory:
```sh
npm i @llamaindex/chat-ui
```
For more information, check out the [github.comrun-llama/chat-ui](https://github.com/run-llama/chat-ui)
@@ -9,161 +9,11 @@ LlamaIndexServer is a Next.js-based application that allows you to quickly launc
## Features
- Serving a workflow as a chatbot
- Add a sophisticated chatbot UI to your LlamaIndex workflow
- Edit code and document artifacts in an OpenAI Canvas-style UI
- Extendable UI components for events and headers
- Built on Next.js for high performance and easy API development
- Optional built-in chat UI with extendable UI components
- Prebuilt development code
## Installation
```package-install
npm i @llamaindex/server
```
## Quick Start
Create an `index.ts` file and add the following code:
```ts
import { LlamaIndexServer } from "@llamaindex/server";
import { wiki } from "@llamaindex/tools"; // or any other tool
const createWorkflow = () => agent({ tools: [wiki()] })
new LlamaIndexServer({
workflow: createWorkflow,
uiConfig: {
appTitle: "LlamaIndex App",
starterQuestions: ["Who is the first president of the United States?"],
},
}).start();
```
## Running the Server
In the same directory as `index.ts`, run the following command to start the server:
```bash
tsx index.ts
```
The server will start at `http://localhost:3000`
You can also make a request to the server:
```bash
curl -X POST "http://localhost:3000/api/chat" -H "Content-Type: application/json" -d '{"message": "Who is the first president of the United States?"}'
```
## Configuration Options
The `LlamaIndexServer` accepts the following configuration options:
- `workflow`: A callable function that creates a workflow instance for each request
- `uiConfig`: An object to configure the chat UI containing the following properties:
- `appTitle`: The title of the application (default: `"LlamaIndex App"`)
- `starterQuestions`: List of starter questions for the chat UI (default: `[]`)
- `componentsDir`: The directory for custom UI components rendering events emitted by the workflow. The default is undefined, which does not render custom UI components.
- `llamaCloudIndexSelector`: Whether to show the LlamaCloud index selector in the chat UI (requires `LLAMA_CLOUD_API_KEY` to be set in the environment variables) (default: `false`)
LlamaIndexServer accepts all the configuration options from Nextjs Custom Server such as `port`, `hostname`, `dev`, etc.
See all Nextjs Custom Server options [here](https://nextjs.org/docs/app/building-your-application/configuring/custom-server).
## AI-generated UI Components
The LlamaIndex server provides support for rendering workflow events using custom UI components, allowing you to extend and customize the chat interface.
These components can be auto-generated using an LLM by providing a JSON schema of the workflow event.
### UI Event Schema
To display custom UI components, your workflow needs to emit UI events that have an event type for identification and a data object:
```typescript
class UIEvent extends WorkflowEvent<{
type: "ui_event";
data: UIEventData;
}> {}
```
The `data` object can be any JSON object. To enable AI generation of the UI component, you need to provide a schema for that data (here we're using Zod):
```typescript
const MyEventDataSchema = z.object({
stage: z.enum(["retrieve", "analyze", "answer"]).describe("The current stage the workflow process is in."),
progress: z.number().min(0).max(1).describe("The progress in percent of the current stage"),
}).describe("WorkflowStageProgress");
type UIEventData = z.infer<typeof MyEventDataSchema>;
```
### Generate UI Components
The `generateEventComponent` function uses an LLM to generate a custom UI component based on the JSON schema of a workflow event. The schema should contain accurate descriptions of each field so that the LLM can generate matching components for your use case. We've done this for you in the example above using the `describe` function from Zod:
```typescript
import { OpenAI } from "llamaindex";
import { generateEventComponent } from "@llamaindex/server";
import { MyEventDataSchema } from "./your-workflow";
// Also works well with Claude 3.5 Sonnet and Google Gemini 2.5 Pro
const llm = new OpenAI({ model: "gpt-4.1" });
const code = generateEventComponent(MyEventDataSchema, llm);
```
After generating the code, we need to save it to a file. The file name must match the event type from your workflow (e.g., `ui_event.jsx` for handling events with `ui_event` type):
```ts
fs.writeFileSync("components/ui_event.jsx", code);
```
Feel free to modify the generated code to match your needs. If you're not satisfied with the generated code, we suggest improving the provided JSON schema first or trying another LLM.
> Note that `generateEventComponent` is generating JSX code, but you can also provide a TSX file.
### Server Setup
To use the generated UI components, you need to initialize the LlamaIndex server with the `componentsDir` that contains your custom UI components:
```ts
new LlamaIndexServer({
workflow: createWorkflow,
uiConfig: {
appTitle: "LlamaIndex App",
componentsDir: "components",
},
}).start();
```
## Default Endpoints and Features
### Chat Endpoint
The server includes a default chat endpoint at `/api/chat` for handling chat interactions.
### Chat UI
The server always provides a chat interface at the root path (`/`) with:
- Configurable starter questions
- Real-time chat interface
- API endpoint integration
### Static File Serving
- The server automatically mounts the `data` and `output` folders at `{server_url}{api_prefix}/files/data` (default: `/api/files/data`) and `{server_url}{api_prefix}/files/output` (default: `/api/files/output`) respectively.
- Your workflows can use both folders to store and access files. By convention, the `data` folder is used for documents that are ingested, and the `output` folder is used for documents generated by the workflow.
## Best Practices
1. Always provide a workflow factory that creates a fresh workflow instance for each request.
2. Use environment variables for sensitive configuration (e.g., API keys).
3. Use starter questions to guide users in the chat UI.
## Getting Started with a New Project
Want to start a new project with LlamaIndexServer? Check out our [create-llama](https://github.com/run-llama/create-llama) tool to quickly generate a new project with LlamaIndexServer.
## API Reference
- [LlamaIndexServer](https://github.com/run-llama/create-llama/blob/main/packages/server)
Check the latest information on the NPM package page: https://www.npmjs.com/package/@llamaindex/server
@@ -2,5 +2,5 @@
"title": "Chat UI",
"description": "Use chat-ui to add a chat interface to your LlamaIndexTS application.",
"defaultOpen": false,
"pages": ["install", "chat", "rsc", "llamaindex-server"]
"pages": ["index", "llamaindex-server"]
}
@@ -1,65 +0,0 @@
---
title: Using Next.js RSC
description: Chat interface for your LlamaIndexTS application using Next.js RSC
---
Using [chat-ui](https://github.com/run-llama/chat-ui), it's easy to add a chat interface to your LlamaIndexTS application using [Next.js RSC](https://nextjs.org/docs/app/building-your-application/rendering/server-components) and [Vercel AI RSC](https://sdk.vercel.ai/docs/ai-sdk-rsc/overview).
With RSC, the chat messages are not returned as JSON from the server (like when using an [API route](/docs/llamaindex/modules/ui/chat)), instead the chat message components are rendered on the server side.
This is for example useful for rendering a whole chat history on the server before sending it to the client. [Check here](https://sdk.vercel.ai/docs/getting-started/navigating-the-library#when-to-use-ai-sdk-rsc), for a discussion of when to use use RSC.
For implementing a chat interface with RSC, you need to create an AI action and then connect the chat interface to use it.
## Create an AI action
First, define an [AI context provider](https://sdk.vercel.ai/examples/rsc/state-management/ai-ui-states) with a chat server action:
```json doc-gen:file
{
"file": "./src/components/demo/chat/rsc/ai-action.tsx",
"codeblock": true
}
```
The chat server action is using LlamaIndexTS to generate a response based on the chat history and the user input.
## Create the chat UI
The entrypoint of our application initializes the AI provider for the application and adds a `ChatSection` component:
```json doc-gen:file
{
"file": "./src/components/demo/chat/rsc/demo.tsx",
"codeblock": true
}
```
The `ChatSection` component is created by using chat components from @llamaindex/chat-ui:
```json doc-gen:file
{
"file": "./src/components/demo/chat/rsc/chat-section.tsx",
"codeblock": true
}
```
It is using a `useChatRSC` hook to conntect the chat interface to the `chat` AI action that we defined earlier:
```json doc-gen:file
{
"file": "./src/components/demo/chat/rsc/use-chat-rsc.tsx",
"codeblock": true
}
```
## Try RSC Chat ⬇️
<ChatDemoRSC />
## Next Steps
The steps above are the bare minimum to get a chat interface working with RSC. From here, you can go two ways:
1. Use our [full-stack RSC example](https://github.com/run-llama/nextjs-rsc) based on [create-llama](https://github.com/run-llama/create-llama) to get started quickly with a fully working chat interface or
2. Learn more about [AI RSC](https://sdk.vercel.ai/examples/rsc), [chat-ui](https://github.com/run-llama/chat-ui) and [LlamaIndexTS](https://github.com/run-llama/llamaindex-ts) to customize the chat interface and AI actions to your needs.
+1 -1
View File
@@ -1,3 +1,3 @@
{
"pages": ["llamaindex", "api", "llamaflow"]
"pages": ["llamaindex", "api", "llamaflow", "chat-ui"]
}
+1
View File
@@ -5,6 +5,7 @@
"build": {
"inputs": [
"node_modules/@llama-flow/docs/**",
"node_modules/@llamaindex/chat-ui-docs/**",
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.mdx",
@@ -1,5 +1,36 @@
# @llamaindex/cloudflare-worker-agent-test
## 0.0.170
### Patch Changes
- llamaindex@0.11.9
## 0.0.169
### Patch Changes
- llamaindex@0.11.8
## 0.0.168
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 0.0.167
### Patch Changes
- llamaindex@0.11.6
## 0.0.166
### Patch Changes
- llamaindex@0.11.5
## 0.0.165
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloudflare-worker-agent-test",
"version": "0.0.165",
"version": "0.0.170",
"type": "module",
"private": true,
"scripts": {
@@ -1,5 +1,23 @@
# @llamaindex/llama-parse-browser-test
## 0.0.70
### Patch Changes
- @llamaindex/cloud@4.0.15
## 0.0.69
### Patch Changes
- @llamaindex/cloud@4.0.14
## 0.0.68
### Patch Changes
- @llamaindex/cloud@4.0.13
## 0.0.67
### Patch Changes
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/llama-parse-browser-test",
"private": true,
"version": "0.0.67",
"version": "0.0.70",
"type": "module",
"scripts": {
"dev": "vite",
+31
View File
@@ -1,5 +1,36 @@
# @llamaindex/next-agent-test
## 0.1.170
### Patch Changes
- llamaindex@0.11.9
## 0.1.169
### Patch Changes
- llamaindex@0.11.8
## 0.1.168
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 0.1.167
### Patch Changes
- llamaindex@0.11.6
## 0.1.166
### Patch Changes
- llamaindex@0.11.5
## 0.1.165
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-agent-test",
"version": "0.1.165",
"version": "0.1.170",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,36 @@
# test-edge-runtime
## 0.1.169
### Patch Changes
- llamaindex@0.11.9
## 0.1.168
### Patch Changes
- llamaindex@0.11.8
## 0.1.167
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 0.1.166
### Patch Changes
- llamaindex@0.11.6
## 0.1.165
### Patch Changes
- llamaindex@0.11.5
## 0.1.164
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/nextjs-edge-runtime-test",
"version": "0.1.164",
"version": "0.1.169",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,49 @@
# @llamaindex/next-node-runtime
## 0.1.38
### Patch Changes
- Updated dependencies [c5846bd]
- @llamaindex/readers@3.1.10
## 0.1.37
### Patch Changes
- llamaindex@0.11.9
- @llamaindex/huggingface@0.1.15
- @llamaindex/readers@3.1.9
## 0.1.36
### Patch Changes
- llamaindex@0.11.8
- @llamaindex/huggingface@0.1.14
- @llamaindex/readers@3.1.8
## 0.1.35
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 0.1.34
### Patch Changes
- llamaindex@0.11.6
## 0.1.33
### Patch Changes
- llamaindex@0.11.5
- @llamaindex/huggingface@0.1.13
- @llamaindex/readers@3.1.7
## 0.1.32
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-node-runtime-test",
"version": "0.1.32",
"version": "0.1.38",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,36 @@
# vite-import-llamaindex
## 0.0.36
### Patch Changes
- llamaindex@0.11.9
## 0.0.35
### Patch Changes
- llamaindex@0.11.8
## 0.0.34
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 0.0.33
### Patch Changes
- llamaindex@0.11.6
## 0.0.32
### Patch Changes
- llamaindex@0.11.5
## 0.0.31
### Patch Changes
@@ -1,7 +1,7 @@
{
"name": "vite-import-llamaindex",
"private": true,
"version": "0.0.31",
"version": "0.0.36",
"type": "module",
"scripts": {
"build": "vite build",
@@ -1 +1,9 @@
{"root":["./src/main.ts","./vite.config.ts"],"version":"5.7.3"}
{
"root": [
"./src/main.ts",
"./vite.config.ts",
"./tsconfig.json"
],
"errors": true,
"version": "5.7.3"
}
@@ -1,5 +1,36 @@
# @llamaindex/waku-query-engine-test
## 0.0.170
### Patch Changes
- llamaindex@0.11.9
## 0.0.169
### Patch Changes
- llamaindex@0.11.8
## 0.0.168
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 0.0.167
### Patch Changes
- llamaindex@0.11.6
## 0.0.166
### Patch Changes
- llamaindex@0.11.5
## 0.0.165
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/waku-query-engine-test",
"version": "0.0.165",
"version": "0.0.170",
"type": "module",
"private": true,
"scripts": {
+185
View File
@@ -1,5 +1,190 @@
# examples
## 0.3.24
### Patch Changes
- Updated dependencies [096bf2b]
- Updated dependencies [c5846bd]
- @llamaindex/tools@0.1.0
- @llamaindex/readers@3.1.10
## 0.3.23
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
- @llamaindex/google@0.3.10
- @llamaindex/openai@0.4.5
- @llamaindex/cloud@4.0.15
- llamaindex@0.11.9
- @llamaindex/node-parser@2.0.11
- @llamaindex/anthropic@0.3.13
- @llamaindex/assemblyai@0.1.10
- @llamaindex/clip@0.0.61
- @llamaindex/cohere@0.0.25
- @llamaindex/deepinfra@0.0.61
- @llamaindex/discord@0.1.10
- @llamaindex/huggingface@0.1.15
- @llamaindex/jinaai@0.0.21
- @llamaindex/mistral@0.1.11
- @llamaindex/mixedbread@0.0.25
- @llamaindex/notion@0.1.10
- @llamaindex/ollama@0.1.11
- @llamaindex/perplexity@0.0.18
- @llamaindex/portkey-ai@0.0.53
- @llamaindex/replicate@0.0.53
- @llamaindex/astra@0.0.25
- @llamaindex/azure@0.1.22
- @llamaindex/chroma@0.0.25
- @llamaindex/elastic-search@0.1.11
- @llamaindex/firestore@1.0.18
- @llamaindex/milvus@0.1.20
- @llamaindex/mongodb@0.0.26
- @llamaindex/pinecone@0.1.11
- @llamaindex/postgres@0.0.54
- @llamaindex/qdrant@0.1.21
- @llamaindex/supabase@0.1.10
- @llamaindex/upstash@0.0.25
- @llamaindex/weaviate@0.0.26
- @llamaindex/vercel@0.1.11
- @llamaindex/voyage-ai@1.0.17
- @llamaindex/readers@3.1.9
- @llamaindex/tools@0.0.17
- @llamaindex/workflow@1.1.10
- @llamaindex/deepseek@0.0.21
- @llamaindex/fireworks@0.0.21
- @llamaindex/groq@0.0.76
- @llamaindex/together@0.0.21
- @llamaindex/vllm@0.0.47
- @llamaindex/xai@0.0.8
## 0.3.22
### Patch Changes
- Updated dependencies [8a51c16]
- Updated dependencies [1b5af14]
- @llamaindex/workflow@1.1.9
- @llamaindex/core@0.6.10
- llamaindex@0.11.8
- @llamaindex/cloud@4.0.14
- @llamaindex/node-parser@2.0.10
- @llamaindex/anthropic@0.3.12
- @llamaindex/assemblyai@0.1.9
- @llamaindex/clip@0.0.60
- @llamaindex/cohere@0.0.24
- @llamaindex/deepinfra@0.0.60
- @llamaindex/discord@0.1.9
- @llamaindex/google@0.3.9
- @llamaindex/huggingface@0.1.14
- @llamaindex/jinaai@0.0.20
- @llamaindex/mistral@0.1.10
- @llamaindex/mixedbread@0.0.24
- @llamaindex/notion@0.1.9
- @llamaindex/ollama@0.1.10
- @llamaindex/openai@0.4.4
- @llamaindex/perplexity@0.0.17
- @llamaindex/portkey-ai@0.0.52
- @llamaindex/replicate@0.0.52
- @llamaindex/astra@0.0.24
- @llamaindex/azure@0.1.21
- @llamaindex/chroma@0.0.24
- @llamaindex/elastic-search@0.1.10
- @llamaindex/firestore@1.0.17
- @llamaindex/milvus@0.1.19
- @llamaindex/mongodb@0.0.25
- @llamaindex/pinecone@0.1.10
- @llamaindex/postgres@0.0.53
- @llamaindex/qdrant@0.1.20
- @llamaindex/supabase@0.1.9
- @llamaindex/upstash@0.0.24
- @llamaindex/weaviate@0.0.25
- @llamaindex/vercel@0.1.10
- @llamaindex/voyage-ai@1.0.16
- @llamaindex/readers@3.1.8
- @llamaindex/tools@0.0.16
- @llamaindex/deepseek@0.0.20
- @llamaindex/fireworks@0.0.20
- @llamaindex/groq@0.0.75
- @llamaindex/together@0.0.20
- @llamaindex/vllm@0.0.46
- @llamaindex/xai@0.0.7
## 0.3.21
### Patch Changes
- Updated dependencies [dbd857f]
- Updated dependencies [3c857f4]
- @llamaindex/workflow@1.1.8
- llamaindex@0.11.7
- @llamaindex/tools@0.0.15
## 0.3.20
### Patch Changes
- Updated dependencies [e7484ef]
- @llamaindex/weaviate@0.0.24
## 0.3.19
### Patch Changes
- Updated dependencies [766054b]
- Updated dependencies [5346623]
- Updated dependencies [71598f8]
- @llamaindex/workflow@1.1.6
- @llamaindex/google@0.3.6
- @llamaindex/core@0.6.9
- llamaindex@0.11.5
- @llamaindex/cloud@4.0.13
- @llamaindex/node-parser@2.0.9
- @llamaindex/anthropic@0.3.11
- @llamaindex/assemblyai@0.1.8
- @llamaindex/clip@0.0.59
- @llamaindex/cohere@0.0.23
- @llamaindex/deepinfra@0.0.59
- @llamaindex/discord@0.1.8
- @llamaindex/huggingface@0.1.13
- @llamaindex/jinaai@0.0.19
- @llamaindex/mistral@0.1.9
- @llamaindex/mixedbread@0.0.23
- @llamaindex/notion@0.1.8
- @llamaindex/ollama@0.1.9
- @llamaindex/openai@0.4.3
- @llamaindex/perplexity@0.0.16
- @llamaindex/portkey-ai@0.0.51
- @llamaindex/replicate@0.0.51
- @llamaindex/astra@0.0.23
- @llamaindex/azure@0.1.20
- @llamaindex/chroma@0.0.23
- @llamaindex/elastic-search@0.1.9
- @llamaindex/firestore@1.0.16
- @llamaindex/milvus@0.1.18
- @llamaindex/mongodb@0.0.24
- @llamaindex/pinecone@0.1.9
- @llamaindex/postgres@0.0.52
- @llamaindex/qdrant@0.1.19
- @llamaindex/supabase@0.1.8
- @llamaindex/upstash@0.0.23
- @llamaindex/weaviate@0.0.23
- @llamaindex/vercel@0.1.9
- @llamaindex/voyage-ai@1.0.15
- @llamaindex/readers@3.1.7
- @llamaindex/tools@0.0.14
- @llamaindex/deepseek@0.0.19
- @llamaindex/fireworks@0.0.19
- @llamaindex/groq@0.0.74
- @llamaindex/together@0.0.19
- @llamaindex/vllm@0.0.45
- @llamaindex/xai@0.0.6
## 0.3.18
### Patch Changes
+1 -1
View File
@@ -1,4 +1,3 @@
import { tool } from "@llamaindex/core/tools";
import { openai } from "@llamaindex/openai";
import {
agent,
@@ -7,6 +6,7 @@ import {
multiAgent,
} from "@llamaindex/workflow";
import fs from "fs";
import { tool } from "llamaindex";
import os from "os";
import { z } from "zod";
+12 -3
View File
@@ -6,15 +6,24 @@ async function main() {
// Create an MCP server for filesystem tools
const server = mcp({
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
args: ["-y", "@modelcontextprotocol/server-filesystem@latest", "."],
verbose: true,
});
// You can also connect to the MCP server using SSE
// See: https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse
//
// You can also connect to a remote MCP server using:
// 1. StreamableHTTP transport (recommended)
// See: https://modelcontextprotocol.io/docs/concepts/transports#streamable-http
// const server = mcp({
// url: "http://localhost:8000/mcp",
// verbose: true,
// });
// 2.Or using SSE transport (will be deprecated soon)
// See: https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse-deprecated
// const server = mcp({
// url: "http://localhost:8000/mcp",
// useSSETransport: true,
// verbose: true,
// });
try {
// Create an agent that uses the MCP tools
+130
View File
@@ -0,0 +1,130 @@
import { ToolCallLLM } from "llamaindex";
import {
agentHandler,
createWorkflow,
workflowEvent,
zodEvent,
} from "@llamaindex/workflow";
import { openai } from "@llamaindex/openai";
import { z } from "zod";
// ===== 1. Define events =====
// An event to trigger the workflow
const planEvent = workflowEvent<{ topic: string }>();
// Generate artifact event
const ArtifactRequirementSchema = z.object({
type: z.literal("markdown"),
title: z.string().describe("The title of the artifact."),
requirement: z
.string()
.describe("The requirement for the artifact generation."),
});
const generateArtifactEvent = zodEvent(ArtifactRequirementSchema, {
debugLabel: "generateArtifactEvent",
});
// Artifact output event
const ArtifactSchema = z.object({
type: z.literal("artifact"),
data: z.object({
type: z.literal("document"),
data: z.object({
title: z.string().describe("The title of the data."),
content: z.string().describe("The content of the data."),
type: z.enum(["markdown", "html"]).describe("The type of the data."),
}),
}),
});
const outputArtifactEvent = zodEvent(ArtifactSchema, {
debugLabel: "outputArtifactEvent",
});
// Events for updating UI
// assume that we have a UI that can render different states of the workflow
// and update the UI based on the state and the requirement
export const UIEventSchema = z.object({
type: z.literal("ui_event"),
data: z.object({
state: z
.enum(["plan", "generate", "completed"])
.describe("The current state of the workflow."),
requirement: z
.string()
.optional()
.describe(
"An optional requirement creating or updating a document, if applicable.",
),
}),
});
const uiEvent = zodEvent(UIEventSchema, { debugLabel: "uiEvent" });
// ===== 2. Define workflow with agents using natural language =====
// We have a document artifact workflow that made up of 2 steps:
// 1. Generate requirement for the document
// 2. Generate document content based on the requirement
export function createDocumentArtifactWorkflow(llm: ToolCallLLM) {
const workflow = createWorkflow();
// Generate requirement for the document
workflow.handle(
[planEvent],
agentHandler({
instructions: `
Your task is to analyze the request and provide requirements for document generation or update.
1. Send an uiEvent with the \`plan\` to show UI what you are going to do.
2. Analyze the conversation history and the user's request carefully to determine the completed tasks and the next steps.
3. Return the generateArtifactEvent with the requirement for the next step of the document generation or update.
`,
results: [generateArtifactEvent],
events: [uiEvent],
llm,
}),
);
// Generate document content based on the requirement
workflow.handle(
[generateArtifactEvent],
agentHandler({
instructions: `
You are a skilled technical writer who can assist users with documentation.
Your task is to generate document content based on the requirement and update the UI state.
Here are the steps to handle this task:
1. First, send an uiEvent with the \`generate\` state and the requirement you received from the input.
2. Next, start generating the content based on the requirement then send an uiEvent with the \`completed\` state to update the state.
3. Finally, return the outputArtifactEvent with the document values.
`,
results: [outputArtifactEvent],
events: [uiEvent],
llm,
}),
);
return workflow;
}
async function main() {
const llm = openai({ model: "gpt-4.1-mini" });
const workflow = createDocumentArtifactWorkflow(llm);
const { stream, sendEvent } = workflow.createContext();
// Ask the workflow to generate a document about `llama`
sendEvent(planEvent.with({ topic: "llama" }));
await stream.until(outputArtifactEvent).forEach((event) => {
if (planEvent.include(event)) {
console.log("Starting workflow: ", event.data);
}
if (uiEvent.include(event)) {
console.log("UI event: ", event.data);
} else if (outputArtifactEvent.include(event)) {
console.log("Output artifact event: ", event.data);
}
});
}
main();
+93
View File
@@ -0,0 +1,93 @@
import { openai } from "@llamaindex/openai";
import { agentHandler, createWorkflow, zodEvent } from "@llamaindex/workflow";
import { Settings } from "llamaindex";
import { z } from "zod";
// Create LLM instance
const llm = openai({ model: "gpt-4.1-mini" });
Settings.llm = llm;
// Define our workflow events
const writeJokeSchema = z.object({
description: z
.string()
.describe("The topic to write a joke or describe the joke to improve."),
writtenJoke: z.optional(z.string()).describe("The written joke."),
retriedTimes: z
.number()
.default(0)
.describe(
"The retried times for writing the joke. Always increase this from the input retriedTimes.",
),
});
const critiqueSchema = z.object({
joke: z.string().describe("The joke to critique"),
retriedTimes: z.number().describe("The retried times for writing the joke."),
});
const finalResultSchema = z.object({
joke: z.string().describe("The joke to critique"),
critique: z.string().describe("The critique of the joke"),
});
const writeJokeEvent = zodEvent(writeJokeSchema, {
debugLabel: "writeJokeEvent",
}); // Input topic for writing a joke
const critiqueEvent = zodEvent(critiqueSchema, {
debugLabel: "critiqueEvent",
}); // Ask for critique of the joke
const finalResultEvent = zodEvent(finalResultSchema, {
debugLabel: "finalResultEvent",
}); // Final result
// Create our workflow
const jokeFlow = createWorkflow();
// Define handlers for each step
// This step always write a joke based on the description
jokeFlow.handle(
[writeJokeEvent],
agentHandler({
instructions: `You are a joke writer. You are given a topic and you need to write a joke about it.`,
results: [critiqueEvent],
}),
);
// This step critiques the joke and asks the writer to improve the joke or send a final result event for stopping.
jokeFlow.handle(
[critiqueEvent],
agentHandler({
instructions: `
You are given a joke and you need to critique it. Follow the following guidelines:
1. You have maximum 3 times to improve the joke.
2. If the joke is not good, increase the retriedTimes, describe how to improve the joke and send a writeJokeEvent.
3. If the joke is good, trigger the finalResultEvent event.
`,
results: [writeJokeEvent, finalResultEvent],
}),
);
// Usage
async function main() {
const { stream, sendEvent } = jokeFlow.createContext();
sendEvent(writeJokeEvent.with({ description: "write a joke about llama" }));
await stream.until(finalResultEvent).forEach((event) => {
if (writeJokeEvent.include(event)) {
console.log(
"Triggering write joke: ",
JSON.stringify(event.data, null, 2),
);
} else if (critiqueEvent.include(event)) {
console.log("Written joke: ", JSON.stringify(event.data, null, 2));
} else if (finalResultEvent.include(event)) {
console.log("Output: ", JSON.stringify(event.data, null, 2));
} else {
console.log("Unknown event: ", JSON.stringify(event.data, null, 2));
}
});
console.log("Done");
}
main().catch(console.error);
+72
View File
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<company name="MidSizeCorp" founded="2008">
<division name="Engineering" head="Dana White">
<department name="Frontend" lead="Alex Kim">
<team name="Web">
<employee id="E01">
<name>Jordan Lee</name>
<role>Lead Developer</role>
<projects>
<project code="PRJ101" status="active">
<title>User Portal</title>
<deadline>2025-08-01</deadline>
<tasks>
<task id="T1011">
<description>Implement login page</description>
<due>2025-05-10</due>
</task>
<task id="T1012">
<description>Design dashboard</description>
<due>2025-05-20</due>
</task>
</tasks>
</project>
</projects>
</employee>
<employee id="E02">
<name>Riley Chen</name>
<role>UI Designer</role>
</employee>
</team>
<team name="Mobile">
<employee id="E03">
<name>Sam Patel</name>
<role>iOS Developer</role>
</employee>
</team>
</department>
<department name="Backend" lead="Morgan Reed">
<team name="API">
<employee id="E04">
<name>Taylor Jones</name>
<role>API Engineer</role>
</employee>
</team>
<team name="Database">
<employee id="E05">
<name>Casey Nguyen</name>
<role>DB Administrator</role>
</employee>
</team>
</department>
</division>
<division name="Marketing" head="Pat Morgan">
<department name="Digital" lead="Alex Rivera">
<team name="Content">
<employee id="M01">
<name>Charlie Brooks</name>
<role>Content Strategist</role>
</employee>
</team>
</department>
</division>
<headquarters location="Chicago, USA">
<address>
<street>789 Lake Shore Drive</street>
<city>Chicago</city>
<zip>60601</zip>
</address>
</headquarters>
</company>
+1 -4
View File
@@ -23,10 +23,7 @@ async function main() {
content: "Say something about you for 10 seconds",
role: "user",
});
} else if (
liveEvents.audio.include(event) &&
typeof event.data === "string"
) {
} else if (liveEvents.audio.include(event)) {
const chunk = Buffer.from(event.data, "base64");
audioChunks.push(chunk);
console.log(`Received audio chunk: ${chunk.length} bytes`);
+1 -2
View File
@@ -1,6 +1,5 @@
import { ModalityType } from "@llamaindex/core/schema";
import { tool } from "@llamaindex/core/tools";
import { gemini, GEMINI_MODEL } from "@llamaindex/google";
import { ModalityType, tool } from "llamaindex";
import { liveEvents } from "llamaindex";
import { z } from "zod";
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
@@ -0,0 +1,54 @@
# OpenAI Realtime Chat with LlamaIndex
This is a demo application showcasing real-time audio and text chat capabilities using OpenAI's GPT-4 with voice through LlamaIndex. The application demonstrates bidirectional audio communication and text chat with an AI assistant.
## Features
- Real-time voice communication with GPT-4
- Text-based chat interface
- WebRTC-based audio streaming
- Bidirectional communication (both text and voice)
- React + TypeScript implementation
## Prerequisites
- Node.js (v18 or higher)
- OpenAI API key with access to GPT-4 voice models
- Modern browser with WebRTC support
## Getting Started
1. Install dependencies:
```bash
pnpm install
```
2. Start the development server:
```bash
pnpm run dev
```
## Usage
The application provides a simple interface where you can:
- Start/Stop a chat session
- Speak to the AI assistant through your microphone
- Receive audio responses from the assistant
- See text transcripts of the conversation
## Technical Details
This project uses:
- LlamaIndex for AI interaction management
- WebRTC for real-time audio streaming
- React for the UI
- Vite for development and building
- TypeScript for type safety
```
```
@@ -0,0 +1,28 @@
import js from "@eslint/js";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import globals from "globals";
import tseslint from "typescript-eslint";
export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
},
);
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
@@ -0,0 +1,29 @@
{
"name": "open-ai-realtime",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@eslint/js": "^9.25.0",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@vitejs/plugin-react": "^4.5.2",
"eslint": "^9.25.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.30.1",
"vite": "^6.3.5"
}
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,183 @@
import { openai } from "@llamaindex/openai";
import { liveEvents, LiveLLMSession, ModalityType } from "llamaindex";
import { useEffect, useRef, useState } from "react";
const MicIcon = ({ isConnected }: { isConnected: boolean }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
{isConnected ? (
<>
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
<line x1="12" y1="19" x2="12" y2="23" />
<line x1="8" y1="23" x2="16" y2="23" />
</>
) : (
<>
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
<path d="M19 10v2a7 7 0 0 1-14 0v-2" />
</>
)}
</svg>
);
const WaveAnimation = () => (
<div className="wave-animation">
{[...Array(3)].map((_, i) => (
<div key={i} className="wave" style={{ animationDelay: `${i * 0.2}s` }} />
))}
</div>
);
export const AudioChat = () => {
const [isConnected, setIsConnected] = useState(false);
const [messages, setMessages] = useState<
Array<{ role: string; content: string }>
>([]);
const [status, setStatus] = useState<string>("");
const audioRef = useRef<HTMLAudioElement>(null);
const sessionRef = useRef<LiveLLMSession | null>(null);
const [stream, setStream] = useState<MediaStream | null>(null);
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
useEffect(() => {
return () => {
if (stream) {
stream.getTracks().forEach((track) => track.stop());
}
};
}, [stream]);
const startChat = async () => {
try {
setStatus("Initializing microphone...");
const userStream = await navigator.mediaDevices.getUserMedia({
audio: true,
});
setStream(userStream);
setStatus("Connecting to AI...");
const apiKey = prompt("Please enter your OpenAI API key:");
if (!apiKey) {
throw new Error("API key is required");
}
// move this call to the server side for security reasons
// Do not store the API key in the frontend!
const serverllm = openai({
apiKey: apiKey,
model: "gpt-4o-realtime-preview-2025-06-03",
});
const tempKey = await serverllm.live.getEphemeralKey();
const llm = openai({
apiKey: tempKey,
model: "gpt-4o-realtime-preview-2025-06-03",
});
const session = await llm.live.connect({
systemInstruction: "You are a helpful assistant who speaks naturally.",
responseModality: [ModalityType.TEXT, ModalityType.AUDIO],
audioConfig: {
stream: userStream,
onTrack: (remoteStream) => {
if (audioRef.current && remoteStream) {
audioRef.current.srcObject = remoteStream;
audioRef.current.play().catch(console.error);
}
},
},
});
sessionRef.current = session;
setIsConnected(true);
setStatus("Connected! Listening...");
for await (const event of session.streamEvents()) {
if (liveEvents.open.include(event)) {
setMessages((prev) => [
...prev,
{
role: "user",
content: "Hello, I'm ready to chat!",
},
]);
session.sendMessage({
content: "Hello, I'm ready to chat!",
role: "user",
});
} else if (liveEvents.text.include(event)) {
setMessages((prev) => [
...prev,
{
role: "assistant",
content: event.text,
},
]);
}
}
} catch (error) {
console.error("Error starting chat:", error);
setStatus("Error connecting. Please try again.");
setIsConnected(false);
}
};
const stopChat = async () => {
setStatus("Disconnecting...");
if (sessionRef.current) {
await sessionRef.current.disconnect();
sessionRef.current = null;
}
if (stream) {
stream.getTracks().forEach((track) => track.stop());
setStream(null);
}
if (audioRef.current) {
audioRef.current.srcObject = null;
}
setIsConnected(false);
setStatus("");
};
return (
<div className="audio-chat-container">
<h1>AI Voice Chat</h1>
<div className="messages-container">
{messages.map((msg, idx) => (
<div key={idx} className={`message ${msg.role}`}>
{msg.content}
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="controls">
{status && <div className="status-indicator">{status}</div>}
<button
className={`mic-button ${isConnected ? "connected" : ""}`}
onClick={isConnected ? stopChat : startChat}
title={isConnected ? "Stop Chat" : "Start Chat"}
>
<MicIcon isConnected={isConnected} />
{isConnected && <WaveAnimation />}
</button>
<audio ref={audioRef} style={{ display: "none" }} />
</div>
</div>
);
};
@@ -0,0 +1,322 @@
:root {
--primary-color: #646cff;
--secondary-color: #535bf2;
--background-dark: #1a1a1a;
--chat-bg: #242424;
--text-primary: #ffffff;
--text-secondary: #888888;
--success-color: #4caf50;
--error-color: #f44336;
--gradient-start: #4776e6;
--gradient-end: #8e54e9;
}
body {
background-color: var(--background-dark);
color: var(--text-primary);
margin: 0;
min-height: 100vh;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
#root {
max-width: 1280px;
height: 100vh;
margin: 0 auto;
padding: 2rem;
display: flex;
align-items: center;
justify-content: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
.audio-chat-container {
display: flex;
flex-direction: column;
gap: 2rem;
width: 100%;
max-width: 800px;
height: 80vh;
margin: 0 auto;
padding: 2rem;
background: var(--chat-bg);
border-radius: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}
.audio-chat-container::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(
to right,
var(--gradient-start),
var(--gradient-end)
);
}
.audio-chat-container h1 {
font-size: 2.5rem;
margin: 0;
background: linear-gradient(
to right,
var(--gradient-start),
var(--gradient-end)
);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-align: center;
}
.messages-container {
display: flex;
flex-direction: column;
gap: 1rem;
flex: 1;
overflow-y: auto;
padding: 1rem;
border-radius: 16px;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
margin: 1rem 0;
}
.message {
padding: 1rem 1.5rem;
border-radius: 16px;
max-width: 80%;
text-align: left;
animation: messageSlide 0.3s ease-out;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
@keyframes messageSlide {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message.user {
background: linear-gradient(
135deg,
var(--gradient-start),
var(--gradient-end)
);
align-self: flex-end;
margin-left: 20%;
color: white;
}
.message.assistant {
background: rgba(255, 255, 255, 0.1);
align-self: flex-start;
margin-right: 20%;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.controls {
display: flex;
justify-content: center;
align-items: center;
padding: 2rem;
position: relative;
}
.mic-button {
width: 80px;
height: 80px;
border-radius: 50%;
border: none;
background: linear-gradient(
135deg,
var(--gradient-start),
var(--gradient-end)
);
color: white;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}
.mic-button::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.1),
rgba(255, 255, 255, 0)
);
border-radius: 50%;
transition: transform 0.3s ease;
}
.mic-button:hover {
transform: scale(1.05);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}
.mic-button:hover::before {
transform: translateY(-100%);
}
.mic-button.connected {
background: var(--error-color);
animation: pulseError 2s infinite;
}
.mic-button svg {
width: 32px;
height: 32px;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
transition: transform 0.3s ease;
}
.mic-button:hover svg {
transform: scale(1.1);
}
@keyframes pulseError {
0% {
box-shadow: 0 0 0 0 rgba(244, 67, 54, 0.4);
}
70% {
box-shadow: 0 0 0 20px rgba(244, 67, 54, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(244, 67, 54, 0);
}
}
/* Status indicator */
.status-indicator {
position: absolute;
top: -30px;
left: 50%;
transform: translateX(-50%);
font-size: 0.9rem;
color: var(--text-secondary);
opacity: 0;
transition: opacity 0.3s ease;
}
.controls:hover .status-indicator {
opacity: 1;
}
/* Scrollbar styling */
.messages-container::-webkit-scrollbar {
width: 8px;
}
.messages-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}
.messages-container::-webkit-scrollbar-thumb {
background: linear-gradient(var(--gradient-start), var(--gradient-end));
border-radius: 4px;
}
.messages-container::-webkit-scrollbar-thumb:hover {
background: linear-gradient(var(--gradient-end), var(--gradient-start));
}
/* Wave Animation */
.wave-animation {
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 4px;
}
.wave {
width: 4px;
height: 15px;
background: currentColor;
border-radius: 2px;
animation: wave 0.5s ease-in-out infinite;
}
@keyframes wave {
0%,
100% {
transform: scaleY(0.5);
}
50% {
transform: scaleY(1.5);
}
}
/* Loading state */
.mic-button.loading {
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@@ -0,0 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { AudioChat } from "./audio-chat.tsx";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<AudioChat />
</StrictMode>,
);
@@ -0,0 +1 @@
/// <reference types="vite/client" />
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}
@@ -0,0 +1,4 @@
{
"files": [],
"references": [{ "path": "./tsconfig.app.json" }]
}
@@ -0,0 +1,7 @@
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
});
+46 -46
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/examples",
"version": "0.3.18",
"version": "0.3.24",
"private": true,
"scripts": {
"lint": "eslint .",
@@ -11,51 +11,51 @@
"@azure/cosmos": "^4.1.1",
"@azure/identity": "^4.4.1",
"@azure/search-documents": "^12.1.0",
"@llamaindex/anthropic": "^0.3.10",
"@llamaindex/assemblyai": "^0.1.7",
"@llamaindex/astra": "^0.0.22",
"@llamaindex/azure": "^0.1.19",
"@llamaindex/chroma": "^0.0.22",
"@llamaindex/clip": "^0.0.58",
"@llamaindex/cloud": "^4.0.12",
"@llamaindex/cohere": "^0.0.22",
"@llamaindex/core": "^0.6.8",
"@llamaindex/deepinfra": "^0.0.58",
"@llamaindex/deepseek": "^0.0.18",
"@llamaindex/discord": "^0.1.7",
"@llamaindex/elastic-search": "^0.1.8",
"@llamaindex/anthropic": "^0.3.13",
"@llamaindex/assemblyai": "^0.1.10",
"@llamaindex/astra": "^0.0.25",
"@llamaindex/azure": "^0.1.22",
"@llamaindex/chroma": "^0.0.25",
"@llamaindex/clip": "^0.0.61",
"@llamaindex/cloud": "^4.0.15",
"@llamaindex/cohere": "^0.0.25",
"@llamaindex/core": "^0.6.11",
"@llamaindex/deepinfra": "^0.0.61",
"@llamaindex/deepseek": "^0.0.21",
"@llamaindex/discord": "^0.1.10",
"@llamaindex/elastic-search": "^0.1.11",
"@llamaindex/env": "^0.1.30",
"@llamaindex/firestore": "^1.0.15",
"@llamaindex/fireworks": "^0.0.18",
"@llamaindex/google": "^0.3.5",
"@llamaindex/groq": "^0.0.73",
"@llamaindex/huggingface": "^0.1.12",
"@llamaindex/jinaai": "^0.0.18",
"@llamaindex/milvus": "^0.1.17",
"@llamaindex/mistral": "^0.1.8",
"@llamaindex/mixedbread": "^0.0.22",
"@llamaindex/mongodb": "^0.0.23",
"@llamaindex/node-parser": "^2.0.8",
"@llamaindex/notion": "^0.1.7",
"@llamaindex/ollama": "^0.1.8",
"@llamaindex/openai": "^0.4.2",
"@llamaindex/perplexity": "^0.0.15",
"@llamaindex/pinecone": "^0.1.8",
"@llamaindex/portkey-ai": "^0.0.50",
"@llamaindex/postgres": "^0.0.51",
"@llamaindex/qdrant": "^0.1.18",
"@llamaindex/readers": "^3.1.6",
"@llamaindex/replicate": "^0.0.50",
"@llamaindex/supabase": "^0.1.7",
"@llamaindex/together": "^0.0.18",
"@llamaindex/tools": "^0.0.13",
"@llamaindex/upstash": "^0.0.22",
"@llamaindex/vercel": "^0.1.8",
"@llamaindex/vllm": "^0.0.44",
"@llamaindex/voyage-ai": "^1.0.14",
"@llamaindex/weaviate": "^0.0.22",
"@llamaindex/workflow": "^1.1.5",
"@llamaindex/xai": "workspace:^0.0.5",
"@llamaindex/firestore": "^1.0.18",
"@llamaindex/fireworks": "^0.0.21",
"@llamaindex/google": "^0.3.10",
"@llamaindex/groq": "^0.0.76",
"@llamaindex/huggingface": "^0.1.15",
"@llamaindex/jinaai": "^0.0.21",
"@llamaindex/milvus": "^0.1.20",
"@llamaindex/mistral": "^0.1.11",
"@llamaindex/mixedbread": "^0.0.25",
"@llamaindex/mongodb": "^0.0.26",
"@llamaindex/node-parser": "^2.0.11",
"@llamaindex/notion": "^0.1.10",
"@llamaindex/ollama": "^0.1.11",
"@llamaindex/openai": "^0.4.5",
"@llamaindex/perplexity": "^0.0.18",
"@llamaindex/pinecone": "^0.1.11",
"@llamaindex/portkey-ai": "^0.0.53",
"@llamaindex/postgres": "^0.0.54",
"@llamaindex/qdrant": "^0.1.21",
"@llamaindex/readers": "^3.1.10",
"@llamaindex/replicate": "^0.0.53",
"@llamaindex/supabase": "^0.1.10",
"@llamaindex/together": "^0.0.21",
"@llamaindex/tools": "^0.1.0",
"@llamaindex/upstash": "^0.0.25",
"@llamaindex/vercel": "^0.1.11",
"@llamaindex/vllm": "^0.0.47",
"@llamaindex/voyage-ai": "^1.0.17",
"@llamaindex/weaviate": "^0.0.26",
"@llamaindex/workflow": "^1.1.10",
"@llamaindex/xai": "workspace:^0.0.8",
"@notionhq/client": "^2.2.15",
"@pinecone-database/pinecone": "^4.0.0",
"@vercel/postgres": "^0.10.0",
@@ -64,7 +64,7 @@
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"js-tiktoken": "^1.0.14",
"llamaindex": "^0.11.4",
"llamaindex": "^0.11.9",
"mongodb": "6.7.0",
"postgres": "^3.4.4",
"wikipedia": "^2.1.2",
+5
View File
@@ -1,3 +1,4 @@
import { OpenAIEmbedding } from "@llamaindex/openai";
import {
Document,
SentenceSplitter,
@@ -7,6 +8,10 @@ import {
import { OldSentenceSplitter } from "./old-sentence-splitter";
export const STORAGE_DIR = "./data";
Settings.embedModel = new OpenAIEmbedding({
model: "text-embedding-3-small",
});
// Update node parser
(async () => {
// generate a document with a very long sentence (9000 words long)
+2 -1
View File
@@ -15,7 +15,8 @@
"start:llamaparse-json": "node --import tsx ./src/llamaparse-json.ts",
"start:discord": "node --import tsx ./src/discord.ts",
"start:json": "node --import tsx ./src/json.ts",
"start:obsidian": "node --import tsx ./src/obsidian.ts"
"start:obsidian": "node --import tsx ./src/obsidian.ts",
"start:xml": "node --import tsx ./src/xml.ts"
},
"dependencies": {
"@llamaindex/cloud": "workspace:* || ^2.0.24",
+16
View File
@@ -0,0 +1,16 @@
import { XMLReader } from "@llamaindex/readers/xml";
async function main() {
// Load PDF
const reader = new XMLReader({
splitLevel: 2,
});
const documents = await reader.loadData("../data/company.xml");
for (const doc of documents) {
console.log(doc.text);
console.log("----");
}
}
main().catch(console.error);
+7 -1
View File
@@ -18,5 +18,11 @@
"module": "commonjs"
}
},
"include": ["./**/*.ts"]
"include": ["./**/*.ts"],
"exclude": [
"node_modules",
"dist",
"models/openai/live/browser/open-ai-realtime",
"**/browser/**"
]
}
+31
View File
@@ -1,5 +1,36 @@
# @llamaindex/autotool
## 8.0.9
### Patch Changes
- llamaindex@0.11.9
## 8.0.8
### Patch Changes
- llamaindex@0.11.8
## 8.0.7
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 8.0.6
### Patch Changes
- llamaindex@0.11.6
## 8.0.5
### Patch Changes
- llamaindex@0.11.5
## 8.0.4
### Patch Changes
@@ -1,5 +1,41 @@
# @llamaindex/autotool-01-node-example
## 0.0.117
### Patch Changes
- llamaindex@0.11.9
- @llamaindex/autotool@8.0.9
## 0.0.116
### Patch Changes
- llamaindex@0.11.8
- @llamaindex/autotool@8.0.8
## 0.0.115
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
- @llamaindex/autotool@8.0.7
## 0.0.114
### Patch Changes
- llamaindex@0.11.6
- @llamaindex/autotool@8.0.6
## 0.0.113
### Patch Changes
- llamaindex@0.11.5
- @llamaindex/autotool@8.0.5
## 0.0.112
### Patch Changes
@@ -13,5 +13,5 @@
"scripts": {
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
},
"version": "0.0.112"
"version": "0.0.117"
}
+1 -1
View File
@@ -6,7 +6,7 @@
"url": "git+https://github.com/run-llama/LlamaIndexTS.git",
"directory": "packages/autotool"
},
"version": "8.0.4",
"version": "8.0.9",
"description": "auto transpile your JS function to LLM Agent compatible",
"files": [
"dist",
+24
View File
@@ -1,5 +1,29 @@
# @llamaindex/cloud
## 4.0.15
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 4.0.14
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 4.0.13
### Patch Changes
- Updated dependencies [71598f8]
- @llamaindex/core@0.6.9
## 4.0.12
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloud",
"version": "4.0.12",
"version": "4.0.15",
"type": "module",
"license": "MIT",
"scripts": {
+21
View File
@@ -1,5 +1,26 @@
# @llamaindex/core
## 0.6.11
### Patch Changes
- a89e187: Feat: added custom abbreviations to sentence splitter
- 62699b7: Improve performance of sentence splitter
- c5b2691: Add more Acronyms on SentenceSplitter
- d8ac8d3: Feat: add support for openai realtime API
## 0.6.10
### Patch Changes
- 1b5af14: fix: jsonToNode for image nodes
## 0.6.9
### Patch Changes
- 71598f8: Added interrupted, generationComplete and turnComplete event support in the live api
## 0.6.8
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/core",
"type": "module",
"version": "0.6.8",
"version": "0.6.11",
"description": "LlamaIndex Core Module",
"exports": {
"./agent": {
+2 -1
View File
@@ -1,4 +1,5 @@
import { extractText, streamConverter } from "../utils";
import { extractText } from "../utils/llms";
import { streamConverter } from "../utils/stream";
import type {
ChatResponse,
ChatResponseChunk,
+4 -1
View File
@@ -1,6 +1,9 @@
export { BaseLLM, ToolCallLLM } from "./base";
export { LiveLLM, LiveLLMSession, liveEvents, type LiveEvent } from "./live";
export { LiveLLM, LiveLLMCapability, LiveLLMSession } from "./live/live";
export { liveEvents, type LiveEvent } from "./live/live-types";
export type { MessageSender } from "./live/sender";
export type {
AudioConfig,
BaseTool,
BaseToolWithCall,
ChatMessage,
-87
View File
@@ -1,87 +0,0 @@
import type {
ChatMessage,
LiveConnectConfig,
MessageContentAudioDetail,
MessageContentTextDetail,
} from "./type";
export type OpenEvent = { type: "open" };
export type AudioEvent = MessageContentAudioDetail;
export type TextEvent = MessageContentTextDetail;
export type ErrorEvent = { type: "error"; error: unknown };
export type CloseEvent = { type: "close" };
export type SetupCompleteEvent = { type: "setupComplete" };
export type LiveEvent =
| OpenEvent
| AudioEvent
| TextEvent
| ErrorEvent
| CloseEvent
| SetupCompleteEvent;
export const liveEvents = {
open: { include: (e: LiveEvent): e is OpenEvent => e.type === "open" },
audio: {
include: (e: LiveEvent): e is AudioEvent => e.type === "audio",
},
text: { include: (e: LiveEvent): e is TextEvent => e.type === "text" },
error: {
include: (e: LiveEvent): e is ErrorEvent => e.type === "error",
},
close: {
include: (e: LiveEvent): e is CloseEvent => e.type === "close",
},
setupComplete: {
include: (e: LiveEvent): e is SetupCompleteEvent =>
e.type === "setupComplete",
},
};
export abstract class LiveLLMSession {
protected eventQueue: LiveEvent[] = [];
protected eventResolvers: ((value: LiveEvent) => void)[] = [];
protected closed = false;
abstract sendMessage(message: ChatMessage): void;
async *streamEvents(): AsyncIterable<LiveEvent> {
while (true) {
const event = await this.nextEvent();
if (event === undefined) {
break;
}
yield event;
}
}
abstract disconnect(): Promise<void>;
protected async nextEvent(): Promise<LiveEvent | undefined> {
if (this.eventQueue.length) {
return Promise.resolve(this.eventQueue.shift());
}
return new Promise((resolve) => {
this.eventResolvers.push(resolve);
});
}
//Uses an async queue to send events to the client
// if the consumer is waiting for an event, it will be resolved immediately
// otherwise, the event will be queued up and sent when the consumer is ready
pushEventToQueue(event: LiveEvent) {
if (this.eventResolvers.length) {
//resolving the promise with the event
this.eventResolvers.shift()!(event);
} else {
this.eventQueue.push(event);
}
}
}
export abstract class LiveLLM {
abstract connect(config?: LiveConnectConfig): Promise<LiveLLMSession>;
}
+63
View File
@@ -0,0 +1,63 @@
import type {
MessageContentAudioDetail,
MessageContentTextDetail,
} from "../type";
export type OpenEvent = { type: "open" };
export type AudioEvent = MessageContentAudioDetail;
export type TextEvent = MessageContentTextDetail;
export type ErrorEvent = { type: "error"; error: unknown };
export type CloseEvent = { type: "close" };
export type SetupCompleteEvent = { type: "setupComplete" };
// a client message has interrupted current model generation
export type InterruptedEvent = { type: "interrupted" };
export type GenerationCompleteEvent = { type: "generationComplete" };
export type TurnCompleteEvent = { type: "turnComplete" };
export type LiveEvent =
| OpenEvent
| AudioEvent
| TextEvent
| ErrorEvent
| CloseEvent
| SetupCompleteEvent
| InterruptedEvent
| GenerationCompleteEvent
| TurnCompleteEvent;
export const liveEvents = {
open: { include: (e: LiveEvent): e is OpenEvent => e.type === "open" },
audio: {
include: (e: LiveEvent): e is AudioEvent => e.type === "audio",
},
text: { include: (e: LiveEvent): e is TextEvent => e.type === "text" },
error: {
include: (e: LiveEvent): e is ErrorEvent => e.type === "error",
},
close: {
include: (e: LiveEvent): e is CloseEvent => e.type === "close",
},
setupComplete: {
include: (e: LiveEvent): e is SetupCompleteEvent =>
e.type === "setupComplete",
},
interrupted: {
include: (e: LiveEvent): e is InterruptedEvent => e.type === "interrupted",
},
generationComplete: {
include: (e: LiveEvent): e is GenerationCompleteEvent =>
e.type === "generationComplete",
},
turnComplete: {
include: (e: LiveEvent): e is TurnCompleteEvent =>
e.type === "turnComplete",
},
};
+124
View File
@@ -0,0 +1,124 @@
import type {
ChatMessage,
LiveConnectConfig,
MessageContentAudioDetail,
MessageContentDetail,
MessageContentImageDataDetail,
MessageContentVideoDetail,
} from "../type";
import type { LiveEvent } from "./live-types";
import type { MessageSender } from "./sender";
export enum LiveLLMCapability {
EPHEMERAL_KEY = "ephemeral_key",
AUDIO_CONFIG = "audio_config",
}
export abstract class LiveLLMSession {
protected eventQueue: LiveEvent[] = [];
protected eventResolvers: ((value: LiveEvent) => void)[] = [];
closed = false;
abstract get messageSender(): MessageSender;
private isTextMessage(content: MessageContentDetail) {
return content.type === "text";
}
private isAudioMessage(
content: MessageContentDetail,
): content is MessageContentAudioDetail {
return content.type === "audio";
}
private isImageMessage(
content: MessageContentDetail,
): content is MessageContentImageDataDetail {
return content.type === "image";
}
private isVideoMessage(
content: MessageContentDetail,
): content is MessageContentVideoDetail {
return content.type === "video";
}
sendMessage(message: ChatMessage) {
const { content, role } = message;
if (!Array.isArray(content)) {
this.messageSender.sendTextMessage(content, role);
} else {
for (const item of content) {
this.processMessage(item, role);
}
}
}
private processMessage(message: MessageContentDetail, role?: string) {
if (this.isTextMessage(message)) {
this.messageSender.sendTextMessage(message.text, role);
} else if (
this.isAudioMessage(message) &&
this.messageSender.sendAudioMessage
) {
this.messageSender.sendAudioMessage(message, role);
} else if (
this.isImageMessage(message) &&
this.messageSender.sendImageMessage
) {
this.messageSender.sendImageMessage(message, role);
} else if (
this.isVideoMessage(message) &&
this.messageSender.sendVideoMessage
) {
this.messageSender.sendVideoMessage(message, role);
}
}
async *streamEvents(): AsyncIterable<LiveEvent> {
while (true) {
const event = await this.nextEvent();
if (event === undefined) {
break;
}
yield event;
}
}
abstract disconnect(): Promise<void>;
protected async nextEvent(): Promise<LiveEvent | undefined> {
if (this.eventQueue.length) {
return Promise.resolve(this.eventQueue.shift());
}
return new Promise((resolve) => {
this.eventResolvers.push(resolve);
});
}
//Uses an async queue to send events to the client
// if the consumer is waiting for an event, it will be resolved immediately
// otherwise, the event will be queued up and sent when the consumer is ready
pushEventToQueue(event: LiveEvent) {
if (this.eventResolvers.length) {
//resolving the promise with the event
this.eventResolvers.shift()!(event);
} else {
this.eventQueue.push(event);
}
}
}
export abstract class LiveLLM {
/**
* Set of capabilities supported by this implementation.
* Override in subclasses as needed.
*/
capabilities: Set<LiveLLMCapability> = new Set();
abstract connect(config?: LiveConnectConfig): Promise<LiveLLMSession>;
abstract getEphemeralKey(): Promise<string | undefined>;
hasCapability(capability: LiveLLMCapability): boolean {
return this.capabilities.has(capability);
}
}
+15
View File
@@ -0,0 +1,15 @@
import type {
MessageContentAudioDetail,
MessageContentImageDataDetail,
MessageContentVideoDetail,
} from "../type";
export interface MessageSender {
sendTextMessage(message: string, role?: string): void;
sendAudioMessage?(content: MessageContentAudioDetail, role?: string): void;
sendImageMessage?(
content: MessageContentImageDataDetail,
role?: string,
): void;
sendVideoMessage?(content: MessageContentVideoDetail, role?: string): void;
}
+6
View File
@@ -290,8 +290,14 @@ export type ToolOutput = {
isError: boolean;
};
export interface AudioConfig {
stream?: MediaStream;
onTrack?: (track: MediaStream | null) => void;
}
export interface LiveConnectConfig {
tools?: BaseTool[];
responseModality?: ModalityType[];
systemInstruction?: string;
audioConfig?: AudioConfig;
}
@@ -0,0 +1,42 @@
export const abbreviations = {
english: [
"i.e.",
"etc.",
"vs.",
"Inc.",
"A.S.A.P.",
"Mr.",
"Mrs.",
"Ms.",
"Dr.",
"Prof.",
"Sr.",
"Jr.",
"Tel.",
"a.m.",
"p.m.",
"Art.",
],
spanish: [
"Sr.",
"Sres.",
"Srs.",
"Sra.",
"Sras.",
"Srta.",
"Srtas.",
"Dr.",
"Drs.",
"Dra.",
"Dras.",
"Prof.",
"Profs.",
"Profa.",
"Profas.",
"Ing.",
"Lic.",
"Arq.",
"Ab.",
"Abs.",
],
};
@@ -12,11 +12,7 @@ import {
type TextSplitterFn,
} from "./utils";
type _Split = {
text: string;
isSentence: boolean;
tokenSize: number;
};
type _Split = [string, boolean, number]; // [text, isSentence, tokenSize]
/**
* Parse text with a preference for complete sentences.
@@ -42,6 +38,11 @@ export class SentenceSplitter extends MetadataAwareTextSplitter {
* Backup regex for splitting into sentences.
*/
secondaryChunkingRegex: string = "[^,.;。?!]+[,.;。?!]?";
/**
* Extra abbreviations to consider while splitting into sentences.
* For example, for contracts, you may want to consider "LLC." as an important abbreviation
*/
extraAbbreviations: string[] | undefined = [];
#chunkingTokenizerFn = splitBySentenceTokenizer();
#splitFns: Set<TextSplitterFn> = new Set();
@@ -59,7 +60,11 @@ export class SentenceSplitter extends MetadataAwareTextSplitter {
this.separator = parsedParams.separator;
this.paragraphSeparator = parsedParams.paragraphSeparator;
this.secondaryChunkingRegex = parsedParams.secondaryChunkingRegex;
this.extraAbbreviations = parsedParams.extraAbbreviations;
}
this.#chunkingTokenizerFn = splitBySentenceTokenizer(
this.extraAbbreviations,
);
this.#tokenizer = params?.tokenizer ?? Settings.tokenizer;
this.#splitFns.add(splitBySep(this.paragraphSeparator));
this.#splitFns.add(this.#chunkingTokenizerFn);
@@ -105,34 +110,26 @@ export class SentenceSplitter extends MetadataAwareTextSplitter {
return chunks;
}
#split(text: string, chunkSize: number): _Split[] {
const tokenSize = this.tokenSize(text);
if (tokenSize <= chunkSize) {
return [
{
text,
isSentence: true,
tokenSize,
},
];
*#split(
text: string,
chunkSize: number,
tokenSize?: number,
): Generator<_Split> {
const _tokenSize = tokenSize ?? this.tokenSize(text);
if (_tokenSize <= chunkSize) {
yield [text, true, _tokenSize];
return;
}
const [textSplitsByFns, isSentence] = this.#getSplitsByFns(text);
const textSplits: _Split[] = [];
for (const textSplit of textSplitsByFns) {
const tokenSize = this.tokenSize(textSplit);
if (tokenSize <= chunkSize) {
textSplits.push({
text: textSplit,
isSentence,
tokenSize,
});
const textSplitTokenSize = this.tokenSize(textSplit);
if (textSplitTokenSize <= chunkSize) {
yield [textSplit, isSentence, textSplitTokenSize];
} else {
const recursiveTextSplits = this.#split(textSplit, chunkSize);
textSplits.push(...recursiveTextSplits);
yield* this.#split(textSplit, chunkSize, textSplitTokenSize);
}
}
return textSplits;
}
#getSplitsByFns(text: string): [splits: string[], isSentence: boolean] {
@@ -151,7 +148,7 @@ export class SentenceSplitter extends MetadataAwareTextSplitter {
return [[text], true];
}
#merge(splits: _Split[], chunkSize: number): string[] {
#merge(splits: Iterable<_Split>, chunkSize: number): string[] {
const chunks: string[] = [];
let currentChunk: [string, number][] = [];
let lastChunk: [string, number][] = [];
@@ -177,23 +174,26 @@ export class SentenceSplitter extends MetadataAwareTextSplitter {
}
};
while (splits.length > 0) {
const curSplit = splits[0]!;
if (curSplit.tokenSize > chunkSize) {
const splitsIterator = splits[Symbol.iterator]();
let currentSplitResult = splitsIterator.next();
while (!currentSplitResult.done) {
const [text, isSentence, tokenSize] = currentSplitResult.value;
if (tokenSize > chunkSize) {
throw new Error("Single token exceeded chunk size");
}
if (currentChunkLength + curSplit.tokenSize > chunkSize && !newChunk) {
if (currentChunkLength + tokenSize > chunkSize && !newChunk) {
closeChunk();
} else {
if (
curSplit.isSentence ||
currentChunkLength + curSplit.tokenSize <= chunkSize ||
isSentence ||
currentChunkLength + tokenSize <= chunkSize ||
newChunk
) {
currentChunkLength += curSplit.tokenSize;
currentChunk.push([curSplit.text, curSplit.tokenSize]);
splits.shift();
currentChunkLength += tokenSize;
currentChunk.push([text, tokenSize]);
newChunk = false;
currentSplitResult = splitsIterator.next();
} else {
closeChunk();
}
+10 -13
View File
@@ -1,3 +1,4 @@
import { abbreviations } from "./abbreviations";
import type { TextSplitter } from "./base";
import SentenceTokenizer from "./sentence_tokenizer";
@@ -34,19 +35,15 @@ export const splitByChar = (): TextSplitterFn => {
return (text: string) => text.split("");
};
let sentenceTokenizer: SentenceTokenizer | null = null;
export const splitBySentenceTokenizer = (): TextSplitterFn => {
if (!sentenceTokenizer) {
sentenceTokenizer = new SentenceTokenizer([
"i.e.",
"etc.",
"vs.",
"Inc.",
"A.S.A.P.",
]);
}
const tokenizer = sentenceTokenizer;
export const splitBySentenceTokenizer = (
extraAbbreviations: string[] | undefined = [],
): TextSplitterFn => {
const tokenizer = new SentenceTokenizer([
...abbreviations.english,
...abbreviations.spanish,
// Add the extra abbreviations provided by the user, e.g. for business-specific context
...extraAbbreviations,
]);
return (text: string) => {
try {
return tokenizer.tokenize(text);
+2
View File
@@ -347,6 +347,8 @@ export function jsonToNode(json: any, type?: ObjectType) {
return new Document(json);
case ObjectType.IMAGE_DOCUMENT:
return new ImageDocument(json);
case ObjectType.IMAGE:
return new ImageNode(json);
default:
throw new Error(`Invalid node type: ${nodeType}`);
}
+7
View File
@@ -51,6 +51,13 @@ export const sentenceSplitterSchema = z
})
.optional()
.default("[^,.;。?!]+[,.;。?!]?"),
extraAbbreviations: z
.array(z.string(), {
description:
"Extra abbreviations to consider while splitting into sentences. For example, for contracts, you may want to consider 'LLC.' as an important abbreviation",
})
.optional()
.default([]),
})
.refine(
(data) => data.chunkOverlap < data.chunkSize,
+1 -13
View File
@@ -14,19 +14,6 @@ export const isIterable = (obj: unknown): obj is Iterable<unknown> => {
return obj != null && typeof obj === "object" && Symbol.iterator in obj;
};
export async function* streamConverter<S, D>(
stream: AsyncIterable<S>,
converter: (s: S) => D | null,
): AsyncIterable<D> {
for await (const data of stream) {
const newData = converter(data);
if (newData === null) {
return;
}
yield newData;
}
}
export async function* streamCallbacks<S>(
stream: AsyncIterable<S>,
callbacks: {
@@ -86,3 +73,4 @@ export {
export { MockLLM } from "./mock";
export { objectEntries } from "./object-entries";
export * from "./stream";
+12
View File
@@ -0,0 +1,12 @@
export async function* streamConverter<S, D>(
stream: AsyncIterable<S>,
converter: (s: S) => D | null,
): AsyncIterable<D> {
for await (const data of stream) {
const newData = converter(data);
if (newData === null) {
return;
}
yield newData;
}
}
@@ -75,6 +75,22 @@ describe("sentence splitter", () => {
expect(splits).toEqual(["This is a sentence. This is another sentence."]);
});
test("overall split long text", () => {
const sentenceSplitter = new SentenceSplitter({
chunkSize: 10,
chunkOverlap: 0,
});
const splits = sentenceSplitter.splitText(
"The first short sentence. The first long long long sentence. The second short sentence. The second long long long sentence.",
);
expect(splits).toEqual([
"The first short sentence.",
"The first long long long sentence.",
"The second short sentence.",
"The second long long long sentence.",
]);
});
test("doesn't split decimals", () => {
const sentenceSplitter = new SentenceSplitter({
chunkSize: 5,
@@ -91,6 +107,39 @@ describe("sentence splitter", () => {
]);
});
test("doesn't split basic abbreviations", () => {
const sentenceSplitter = new SentenceSplitter({
chunkSize: 15,
chunkOverlap: 0,
});
const splits = sentenceSplitter.splitText(
"This is a sentence of Broda Noel. This is the sentence of Sr. Broda Noel. This is a sentence of somebody else",
);
expect(splits).toEqual([
"This is a sentence of Broda Noel.",
"This is the sentence of Sr. Broda Noel.",
"This is a sentence of somebody else",
]);
});
test("doesn't split extra abbreviations", () => {
const sentenceSplitter = new SentenceSplitter({
chunkSize: 10,
chunkOverlap: 0,
extraAbbreviations: ["S.A."],
});
const splits = sentenceSplitter.splitText(
"This is a sentence. The S.A. Broda Company. This is another sentence",
);
expect(splits).toEqual([
"This is a sentence.",
"The S.A. Broda Company.",
"This is another sentence",
]);
});
test("splits cjk", () => {
const sentenceSplitter = new SentenceSplitter({
chunkSize: 30,
+31
View File
@@ -1,5 +1,36 @@
# @llamaindex/experimental
## 0.0.186
### Patch Changes
- llamaindex@0.11.9
## 0.0.185
### Patch Changes
- llamaindex@0.11.8
## 0.0.184
### Patch Changes
- Updated dependencies [3c857f4]
- llamaindex@0.11.7
## 0.0.183
### Patch Changes
- llamaindex@0.11.6
## 0.0.182
### Patch Changes
- llamaindex@0.11.5
## 0.0.181
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/experimental",
"description": "Experimental package for LlamaIndexTS",
"version": "0.0.181",
"version": "0.0.186",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
+50
View File
@@ -1,5 +1,55 @@
# llamaindex
## 0.11.9
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
- @llamaindex/cloud@4.0.15
- @llamaindex/node-parser@2.0.11
- @llamaindex/workflow@1.1.10
## 0.11.8
### Patch Changes
- Updated dependencies [8a51c16]
- Updated dependencies [1b5af14]
- @llamaindex/workflow@1.1.9
- @llamaindex/core@0.6.10
- @llamaindex/cloud@4.0.14
- @llamaindex/node-parser@2.0.10
## 0.11.7
### Patch Changes
- 3c857f4: chore: move ajv to dev deps
- Updated dependencies [dbd857f]
- @llamaindex/workflow@1.1.8
## 0.11.6
### Patch Changes
- Updated dependencies [40161fe]
- @llamaindex/workflow@1.1.7
## 0.11.5
### Patch Changes
- Updated dependencies [766054b]
- Updated dependencies [71598f8]
- @llamaindex/workflow@1.1.6
- @llamaindex/core@0.6.9
- @llamaindex/cloud@4.0.13
- @llamaindex/node-parser@2.0.9
## 0.11.4
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "llamaindex",
"version": "0.11.4",
"version": "0.11.9",
"license": "MIT",
"type": "module",
"keywords": [
@@ -24,14 +24,14 @@
"@llamaindex/core": "workspace:*",
"@llamaindex/env": "workspace:*",
"@llamaindex/node-parser": "workspace:*",
"@llamaindex/workflow": "1.1.5",
"@llamaindex/workflow": "workspace:*",
"@types/lodash": "^4.17.7",
"@types/node": "^22.9.0",
"ajv": "^8.17.1",
"lodash": "^4.17.21",
"magic-bytes.js": "^1.10.0"
},
"devDependencies": {
"ajv": "^8.17.1",
"@types/node": "^22.9.0"
},
"engines": {
+19
View File
@@ -1,5 +1,24 @@
# @llamaindex/core-test
## 0.1.6
### Patch Changes
- Updated dependencies [d8ac8d3]
- @llamaindex/openai@0.4.5
## 0.1.5
### Patch Changes
- @llamaindex/openai@0.4.4
## 0.1.4
### Patch Changes
- @llamaindex/openai@0.4.3
## 0.1.3
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/llamaindex-test",
"private": true,
"version": "0.1.3",
"version": "0.1.6",
"type": "module",
"scripts": {
"test": "vitest run"
+24
View File
@@ -1,5 +1,29 @@
# @llamaindex/node-parser
## 2.0.11
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 2.0.10
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 2.0.9
### Patch Changes
- Updated dependencies [71598f8]
- @llamaindex/core@0.6.9
## 2.0.8
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/node-parser",
"version": "2.0.8",
"version": "2.0.11",
"description": "Node parser for LlamaIndex",
"type": "module",
"exports": {
+24
View File
@@ -1,5 +1,29 @@
# @llamaindex/anthropic
## 0.3.13
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 0.3.12
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 0.3.11
### Patch Changes
- Updated dependencies [71598f8]
- @llamaindex/core@0.6.9
## 0.3.10
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/anthropic",
"description": "Anthropic Adapter for LlamaIndex",
"version": "0.3.10",
"version": "0.3.13",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",

Some files were not shown because too many files have changed in this diff Show More