Compare commits

...

19 Commits

Author SHA1 Message Date
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
180 changed files with 5476 additions and 1064 deletions
+41
View File
@@ -1,5 +1,46 @@
# @llamaindex/doc
## 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
+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.25",
"version": "0.2.28",
"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-docs": "0.0.3",
"@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",
+5
View File
@@ -13,6 +13,11 @@ export const docs = defineDocs({
"./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,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.
@@ -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,5 +1,24 @@
# @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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloudflare-worker-agent-test",
"version": "0.0.167",
"version": "0.0.170",
"type": "module",
"private": true,
"scripts": {
@@ -1,5 +1,17 @@
# @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
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/llama-parse-browser-test",
"private": true,
"version": "0.0.68",
"version": "0.0.70",
"type": "module",
"scripts": {
"dev": "vite",
+19
View File
@@ -1,5 +1,24 @@
# @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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-agent-test",
"version": "0.1.167",
"version": "0.1.170",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,24 @@
# 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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/nextjs-edge-runtime-test",
"version": "0.1.166",
"version": "0.1.169",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,28 @@
# @llamaindex/next-node-runtime
## 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
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/next-node-runtime-test",
"version": "0.1.34",
"version": "0.1.37",
"private": true,
"scripts": {
"dev": "next dev",
@@ -1,5 +1,24 @@
# 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
@@ -1,7 +1,7 @@
{
"name": "vite-import-llamaindex",
"private": true,
"version": "0.0.33",
"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,24 @@
# @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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/waku-query-engine-test",
"version": "0.0.167",
"version": "0.0.170",
"type": "module",
"private": true,
"scripts": {
+123
View File
@@ -1,5 +1,128 @@
# examples
## 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
+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";
+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);
+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.19",
"version": "0.3.23",
"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.11",
"@llamaindex/assemblyai": "^0.1.8",
"@llamaindex/astra": "^0.0.23",
"@llamaindex/azure": "^0.1.20",
"@llamaindex/chroma": "^0.0.23",
"@llamaindex/clip": "^0.0.59",
"@llamaindex/cloud": "^4.0.13",
"@llamaindex/cohere": "^0.0.23",
"@llamaindex/core": "^0.6.9",
"@llamaindex/deepinfra": "^0.0.59",
"@llamaindex/deepseek": "^0.0.19",
"@llamaindex/discord": "^0.1.8",
"@llamaindex/elastic-search": "^0.1.9",
"@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.16",
"@llamaindex/fireworks": "^0.0.19",
"@llamaindex/google": "^0.3.6",
"@llamaindex/groq": "^0.0.74",
"@llamaindex/huggingface": "^0.1.13",
"@llamaindex/jinaai": "^0.0.19",
"@llamaindex/milvus": "^0.1.18",
"@llamaindex/mistral": "^0.1.9",
"@llamaindex/mixedbread": "^0.0.23",
"@llamaindex/mongodb": "^0.0.24",
"@llamaindex/node-parser": "^2.0.9",
"@llamaindex/notion": "^0.1.8",
"@llamaindex/ollama": "^0.1.9",
"@llamaindex/openai": "^0.4.3",
"@llamaindex/perplexity": "^0.0.16",
"@llamaindex/pinecone": "^0.1.9",
"@llamaindex/portkey-ai": "^0.0.51",
"@llamaindex/postgres": "^0.0.52",
"@llamaindex/qdrant": "^0.1.19",
"@llamaindex/readers": "^3.1.7",
"@llamaindex/replicate": "^0.0.51",
"@llamaindex/supabase": "^0.1.8",
"@llamaindex/together": "^0.0.19",
"@llamaindex/tools": "^0.0.14",
"@llamaindex/upstash": "^0.0.23",
"@llamaindex/vercel": "^0.1.9",
"@llamaindex/vllm": "^0.0.45",
"@llamaindex/voyage-ai": "^1.0.15",
"@llamaindex/weaviate": "^0.0.23",
"@llamaindex/workflow": "^1.1.6",
"@llamaindex/xai": "workspace:^0.0.6",
"@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.9",
"@llamaindex/replicate": "^0.0.53",
"@llamaindex/supabase": "^0.1.10",
"@llamaindex/together": "^0.0.21",
"@llamaindex/tools": "^0.0.17",
"@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.5",
"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)
+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/**"
]
}
+19
View File
@@ -1,5 +1,24 @@
# @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
@@ -1,5 +1,27 @@
# @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
@@ -13,5 +13,5 @@
"scripts": {
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
},
"version": "0.0.114"
"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.6",
"version": "8.0.9",
"description": "auto transpile your JS function to LLM Agent compatible",
"files": [
"dist",
+17
View File
@@ -1,5 +1,22 @@
# @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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/cloud",
"version": "4.0.13",
"version": "4.0.15",
"type": "module",
"license": "MIT",
"scripts": {
+15
View File
@@ -1,5 +1,20 @@
# @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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/core",
"type": "module",
"version": "0.6.9",
"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,
@@ -1,9 +1,7 @@
import type {
ChatMessage,
LiveConnectConfig,
MessageContentAudioDetail,
MessageContentTextDetail,
} from "./type";
} from "../type";
export type OpenEvent = { type: "open" };
@@ -63,45 +61,3 @@ export const liveEvents = {
e.type === "turnComplete",
},
};
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>;
}
+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,
+19
View File
@@ -1,5 +1,24 @@
# @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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/experimental",
"description": "Experimental package for LlamaIndexTS",
"version": "0.0.183",
"version": "0.0.186",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
+32
View File
@@ -1,5 +1,37 @@
# 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "llamaindex",
"version": "0.11.6",
"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.7",
"@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": {
+13
View File
@@ -1,5 +1,18 @@
# @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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/llamaindex-test",
"private": true,
"version": "0.1.4",
"version": "0.1.6",
"type": "module",
"scripts": {
"test": "vitest run"
+17
View File
@@ -1,5 +1,22 @@
# @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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@llamaindex/node-parser",
"version": "2.0.9",
"version": "2.0.11",
"description": "Node parser for LlamaIndex",
"type": "module",
"exports": {
+17
View File
@@ -1,5 +1,22 @@
# @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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/anthropic",
"description": "Anthropic Adapter for LlamaIndex",
"version": "0.3.11",
"version": "0.3.13",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
@@ -1,5 +1,22 @@
# @llamaindex/assemblyai
## 0.1.10
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 0.1.9
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 0.1.8
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/assemblyai",
"description": "AssemblyAI Reader for LlamaIndex",
"version": "0.1.8",
"version": "0.1.10",
"type": "module",
"types": "dist/index.d.ts",
"main": "dist/index.cjs",
+17
View File
@@ -1,5 +1,22 @@
# @llamaindex/community
## 0.0.106
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 0.0.105
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 0.0.104
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/aws",
"description": "AWS package for LlamaIndexTS",
"version": "0.0.104",
"version": "0.0.106",
"type": "module",
"types": "dist/type/index.d.ts",
"main": "dist/cjs/index.js",
+19
View File
@@ -1,5 +1,24 @@
# @llamaindex/clip
## 0.0.61
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
- @llamaindex/openai@0.4.5
## 0.0.60
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
- @llamaindex/openai@0.4.4
## 0.0.59
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/clip",
"description": "Clip Embedding Adapter for LlamaIndex",
"version": "0.0.59",
"version": "0.0.61",
"type": "module",
"types": "dist/index.d.ts",
"main": "dist/index.cjs",
+17
View File
@@ -1,5 +1,22 @@
# @llamaindex/cohere
## 0.0.25
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 0.0.24
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 0.0.23
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/cohere",
"description": "Cohere Adapter for LlamaIndex",
"version": "0.0.23",
"version": "0.0.25",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
+19
View File
@@ -1,5 +1,24 @@
# @llamaindex/deepinfra
## 0.0.61
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
- @llamaindex/openai@0.4.5
## 0.0.60
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
- @llamaindex/openai@0.4.4
## 0.0.59
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/deepinfra",
"description": "Deepinfra Adapter for LlamaIndex",
"version": "0.0.59",
"version": "0.0.61",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
+13
View File
@@ -1,5 +1,18 @@
# @llamaindex/deepseek
## 0.0.21
### Patch Changes
- Updated dependencies [d8ac8d3]
- @llamaindex/openai@0.4.5
## 0.0.20
### Patch Changes
- @llamaindex/openai@0.4.4
## 0.0.19
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/deepseek",
"description": "DeepSeek Adapter for LlamaIndex",
"version": "0.0.19",
"version": "0.0.21",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
+17
View File
@@ -1,5 +1,22 @@
# @llamaindex/discord
## 0.1.10
### Patch Changes
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 0.1.9
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 0.1.8
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/discord",
"description": "Discord Reader for LlamaIndex",
"version": "0.1.8",
"version": "0.1.10",
"type": "module",
"types": "dist/index.d.ts",
"main": "dist/index.cjs",
+13
View File
@@ -1,5 +1,18 @@
# @llamaindex/fireworks
## 0.0.21
### Patch Changes
- Updated dependencies [d8ac8d3]
- @llamaindex/openai@0.4.5
## 0.0.20
### Patch Changes
- @llamaindex/openai@0.4.4
## 0.0.19
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/fireworks",
"description": "Fireworks Adapter for LlamaIndex",
"version": "0.0.19",
"version": "0.0.21",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
+24
View File
@@ -1,5 +1,29 @@
# @llamaindex/google
## 0.3.10
### Patch Changes
- d8ac8d3: Feat: add support for openai realtime API
- Updated dependencies [a89e187]
- Updated dependencies [62699b7]
- Updated dependencies [c5b2691]
- Updated dependencies [d8ac8d3]
- @llamaindex/core@0.6.11
## 0.3.9
### Patch Changes
- Updated dependencies [1b5af14]
- @llamaindex/core@0.6.10
## 0.3.8
### Patch Changes
- 56763dc: Update to the latest Gemini 2.5 Pro Preview key
## 0.3.7
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/google",
"description": "Google Adapter for LlamaIndex",
"version": "0.3.7",
"version": "0.3.10",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
+18 -117
View File
@@ -12,14 +12,11 @@ import {
LiveLLM,
LiveLLMSession,
type BaseTool,
type ChatMessage,
type LiveConnectConfig,
type MessageContentAudioDetail,
type MessageContentDetail,
type MessageContentImageDataDetail,
type MessageContentVideoDetail,
type MessageSender,
} from "@llamaindex/core/llms";
import { getEnv, uint8ArrayToBase64 } from "@llamaindex/env";
import { getEnv } from "@llamaindex/env";
import { GeminiMessageSender } from "./message-sender";
import { GEMINI_MODEL, type GeminiVoiceName } from "./types";
import {
mapBaseToolToGeminiLiveFunctionDeclaration,
@@ -40,6 +37,10 @@ export class GeminiLiveSession extends LiveLLMSession {
super();
}
get messageSender(): MessageSender {
return new GeminiMessageSender(this);
}
private isInterruptedEvent(event: LiveServerMessage): boolean {
return event.serverContent?.interrupted === true;
}
@@ -70,22 +71,6 @@ export class GeminiLiveSession extends LiveLLMSession {
return event.setupComplete !== undefined;
}
private isTextMessage(content: MessageContentDetail) {
return content.type === "text";
}
private isAudioMessage(content: MessageContentDetail) {
return content.type === "audio";
}
private isImageMessage(content: MessageContentDetail) {
return content.type === "image";
}
private isVideoMessage(content: MessageContentDetail) {
return content.type === "video";
}
//for the tool call event, we need to return the response with function responses
private async handleToolCallEvent(
event: LiveServerMessage,
@@ -195,105 +180,11 @@ export class GeminiLiveSession extends LiveLLMSession {
}
}
private sendTextMessage(message: string, role?: string) {
this.session?.sendClientContent({
turns: [
{
parts: [{ text: message }],
...(role ? { role } : {}),
},
],
});
}
private sendAudioMessage(content: MessageContentAudioDetail, role?: string) {
if (typeof content.data === "string") {
this.session?.sendRealtimeInput({
audio: {
data: content.data,
mimeType: content.mimeType,
},
});
} else {
this.session?.sendRealtimeInput({
audio: {
data: uint8ArrayToBase64(content.data),
mimeType: content.mimeType,
},
});
}
}
private sendImageMessage(
content: MessageContentImageDataDetail,
role?: string,
) {
if (typeof content.data === "string") {
this.session?.sendRealtimeInput({
media: {
data: content.data,
mimeType: content.mimeType,
},
});
} else {
this.session?.sendRealtimeInput({
media: {
data: uint8ArrayToBase64(content.data),
mimeType: content.mimeType,
},
});
}
}
private sendVideoMessage(content: MessageContentVideoDetail, role?: string) {
if (typeof content.data === "string") {
this.session?.sendRealtimeInput({
video: {
data: content.data,
mimeType: content.mimeType,
},
});
} else {
this.session?.sendRealtimeInput({
video: {
data: uint8ArrayToBase64(content.data),
mimeType: content.mimeType,
},
});
}
}
private handleUserInput(message: ChatMessage) {
const { content, role } = message;
if (!Array.isArray(content)) {
this.sendTextMessage(content, role);
} else {
for (const item of content) {
if (this.isTextMessage(item)) {
this.sendTextMessage(item.text, role);
} else if (this.isAudioMessage(item)) {
this.sendAudioMessage(item as MessageContentAudioDetail, role);
} else if (this.isImageMessage(item)) {
this.sendImageMessage(item as MessageContentImageDataDetail, role);
} else if (this.isVideoMessage(item)) {
this.sendVideoMessage(item as MessageContentVideoDetail, role);
}
}
}
}
sendMessage(message: ChatMessage) {
if (!this.session) {
throw new Error("Session not connected");
}
this.handleUserInput(message);
}
async disconnect() {
if (!this.session) {
throw new Error("Session not connected");
}
this.session.close();
}
}
@@ -322,6 +213,10 @@ export class GeminiLive extends LiveLLM {
}
}
async getEphemeralKey(): Promise<string | undefined> {
throw new Error("Ephemeral key is not supported for Gemini Live");
}
async connect(config?: LiveConnectConfig) {
const liveConfig: GoogleLiveConnectConfig = {
responseModalities: config?.responseModality
@@ -356,6 +251,12 @@ export class GeminiLive extends LiveLLM {
};
}
if (config?.audioConfig) {
throw new Error(
"Audio config is not supported for Gemini Live, directly send and recieve audio events instead",
);
}
const geminiLiveSession = new GeminiLiveSession();
geminiLiveSession.session = await this.client.live.connect({
@@ -0,0 +1,53 @@
import type {
MessageContentAudioDetail,
MessageContentImageDataDetail,
MessageContentVideoDetail,
MessageSender,
} from "@llamaindex/core/llms";
import type { GeminiLiveSession } from "./live";
export class GeminiMessageSender implements MessageSender {
private geminiSession: GeminiLiveSession;
constructor(session: GeminiLiveSession) {
this.geminiSession = session;
}
sendTextMessage(message: string, role?: string) {
this.geminiSession.session?.sendClientContent({
turns: [
{
parts: [{ text: message }],
...(role ? { role } : {}),
},
],
});
}
sendAudioMessage(content: MessageContentAudioDetail, role?: string) {
this.geminiSession.session?.sendRealtimeInput({
audio: {
data: content.data,
mimeType: content.mimeType,
},
});
}
sendImageMessage(content: MessageContentImageDataDetail, role?: string) {
this.geminiSession.session?.sendRealtimeInput({
media: {
data: content.data,
mimeType: content.mimeType,
},
});
}
sendVideoMessage(content: MessageContentVideoDetail, role?: string) {
this.geminiSession.session?.sendRealtimeInput({
video: {
data: content.data,
mimeType: content.mimeType,
},
});
}
}
+1 -1
View File
@@ -75,7 +75,7 @@ export enum GEMINI_MODEL {
GEMINI_2_0_PRO_EXPERIMENTAL = "gemini-2.0-pro-exp-02-05",
GEMINI_2_0_FLASH_LIVE = "gemini-2.0-flash-live-001",
GEMINI_2_5_PRO_PREVIEW = "gemini-2.5-pro-preview-03-25",
GEMINI_2_5_PRO_PREVIEW_LATEST = "gemini-2.5-pro-preview-05-06",
GEMINI_2_5_PRO_PREVIEW_LATEST = "gemini-2.5-pro-preview-06-05",
GEMINI_2_5_FLASH_PREVIEW = "gemini-2.5-flash-preview-05-20",
}
+13
View File
@@ -1,5 +1,18 @@
# @llamaindex/groq
## 0.0.76
### Patch Changes
- Updated dependencies [d8ac8d3]
- @llamaindex/openai@0.4.5
## 0.0.75
### Patch Changes
- @llamaindex/openai@0.4.4
## 0.0.74
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/groq",
"description": "Groq Adapter for LlamaIndex",
"version": "0.0.74",
"version": "0.0.76",
"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