Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
29f7814
feat: support vercel ai sdk 5 beta
thucpn Jul 28, 2025
6f5b3b0
update chat interface
thucpn Jul 28, 2025
6d92fa5
regenerate & sendMessage
thucpn Jul 29, 2025
9c0419b
update chat interface
thucpn Jul 29, 2025
901f04a
update comment
thucpn Jul 29, 2025
3acd4df
update chat context
thucpn Jul 29, 2025
754ec86
keep `reload`, `append` and `isLoading` for backward compatibility
thucpn Jul 29, 2025
2e91a37
update components
thucpn Jul 29, 2025
b2fea79
keep markdown work
thucpn Jul 29, 2025
77991fd
update example
thucpn Jul 31, 2025
cd3c6e2
chore: message parts
thucpn Jul 31, 2025
743fa92
fix format
thucpn Jul 31, 2025
6d69946
update example
thucpn Aug 1, 2025
459ee92
bump ai 5.0.0 in examples
thucpn Aug 1, 2025
842e88d
update route handler for chat
thucpn Aug 1, 2025
b0bac0c
update interface
thucpn Aug 1, 2025
5edcae3
fix imports
thucpn Aug 1, 2025
7363cc9
transform data parts to annotations
thucpn Aug 1, 2025
6ac941a
update chat advanced example
thucpn Aug 1, 2025
e0e7481
update edge example
thucpn Aug 1, 2025
b15eb10
update rsc example
thucpn Aug 1, 2025
886c586
bump vercel 5 in app web
thucpn Aug 1, 2025
4619a55
update all app/web demo
thucpn Aug 1, 2025
26c8d62
use @ai-sdk/llamaindex adapter
thucpn Aug 1, 2025
016d8f3
use own adapter
thucpn Aug 1, 2025
b19fd92
annotations -> parts, introduce usePart()
thucpn Aug 4, 2025
5e49686
introduce usePart and useAllParts
thucpn Aug 4, 2025
e4e272e
able to custom part position if needed
thucpn Aug 4, 2025
618916d
remove inline annotations
thucpn Aug 4, 2025
b51ebd9
group before render
thucpn Aug 5, 2025
ff0a281
enhance group parts
thucpn Aug 5, 2025
75f87f7
update chat message content
thucpn Aug 5, 2025
4c5a29b
useCurrentPart
thucpn Aug 5, 2025
32fa1dd
fix: usePartData
thucpn Aug 5, 2025
cce7a2b
remove comments
thucpn Aug 5, 2025
8233fd1
fix: source part and update examples
thucpn Aug 5, 2025
dc3739c
refactor: extract all artifact data
thucpn Aug 5, 2025
dee2320
refactor: suggested questions
thucpn Aug 5, 2025
a020e27
update comment
thucpn Aug 5, 2025
b927d6a
depreacated ChatEvents and AgentEvent
thucpn Aug 5, 2025
16c4a29
image & document_file -> file
thucpn Aug 5, 2025
ad740ce
update comments
thucpn Aug 5, 2025
7419ccc
remove reload & append
thucpn Aug 5, 2025
17792d4
suggestion
thucpn Aug 5, 2025
9450d68
simplify file
thucpn Aug 5, 2025
d8936da
enhance chat file
thucpn Aug 5, 2025
87f2cfb
create alias Part with available built-in parts
thucpn Aug 5, 2025
c9ff760
remove log
thucpn Aug 5, 2025
227c37f
update example
thucpn Aug 5, 2025
414c204
ChatMessage.Part.Artifact
thucpn Aug 5, 2025
740791f
issue when saving artiact
thucpn Aug 5, 2025
ccdf281
fix circular import
thucpn Aug 5, 2025
c96b35b
update edge example and advanced example
thucpn Aug 5, 2025
98e18a1
update example
thucpn Aug 5, 2025
bcea59c
ensure rsc working well
thucpn Aug 5, 2025
59e1c27
fix types in demo
thucpn Aug 5, 2025
f3f30b7
update registry
thucpn Aug 5, 2025
04c9046
update useChatWorkflow
thucpn Aug 5, 2025
ef37fc6
fix format
thucpn Aug 5, 2025
19708c9
generate drafts document
thucpn Aug 5, 2025
8749ac0
update example
thucpn Aug 5, 2025
2b9c57b
fix doc
thucpn Aug 5, 2025
ae96ca4
update doc to explain data-
thucpn Aug 5, 2025
8deeb99
add example for id behavior
thucpn Aug 5, 2025
5aa1f64
update demo artifact example
thucpn Aug 5, 2025
e1f6353
update useFile
thucpn Aug 5, 2025
8bb56fa
able to custom render data in chat event
thucpn Aug 6, 2025
9da31c7
support AnyPart
thucpn Aug 6, 2025
2927f19
rename attachments
thucpn Aug 6, 2025
98212a7
rename: getAttachments
thucpn Aug 6, 2025
f165668
lint
thucpn Aug 6, 2025
097c587
bump chat-ui and vercel v5 in server
thucpn Aug 6, 2025
4c5a926
update dynamic comps
thucpn Aug 6, 2025
aa242d4
update chat handler
thucpn Aug 6, 2025
9c7b729
fix imports
thucpn Aug 6, 2025
c6b49cc
file handle
thucpn Aug 6, 2025
533b2b0
ServerMessage adapter
thucpn Aug 6, 2025
6bdc1ee
support FilePart
thucpn Aug 6, 2025
772ebad
fix imports
thucpn Aug 6, 2025
b48a1fa
update file interface
thucpn Aug 6, 2025
86f03d8
update server message
thucpn Aug 6, 2025
5fc83eb
fix upload file example
thucpn Aug 6, 2025
165d5db
refactor: sending events in backend
thucpn Aug 6, 2025
69a5c08
missing optional id to merge data parts
thucpn Aug 6, 2025
b01e029
new streaming logic
thucpn Aug 7, 2025
8a3524d
update doc
thucpn Aug 8, 2025
48a5449
simplify workflow stream with sse
thucpn Aug 8, 2025
b2aba81
AgentWorkflowAdapter + LlamaIndexServerAdapter
thucpn Aug 8, 2025
10580f2
remove dead code
thucpn Aug 8, 2025
3286d47
refactor: stream pipe through
thucpn Aug 11, 2025
b7d7950
fix callback
thucpn Aug 11, 2025
e646e18
fix imports
thucpn Aug 11, 2025
67912e1
fix: remove dead imports
thucpn Aug 11, 2025
c44881b
fix stream end
thucpn Aug 11, 2025
a73c4c5
fix suggestion content
thucpn Aug 11, 2025
2deecf1
fix stream header
thucpn Aug 11, 2025
c1cea88
update route.ts for eject mode
thucpn Aug 11, 2025
3c187fd
fix export utils
thucpn Aug 11, 2025
ae80725
filter out invalid UI events
thucpn Aug 11, 2025
d3ba228
fix: dynamic events
thucpn Aug 11, 2025
5ca67a7
update llamaindex server examples
thucpn Aug 11, 2025
b246e0f
fix: format
thucpn Aug 11, 2025
e2a160d
fix: prettier
thucpn Aug 11, 2025
abf8d91
update pnpm workspace
thucpn Aug 11, 2025
0dbdf9b
fix lint
thucpn Aug 11, 2025
4c53b49
update frontend for fastapi example
thucpn Aug 11, 2025
e68764b
fix lint
thucpn Aug 11, 2025
10dc142
fix cycle import
thucpn Aug 11, 2025
78bc7ae
fix format
thucpn Aug 11, 2025
f01fa0c
fix typing issue in example
thucpn Aug 11, 2025
6e00444
update backend for fastapi example
thucpn Aug 11, 2025
ea63358
update header for eject mode
thucpn Aug 11, 2025
641dc06
fix unit test
thucpn Aug 11, 2025
e15accf
fix imports
thucpn Aug 11, 2025
0a7a283
fix: issue when update constants file
thucpn Aug 11, 2025
134ec59
update document
thucpn Aug 11, 2025
1537efc
update entire document
thucpn Aug 11, 2025
3bfa014
fix format
thucpn Aug 11, 2025
b710c70
add changeset
thucpn Aug 11, 2025
4412e1a
fix typo
thucpn Aug 11, 2025
26d104b
remove @ai-sdk/llamaindex
thucpn Aug 11, 2025
1d874d2
update lock
thucpn Aug 11, 2025
7bed093
fix document typo
thucpn Aug 12, 2025
cdd2efa
update doc for transport
thucpn Aug 12, 2025
09f1825
mergeAdjacentTextParts in useChatWorkflow
thucpn Aug 12, 2025
277603c
getTextMessageContent from all parts
thucpn Aug 12, 2025
81577a3
update type for getAttachments
thucpn Aug 12, 2025
1bf41a6
fix code review
thucpn Aug 12, 2025
9dd837b
prefer type
thucpn Aug 12, 2025
1288fe5
fix lint
thucpn Aug 12, 2025
ad22615
fix lint
thucpn Aug 12, 2025
2d125c2
fix imports
thucpn Aug 12, 2025
3081109
move SSEStreamResponse to vercel.py
thucpn Aug 12, 2025
ea57086
getText
thucpn Aug 12, 2025
ea4dccd
update document
thucpn Aug 12, 2025
eb4e81f
fix loading status
thucpn Aug 12, 2025
ae53ecf
toServerMessage
thucpn Aug 12, 2025
f81d049
update server document
thucpn Aug 12, 2025
b677d23
fix: is file part
thucpn Aug 12, 2025
4172112
fix format
thucpn Aug 12, 2025
7ac96ac
bump ai version for server eject
thucpn Aug 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/tasty-rules-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@llamaindex/chat-ui': minor
'@llamaindex/server': minor
'llamaindex-server-examples': patch
'web': patch
'@llamaindex/chat-ui-docs': patch
---

support vercel ai sdk ver 5
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Components are designed to be composable. You can use them as is:

```tsx
import { ChatSection } from '@llamaindex/chat-ui'
import { useChat } from 'ai/react'
import { useChat } from '@ai-sdk/react'

const ChatExample = () => {
const handler = useChat()
Expand All @@ -96,7 +96,7 @@ Or you can extend them with your own children components:
```tsx
import { ChatSection, ChatMessages, ChatInput } from '@llamaindex/chat-ui'
import LlamaCloudSelector from './components/LlamaCloudSelector' // your custom component
import { useChat } from 'ai/react'
import { useChat } from '@ai-sdk/react'

const ChatExample = () => {
const handler = useChat()
Expand Down Expand Up @@ -158,7 +158,7 @@ Additionally, you can also override each component's styles by setting custom cl

```tsx
import { ChatSection, ChatMessages, ChatInput } from '@llamaindex/chat-ui'
import { useChat } from 'ai/react'
import { useChat } from '@ai-sdk/react'

const ChatExample = () => {
const handler = useChat()
Expand Down
57 changes: 43 additions & 14 deletions apps/web/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Message, LlamaIndexAdapter, StreamData } from 'ai'
import { fakeStreamText, TextChunk, writeStream } from '@/app/utils'
import { UIMessage as Message } from '@ai-sdk/react'
import {
ChatMessage,
MessageContentDetail,
OpenAI,
OpenAIEmbedding,
Settings,
SimpleChatEngine,
} from 'llamaindex'
import { NextResponse, type NextRequest } from 'next/server'
import { fakeStreamText } from '@/app/utils'

export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
Expand All @@ -24,32 +24,61 @@ export async function POST(request: NextRequest) {
const messages = body.messages
const lastMessage = messages[messages.length - 1]

const vercelStreamData = new StreamData()

if (!process.env.OPENAI_API_KEY) {
// Return fake stream if API key is not set
return new Response(fakeStreamText(), {
headers: {
'Content-Type': 'text/plain',
'Content-Type': 'text/event-stream',
Connection: 'keep-alive',
},
})
}

const chatEngine = new SimpleChatEngine()

const messageContent = (lastMessage.parts[0] as { text: string }).text

const response = await chatEngine.chat({
message: lastMessage.content,
chatHistory: messages as ChatMessage[],
message: messageContent,
chatHistory: messages.map(message => ({
role: message.role,
content: message.parts as MessageContentDetail[],
})),
stream: true,
})

return LlamaIndexAdapter.toDataStreamResponse(response, {
data: vercelStreamData,
callbacks: {
onCompletion: async () => {
await vercelStreamData.close()
},
const sseStream = new ReadableStream({
async start(controller) {
// Generate a unique message id
const messageId = crypto.randomUUID()

// Start the text chunk
const startChunk: TextChunk = { id: messageId, type: 'text-start' }
writeStream(controller, startChunk)

// Consume the response and write the chunks to the controller
for await (const chunk of response) {
writeStream(controller, {
id: messageId,
type: 'text-delta',
delta: chunk.delta,
})
}

// End the text chunk
const endChunk: TextChunk = { id: messageId, type: 'text-end' }
writeStream(controller, endChunk)

controller.close()
},
})

return new Response(sseStream, {
status: 200,
statusText: 'OK',
headers: {
'content-type': 'text/event-stream',
connection: 'keep-alive',
},
})
} catch (error) {
Expand Down
26 changes: 21 additions & 5 deletions apps/web/app/demo/canvas/code-preview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,35 @@ import {
useChatCanvas,
} from '@llamaindex/chat-ui'
import { DynamicComponent } from '@llamaindex/dynamic-ui'
import { Message, useChat } from 'ai/react'
import { UIMessage as Message, useChat } from '@ai-sdk/react'

const initialMessages: Message[] = [
{
id: 'code-gen1',
role: 'user',
content: 'Generate a simple calculator',
parts: [{ type: 'text', text: 'Generate a simple calculator' }],
},
{
id: 'code-gen2',
role: 'assistant',
content:
'\n```annotation\n{"type":"artifact","data":{"type":"code","created_at":1752124365106,"data":{"language":"typescript","file_name":"calculator.tsx","code":"import React, { useState } from \\"react\\"\\nimport { Button } from \\"@/components/ui/button\\"\\nimport { Card } from \\"@/components/ui/card\\"\\nimport { cn } from \\"@/lib/utils\\"\\n\\nconst buttons = [\\n [\\"7\\", \\"8\\", \\"9\\", \\"/\\"],\\n [\\"4\\", \\"5\\", \\"6\\", \\"*\\"],\\n [\\"1\\", \\"2\\", \\"3\\", \\"-\\"],\\n [\\"0\\", \\"C\\", \\"=\\", \\"+\\"],\\n]\\n\\nexport default function Calculator() {\\n const [input, setInput] = useState<string>(\\"\\")\\n const [result, setResult] = useState<string | null>(null)\\n\\n const handleButtonClick = (value: string) => {\\n if (value === \\"C\\") {\\n setInput(\\"\\")\\n setResult(null)\\n return\\n }\\n if (value === \\"=\\") {\\n try {\\n // eslint-disable-next-line no-eval\\n const evalResult = eval(input)\\n setResult(evalResult.toString())\\n } catch {\\n setResult(\\"Error\\")\\n }\\n return\\n }\\n if (result !== null) {\\n setInput(value.match(/[0-9.]/) ? value : result + value)\\n setResult(null)\\n } else {\\n setInput((prev) => prev + value)\\n }\\n }\\n\\n return (\\n <div className=\\"flex items-center justify-center min-h-screen bg-muted\\">\\n <Card className=\\"w-[320px] p-6 shadow-lg\\">\\n <div className={cn(\\"mb-4 h-16 bg-background rounded flex items-end justify-end px-4 text-2xl font-mono border\\", result && \\"text-muted-foreground\\")}>\\n {result !== null ? result : input || \\"0\\"}\\n </div>\\n <div className=\\"grid grid-cols-4 gap-3\\">\\n {buttons.flat().map((btn, idx) => (\\n <Button\\n key={idx}\\n variant={btn === \\"C\\" ? \\"destructive\\" : btn === \\"=\\" ? \\"default\\" : \\"outline\\"}\\n className={cn(\\n \\"h-12 text-xl\\",\\n btn === \\"=\\" && \\"col-span-1 bg-primary text-primary-foreground\\",\\n btn === \\"C\\" && \\"col-span-1\\"\\n )}\\n onClick={() => handleButtonClick(btn)}\\n >\\n {btn}\\n </Button>\\n ))}\\n </div>\\n </Card>\\n </div>\\n )\\n}"}}}\n```\nHere\'s how the simple calculator works:\n\n- The calculator displays the current input or the result at the top.\n- You can click the number buttons (0-9) and the operators (+, -, *, /) to build your calculation.\n- Pressing the = button evaluates the expression and shows the result.\n- Pressing the C button clears the input and resets the calculator.\n- If you get an error (like dividing by zero or entering an invalid expression), "Error" will be displayed.\n\nYou can further customize the calculator\'s appearance or add more features as needed! If you have any questions about how the code works or want to add more functionality, let me know!',
parts: [
{
type: 'text',
text: "Here's the simple calculator:",
},
{
type: 'data-artifact',
data: {
type: 'code',
created_at: 1752124365106,
data: {
language: 'typescript',
file_name: 'calculator.tsx',
code: 'import React, { useState } from "react"\nimport { Button } from "@/components/ui/button"\nimport { Card } from "@/components/ui/card"\nimport { cn } from "@/lib/utils"\n\nconst buttons = [\n ["7", "8", "9", "/"],\n ["4", "5", "6", "*"],\n ["1", "2", "3", "-"],\n ["0", "C", "=", "+"],\n]\n\nexport default function Calculator() {\n const [input, setInput] = useState<string>("")\n const [result, setResult] = useState<string | null>(null)\n\n const handleButtonClick = (value: string) => {\n if (value === "C") {\n setInput("")\n setResult(null)\n return\n }\n if (value === "=") {\n try {\n // eslint-disable-next-line no-eval\n const evalResult = eval(input)\n setResult(evalResult.toString())\n } catch {\n setResult("Error")\n }\n return\n }\n if (result !== null) {\n setInput(value.match(/[0-9.]/) ? value : result + value)\n setResult(null)\n } else {\n setInput((prev) => prev + value)\n }\n }\n\n return (\n <div className="flex items-center justify-center min-h-screen bg-muted">\n <Card className="w-[320px] p-6 shadow-lg">\n <div className={cn("mb-4 h-16 bg-background rounded flex items-end justify-end px-4 text-2xl font-mono border", result && "text-muted-foreground")}>\n {result !== null ? result : input || "0"}\n </div>\n <div className="grid grid-cols-4 gap-3">\n {buttons.flat().map((btn, idx) => (\n <Button\n key={idx}\n variant={btn === "C" ? "destructive" : btn === "=" ? "default" : "outline"}\n className={cn(\n "h-12 text-xl",\n btn === "=" && "col-span-1 bg-primary text-primary-foreground",\n btn === "C" && "col-span-1"\n )}\n onClick={() => handleButtonClick(btn)}\n >\n {btn}\n </Button>\n ))}\n </div>\n </Card>\n </div>\n )\n}',
},
},
},
],
},
]

Expand All @@ -30,7 +46,7 @@ export default function Page(): JSX.Element {
}

function CustomChat() {
const handler = useChat({ initialMessages })
const handler = useChat({ messages: initialMessages })

return (
<ChatSection
Expand Down
78 changes: 28 additions & 50 deletions apps/web/app/demo/canvas/custom/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
useChatCanvas,
useChatUI,
} from '@llamaindex/chat-ui'
import { Message, useChat } from 'ai/react'
import { UIMessage as Message, useChat } from '@ai-sdk/react'
import { Image } from 'lucide-react'

const code = `
Expand All @@ -25,11 +25,11 @@ import {
useChatCanvas,
useChatUI,
} from '@llamaindex/chat-ui'
import { useChat } from 'ai/react'
import { useChat } from '@ai-sdk/react'
import { Image } from 'lucide-react'

export function CustomChat() {
const handler = useChat({ initialMessages: [] })
const handler = useChat()

return (
<ChatSection
Expand Down Expand Up @@ -103,11 +103,8 @@ function CustomChatMessages() {
>
<ChatMessage.Avatar />
<ChatMessage.Content>
<ChatMessage.Content.Markdown
annotationRenderers={{
artifact: CustomArtifactCard,
}}
/>
<ChatMessage.Part.Markdown />
<ChatMessage.Part.Artifact />
</ChatMessage.Content>
<ChatMessage.Actions />
</ChatMessage>
Expand All @@ -116,32 +113,24 @@ function CustomChatMessages() {
</ChatMessages>
)
}

// custom artifact card for image artifacts
function CustomArtifactCard({ data }: { data: Artifact }) {
return (
<ChatCanvas.Artifact
data={data}
getTitle={artifact => (artifact as ImageArtifact).data.caption}
iconMap={{ image: Image }}
/>
)
}
`

const initialMessages: Message[] = [
{
id: '1',
role: 'user',
content: 'Generate an image of a cat',
parts: [{ type: 'text', text: 'Generate an image of a cat' }],
},
{
id: '2',
role: 'assistant',
content:
'Here is a cat image named Millie.' +
`\n\`\`\`annotation\n${JSON.stringify({
type: 'artifact',
parts: [
{
type: 'text',
text: 'Here is a cat image named Millie.',
},
{
type: 'data-artifact',
data: {
type: 'image',
data: {
Expand All @@ -150,21 +139,24 @@ const initialMessages: Message[] = [
},
created_at: 1745480281756,
},
})}
\n\`\`\`\n`,
},
],
},
{
id: '3',
role: 'user',
content: 'Please generate a black cat image',
parts: [{ type: 'text', text: 'Please generate a black cat image' }],
},
{
id: '4',
role: 'assistant',
content:
'Here is a black cat image named Poppy.' +
`\n\`\`\`annotation\n${JSON.stringify({
type: 'artifact',
parts: [
{
type: 'text',
text: 'Here is a black cat image named Poppy.',
},
{
type: 'data-artifact',
data: {
type: 'image',
data: {
Expand All @@ -173,8 +165,8 @@ const initialMessages: Message[] = [
},
created_at: 1745480281999,
},
})}
\n\`\`\`\n`,
},
],
},
]

Expand All @@ -184,7 +176,7 @@ export default function Page(): JSX.Element {

function CustomChat() {
const { copyToClipboard, isCopied } = useCopyToClipboard({ timeout: 2000 })
const handler = useChat({ initialMessages })
const handler = useChat({ messages: initialMessages })

return (
<ChatSection
Expand Down Expand Up @@ -279,11 +271,8 @@ function CustomChatMessages() {
>
<ChatMessage.Avatar />
<ChatMessage.Content>
<ChatMessage.Content.Markdown
annotationRenderers={{
artifact: CustomArtifactCard,
}}
/>
<ChatMessage.Part.Markdown />
<ChatMessage.Part.Artifact />
</ChatMessage.Content>
<ChatMessage.Actions />
</ChatMessage>
Expand All @@ -292,14 +281,3 @@ function CustomChatMessages() {
</ChatMessages>
)
}

// custom artifact card for image artifacts
function CustomArtifactCard({ data }: { data: Artifact }) {
return (
<ChatCanvas.Artifact
data={data}
getTitle={artifact => (artifact as ImageArtifact).data.caption}
iconMap={{ image: Image }}
/>
)
}
Loading