diff --git a/.changeset/nine-pillows-hug.md b/.changeset/nine-pillows-hug.md new file mode 100644 index 000000000000..5ff42392b2e0 --- /dev/null +++ b/.changeset/nine-pillows-hug.md @@ -0,0 +1,6 @@ +--- +'@ai-sdk/ui-utils': patch +'@ai-sdk/react': patch +--- + +feat (ui/react): support resuming an ongoing stream diff --git a/content/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx b/content/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx index e722005a3536..e690aa6e6495 100644 --- a/content/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx +++ b/content/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx @@ -325,3 +325,165 @@ When the client reloads the page after a disconnect, the chat will be restored f the case where the client reloads the page after a disconnection, but the streaming is not yet complete. + +## Resuming ongoing streams + +This feature is experimental and may change in future versions. + +The `useChat` hook has experimental support for resuming an ongoing chat generation stream by any client, either after a network disconnect or by reloading the chat page. This can be useful for building applications that involve long-running conversations or for ensuring that messages are not lost in case of network failures. + +The following are the pre-requisities for your chat application to support resumable streams: + +- Installing the [`resumable-stream`](https://www.npmjs.com/package/resumable-stream) package that helps create and manage the publisher/subscriber mechanism of the streams. +- Creating a [Redis](https://vercel.com/marketplace/redis) instance to store the stream state. +- Creating a table that tracks the stream IDs associated with a chat. + +To resume a chat stream, you will use the `experimental_resume` function returned by the `useChat` hook. You will call this function during the initial mount of the hook inside the main chat component. + +```tsx filename="app/components/chat.tsx" +'use client' + +import { useChat } from "@ai-sdk/react"; +import { Input } from "@/components/input"; +import { Messages } from "@/components/messages"; + +export function Chat() { + const { experimental_resume } = useChat({id}); + + useEffect(() => { + experimental_resume(); + + // we use an empty dependency array to + // ensure this effect runs only once + }, []) + + return ( +
+ + +
+ ) +} +``` + +The `experimental_resume` function makes a `GET` request to your configured chat endpoint (or `/api/chat` by default) whenever your client calls it. If there’s an active stream, it will pick up where it left off, otherwise it simply finishes without error. + +The `GET` request automatically appends the `chatId` query parameter to the URL to help identify the chat the request belongs to. Using the `chatId`, you can look up the most recent stream ID from the database and resume the stream. + +```bash +GET /api/chat?chatId= +``` + +Earlier, you must've implemented the `POST` handler for the `/api/chat` route to create new chat generations. When using `experimental_resume`, you must also implement the `GET` handler for `/api/chat` route to resume streams. + +### 1. Implement the GET handler + +Add a `GET` method to `/api/chat` that: + +1. Reads `chatId` from the query string +2. Validates it’s present +3. Loads any stored stream IDs for that chat +4. Returns the latest one to `streamContext.resumableStream()` +5. Falls back to an empty stream if it’s already closed + +```ts filename="app/api/chat/route.ts" +import { loadStreams } from '@/util/chat-store'; +import { createDataStream } from 'ai'; +import { after } from 'next/server'; +import { createResumableStreamContext } from 'resumable-stream'; + +const streamContext = createResumableStreamContext({ + waitUntil: after, +}); + +export async function GET() { + const { searchParams } = new URL(request.url); + const chatId = searchParams.get('chatId'); + + if (!chatId) { + return new Response('id is required', { status: 400 }); + } + + const streamIds = await loadStreams(chatId); + + if (!streamIds.length) { + return new Response('No streams found', { status: 404 }); + } + + const recentStreamId = streamIds.at(-1); + + if (!recentStreamId) { + return new Response('No recent stream found', { status: 404 }); + } + + const emptyDataStream = createDataStream({ + execute: () => {}, + }); + + return new Response( + await streamContext.resumableStream(recentStreamId, () => emptyDataStream), + ); +} +``` + +After you've implemented the `GET` handler, you can update the `POST` handler to handle the creation of resumable streams. + +### 2. Update the POST handler + +When you create a brand-new chat completion, you must: + +1. Generate a fresh `streamId` +2. Persist it alongside your `chatId` +3. Kick off a `createDataStream` that pipes tokens as they arrive +4. Hand that new stream to `streamContext.resumableStream()` + +```ts filename="app/api/chat/route.ts" +import { + appendResponseMessages, + createDataStream, + generateId, + streamText, +} from 'ai'; +import { appendStreamId, saveChat } from '@/util/chat-store'; +import { createResumableStreamContext } from 'resumable-stream'; + +const streamContext = createResumableStreamContext({ + waitUntil: after, +}); + +async function POST(request: Request) { + const { id, messages } = await req.json(); + const streamId = generateId(); + + // Record this new stream so we can resume later + await appendStreamId({ chatId: id, streamId }); + + // Build the data stream that will emit tokens + const stream = createDataStream({ + execute: dataStream => { + const result = streamText({ + model: openai('gpt-4o'), + messages, + onFinish: async ({ response }) => { + await saveChat({ + id, + messages: appendResponseMessages({ + messages, + responseMessages: response.messages, + }), + }); + }, + }); + + // Return a resumable stream to the client + result.mergeIntoDataStream(dataStream); + }, + }); + + return new Response( + await streamContext.resumableStream(streamId, () => stream), + ); +} +``` + +With both handlers, your clients can now gracefully resume ongoing streams. diff --git a/content/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx b/content/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx index 3a54c09dd098..e39468051647 100644 --- a/content/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +++ b/content/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx @@ -600,6 +600,11 @@ Allows you to easily create a conversational user interface for your chatbot app type: '() => void', description: 'Function to abort the current API request.', }, + { + name: 'experimental_resume', + type: '() => void', + description: 'Function to resume an ongoing chat generation stream.', + } { name: 'setMessages', type: '(messages: Message[] | ((messages: Message[]) => Message[]) => void', diff --git a/examples/next-openai/.env.local.example b/examples/next-openai/.env.local.example index 7de83b351115..bd742868814f 100644 --- a/examples/next-openai/.env.local.example +++ b/examples/next-openai/.env.local.example @@ -13,3 +13,5 @@ BLOB_READ_WRITE_TOKEN=xxxxxxx # Required for reasoning example DEEPSEEK_API_KEY=xxxxxxx +# Required for resumable streams. You can create a Redis store here: https://vercel.com/marketplace/redis +REDIS_URL=xxxxxx diff --git a/examples/next-openai/.gitignore b/examples/next-openai/.gitignore index 704a855f25d4..d186e8809dfc 100644 --- a/examples/next-openai/.gitignore +++ b/examples/next-openai/.gitignore @@ -34,5 +34,6 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -# chat persistence +# persistence .chats +.streams diff --git a/examples/next-openai/app/api/use-chat-resume/route.ts b/examples/next-openai/app/api/use-chat-resume/route.ts new file mode 100644 index 000000000000..a125503fff4a --- /dev/null +++ b/examples/next-openai/app/api/use-chat-resume/route.ts @@ -0,0 +1,99 @@ +import { + appendMessageToChat, + appendStreamId, + loadStreams, + saveChat, +} from '@/util/chat-store'; +import { openai } from '@ai-sdk/openai'; +import { + appendResponseMessages, + createDataStream, + generateId, + Message, + streamText, +} from 'ai'; +import { after } from 'next/server'; +import { createResumableStreamContext } from 'resumable-stream'; + +// Allow streaming responses up to 30 seconds +export const maxDuration = 30; + +export async function POST(req: Request) { + const streamContext = createResumableStreamContext({ + waitUntil: after, + }); + + const { id, messages }: { id: string; messages: Message[] } = + await req.json(); + + const streamId = generateId(); + + const recentUserMessage = messages + .filter(message => message.role === 'user') + .at(-1); + + if (!recentUserMessage) { + throw new Error('No recent user message found'); + } + + await appendMessageToChat({ chatId: id, message: recentUserMessage }); + + await appendStreamId({ chatId: id, streamId }); + + const stream = createDataStream({ + execute: dataStream => { + const result = streamText({ + model: openai('gpt-4o'), + messages, + onFinish: async ({ response }) => { + await saveChat({ + id, + messages: appendResponseMessages({ + messages, + responseMessages: response.messages, + }), + }); + }, + }); + + result.mergeIntoDataStream(dataStream); + }, + }); + + return new Response( + await streamContext.resumableStream(streamId, () => stream), + ); +} + +export async function GET(request: Request) { + const streamContext = createResumableStreamContext({ + waitUntil: after, + }); + + const { searchParams } = new URL(request.url); + const chatId = searchParams.get('chatId'); + + if (!chatId) { + return new Response('id is required', { status: 400 }); + } + + const streamIds = await loadStreams(chatId); + + if (!streamIds.length) { + return new Response('No streams found', { status: 404 }); + } + + const recentStreamId = streamIds.at(-1); + + if (!recentStreamId) { + return new Response('No recent stream found', { status: 404 }); + } + + const emptyDataStream = createDataStream({ + execute: () => {}, + }); + + return new Response( + await streamContext.resumableStream(recentStreamId, () => emptyDataStream), + ); +} diff --git a/examples/next-openai/app/use-chat-resume/[id]/page.tsx b/examples/next-openai/app/use-chat-resume/[id]/page.tsx new file mode 100644 index 000000000000..85ac133795d7 --- /dev/null +++ b/examples/next-openai/app/use-chat-resume/[id]/page.tsx @@ -0,0 +1,14 @@ +import { loadChat } from '@/util/chat-store'; +import { Chat } from '../chat'; + +export default async function Page({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + + const messages = await loadChat(id); + + return ; +} diff --git a/examples/next-openai/app/use-chat-resume/chat.tsx b/examples/next-openai/app/use-chat-resume/chat.tsx new file mode 100644 index 000000000000..72be3a8f8ba0 --- /dev/null +++ b/examples/next-openai/app/use-chat-resume/chat.tsx @@ -0,0 +1,111 @@ +'use client'; + +import { useChat } from '@ai-sdk/react'; +import { Message } from 'ai'; +import Link from 'next/link'; +import { useEffect } from 'react'; + +export function Chat({ + chatId, + autoResume, + initialMessages = [], +}: { + chatId: string; + autoResume: boolean; + initialMessages: Message[]; +}) { + const { + error, + input, + status, + handleInputChange, + handleSubmit, + messages, + reload, + stop, + experimental_resume, + } = useChat({ + id: chatId, + api: '/api/use-chat-resume', + initialMessages, + sendExtraMessageFields: true, + onError: error => { + console.error('Error streaming text:', error); + }, + }); + + useEffect(() => { + if (autoResume) { + experimental_resume(); + } + // We want to disable the exhaustive deps rule here because we only want to run this effect once + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ + Chat Id: {chatId} + + +
Status: {status}
+ + {messages.map(message => ( +
+
+ {message.role === 'user' ? 'User: ' : 'AI: '} +
+ +
+
{message.id}
+ {message.parts + .filter(part => part.type !== 'source') + .map((part, partIndex) => { + if (part.type === 'text') { + return ( +
{part.text}
+ ); + } + })} +
+
+ ))} + + {(status === 'submitted' || status === 'streaming') && ( +
+ {status === 'submitted' &&
Loading...
} + +
+ )} + + {error && ( +
+
An error occurred.
+ +
+ )} + +
+ +
+
+ ); +} diff --git a/examples/next-openai/app/use-chat-resume/page.tsx b/examples/next-openai/app/use-chat-resume/page.tsx new file mode 100644 index 000000000000..dc60255f6769 --- /dev/null +++ b/examples/next-openai/app/use-chat-resume/page.tsx @@ -0,0 +1,8 @@ +import { Chat } from './chat'; +import { generateId } from 'ai'; + +export default function Page() { + const chatId = generateId(32); + + return ; +} diff --git a/examples/next-openai/package.json b/examples/next-openai/package.json index 1ead5642bdc9..78177f103b77 100644 --- a/examples/next-openai/package.json +++ b/examples/next-openai/package.json @@ -26,6 +26,8 @@ "react": "^18", "react-dom": "^18", "react-markdown": "9.0.1", + "redis": "^4.7.0", + "resumable-stream": "^2.0.0", "zod": "3.23.8" }, "devDependencies": { diff --git a/examples/next-openai/util/chat-store.ts b/examples/next-openai/util/chat-store.ts index 6f58a23edcba..319ab3e299c4 100644 --- a/examples/next-openai/util/chat-store.ts +++ b/examples/next-openai/util/chat-store.ts @@ -1,5 +1,5 @@ import { generateId, Message } from 'ai'; -import { existsSync, mkdirSync } from 'fs'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { readFile, writeFile } from 'fs/promises'; import path from 'path'; @@ -23,12 +23,58 @@ export async function saveChat({ await writeFile(getChatFile(id), JSON.stringify(messages, null, 2)); } +export async function appendMessageToChat({ + chatId, + message, +}: { + chatId: string; + message: Message; +}): Promise { + const file = getChatFile(chatId); + const messages = await loadChat(chatId); + messages.push(message); + await writeFile(file, JSON.stringify(messages, null, 2)); +} + export async function loadChat(id: string): Promise { return JSON.parse(await readFile(getChatFile(id), 'utf8')); } function getChatFile(id: string): string { const chatDir = path.join(process.cwd(), '.chats'); + + if (!existsSync(chatDir)) mkdirSync(chatDir, { recursive: true }); + + const chatFile = path.join(chatDir, `${id}.json`); + + if (!existsSync(chatFile)) { + writeFileSync(chatFile, '[]'); + } + + return chatFile; +} + +export async function appendStreamId({ + chatId, + streamId, +}: { + chatId: string; + streamId: string; +}) { + const file = getStreamsFile(chatId); + const streams = await loadStreams(chatId); + streams.push(streamId); + await writeFile(file, JSON.stringify(streams, null, 2)); +} + +export async function loadStreams(chatId: string): Promise { + const file = getStreamsFile(chatId); + if (!existsSync(file)) return []; + return JSON.parse(await readFile(file, 'utf8')); +} + +function getStreamsFile(chatId: string): string { + const chatDir = path.join(process.cwd(), '.streams'); if (!existsSync(chatDir)) mkdirSync(chatDir, { recursive: true }); - return path.join(chatDir, `${id}.json`); + return path.join(chatDir, `${chatId}.json`); } diff --git a/packages/react/src/use-chat.ts b/packages/react/src/use-chat.ts index 79ee0a50912f..3c064f7677fa 100644 --- a/packages/react/src/use-chat.ts +++ b/packages/react/src/use-chat.ts @@ -52,6 +52,12 @@ export type UseChatHelpers = { * Abort the current request immediately, keep the generated tokens if any. */ stop: () => void; + + /** + * Resume an ongoing chat generation stream. This does not resume an aborted generation. + */ + experimental_resume: () => void; + /** * Update the `messages` state locally. This is useful when you want to * edit the messages on the client, and then trigger the `reload` method @@ -236,7 +242,10 @@ By default, it's set to 1, which means that only a single LLM call is made. }, [credentials, headers, body]); const triggerRequest = useCallback( - async (chatRequest: ChatRequest) => { + async ( + chatRequest: ChatRequest, + requestType: 'generate' | 'resume' = 'generate', + ) => { mutateStatus('submitted'); setError(undefined); @@ -339,6 +348,7 @@ By default, it's set to 1, which means that only a single LLM call is made. generateId, fetch, lastMessage: chatMessages[chatMessages.length - 1], + requestType, }); abortControllerRef.current = null; @@ -456,6 +466,12 @@ By default, it's set to 1, which means that only a single LLM call is made. } }, []); + const experimental_resume = useCallback(async () => { + const messages = messagesRef.current; + + triggerRequest({ messages }, 'resume'); + }, [triggerRequest]); + const setMessages = useCallback( (messages: Message[] | ((messages: Message[]) => Message[])) => { if (typeof messages === 'function') { @@ -581,6 +597,7 @@ By default, it's set to 1, which means that only a single LLM call is made. append, reload, stop, + experimental_resume, input, setInput, handleInputChange, diff --git a/packages/react/src/use-chat.ui.test.tsx b/packages/react/src/use-chat.ui.test.tsx index ed143addba7a..9eb836962141 100644 --- a/packages/react/src/use-chat.ui.test.tsx +++ b/packages/react/src/use-chat.ui.test.tsx @@ -1946,3 +1946,83 @@ describe('initialMessages', () => { }); }); }); + +describe('resume ongoing stream and return assistant message', () => { + const controller = new TestResponseController(); + + setupTestComponent( + () => { + const { messages, status, experimental_resume } = useChat({ + id: '123', + initialMessages: [{ id: 'msg_123', role: 'user', content: 'hi' }], + }); + + useEffect(() => { + experimental_resume(); + + // We want to disable the exhaustive deps rule here because we only want to run this effect once + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ {messages.map((m, idx) => ( +
+ {m.role === 'user' ? 'User: ' : 'AI: '} + {m.content} +
+ ))} + +
{status}
+
+ ); + }, + { + init: TestComponent => { + server.urls['/api/chat'].response = { + type: 'controlled-stream', + controller, + }; + + return ; + }, + }, + ); + + it('construct messages from resumed stream', async () => { + await screen.findByTestId('message-0'); + expect(screen.getByTestId('message-0')).toHaveTextContent('User: hi'); + + await waitFor(() => { + expect(screen.getByTestId('status')).toHaveTextContent('submitted'); + }); + + controller.write('0:"Hello"\n'); + + await waitFor(() => { + expect(screen.getByTestId('status')).toHaveTextContent('streaming'); + }); + + controller.write('0:"," \n'); + controller.write('0:" world"\n'); + controller.write('0:"."\n'); + + controller.close(); + + await screen.findByTestId('message-1'); + expect(screen.getByTestId('message-1')).toHaveTextContent( + 'AI: Hello, world.', + ); + + await waitFor(() => { + expect(screen.getByTestId('status')).toHaveTextContent('ready'); + + expect(server.calls.length).toBeGreaterThan(0); + const mostRecentCall = server.calls[0]; + + const { requestMethod, requestUrl } = mostRecentCall; + expect(requestMethod).toBe('GET'); + expect(requestUrl).toBe('http://localhost:3000/api/chat?chatId=123'); + }); + }); +}); diff --git a/packages/ui-utils/src/call-chat-api.ts b/packages/ui-utils/src/call-chat-api.ts index 80fc1636f8eb..54160b515e48 100644 --- a/packages/ui-utils/src/call-chat-api.ts +++ b/packages/ui-utils/src/call-chat-api.ts @@ -20,6 +20,7 @@ export async function callChatApi({ generateId, fetch = getOriginalFetch(), lastMessage, + requestType = 'generate', }: { api: string; body: Record; @@ -39,17 +40,31 @@ export async function callChatApi({ generateId: IdGenerator; fetch: ReturnType | undefined; lastMessage: UIMessage | undefined; + requestType?: 'generate' | 'resume'; }) { - const response = await fetch(api, { - method: 'POST', - body: JSON.stringify(body), - headers: { - 'Content-Type': 'application/json', - ...headers, - }, - signal: abortController?.()?.signal, - credentials, - }).catch(err => { + const request = + requestType === 'resume' + ? fetch(`${api}?chatId=${body.id}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + ...headers, + }, + signal: abortController?.()?.signal, + credentials, + }) + : fetch(api, { + method: 'POST', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + ...headers, + }, + signal: abortController?.()?.signal, + credentials, + }); + + const response = await request.catch(err => { restoreMessagesOnFailure(); throw err; }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ae38a4345a9..9fa89acc91fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -343,7 +343,7 @@ importers: version: 10.2.3(chokidar@3.6.0)(typescript@5.5.4) '@nestjs/testing': specifier: ^10.4.12 - version: 10.4.12(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.2)(@nestjs/platform-express@10.4.9) + version: 10.4.12(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.9)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.9(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.2)) '@types/express': specifier: ^5.0.0 version: 5.0.0 @@ -513,7 +513,7 @@ importers: version: link:../../packages/ai langchain: specifier: 0.1.36 - version: 0.1.36(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0(@aws-sdk/client-sso-oidc@3.662.0(@aws-sdk/client-sts@3.662.0))(@aws-sdk/client-sts@3.662.0))(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(fast-xml-parser@4.4.1)(ignore@5.3.2)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(playwright@1.50.1)(ws@8.18.0) + version: 0.1.36(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0)(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(fast-xml-parser@4.4.1)(ignore@5.3.2)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(playwright@1.50.1)(redis@4.7.0)(ws@8.18.0) next: specifier: latest version: 15.2.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -605,6 +605,12 @@ importers: react-markdown: specifier: 9.0.1 version: 9.0.1(@types/react@18.3.3)(react@18.3.1) + redis: + specifier: ^4.7.0 + version: 4.7.0 + resumable-stream: + specifier: ^2.0.0 + version: 2.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -647,7 +653,7 @@ importers: version: link:../../packages/react '@vercel/functions': specifier: latest - version: 2.0.0(@aws-sdk/credential-provider-web-identity@3.662.0(@aws-sdk/client-sts@3.662.0)) + version: 2.0.0(@aws-sdk/credential-provider-web-identity@3.662.0) ai: specifier: 4.3.12 version: link:../../packages/ai @@ -1014,7 +1020,7 @@ importers: version: 3.5.12 nuxt: specifier: 3.14.159 - version: 3.14.159(@parcel/watcher@2.4.1)(@types/node@20.17.24)(@upstash/redis@1.34.3)(eslint@9.21.0(jiti@2.4.0))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3))(webpack-sources@3.2.3) + version: 3.14.159(@parcel/watcher@2.4.1)(@types/node@20.17.24)(@upstash/redis@1.34.3)(eslint@9.21.0)(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3))(webpack-sources@3.2.3) tailwindcss: specifier: 3.4.15 version: 3.4.15(ts-node@10.9.2(@types/node@20.17.24)(typescript@5.6.3)) @@ -2093,7 +2099,7 @@ importers: version: link:../../tools/tsconfig '@vitejs/plugin-vue': specifier: 5.2.0 - version: 5.2.0(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3))(vue@3.5.13(typescript@5.6.3)) + version: 5.2.0(vite@5.4.11(@types/node@22.7.4)(terser@5.31.3))(vue@3.5.13(typescript@5.6.3)) eslint: specifier: 8.57.1 version: 8.57.1 @@ -2114,7 +2120,7 @@ importers: version: 5.6.3 vite-plugin-solid: specifier: 2.7.2 - version: 2.7.2(solid-js@1.8.7) + version: 2.7.2(solid-js@1.8.7)(vite@5.4.11(@types/node@22.7.4)(terser@5.31.3)) vitest: specifier: 2.1.4 version: 2.1.4(@edge-runtime/vm@5.0.0)(@types/node@20.17.24)(jsdom@24.0.0)(msw@2.6.4(@types/node@20.17.24)(typescript@5.6.3))(terser@5.31.3) @@ -6444,6 +6450,35 @@ packages: resolution: {integrity: sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==} engines: {node: '>=18'} + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/client@1.6.0': + resolution: {integrity: sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==} + engines: {node: '>=14'} + + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/json@1.0.7': + resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/search@1.2.0': + resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/time-series@1.1.0': + resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} + peerDependencies: + '@redis/client': ^1.0.0 + '@redocly/ajv@8.11.2': resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==} @@ -10216,6 +10251,10 @@ packages: peerDependencies: next: '>=13.2.0' + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -13273,6 +13312,9 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + redis@4.7.0: + resolution: {integrity: sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==} + reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -13363,6 +13405,9 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + resumable-stream@2.0.0: + resolution: {integrity: sha512-D7E0wDUnfoy+Lerba/gyuD44OG3G0APqDcQ9soMSerujaVujPLWc5sSCLXf/ZFQPreLb3MKDjSm3TOPXpNtpZw==} + ret@0.5.0: resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} engines: {node: '>=10'} @@ -17123,6 +17168,12 @@ snapshots: eslint: 9.21.0(jiti@2.4.0) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.4.1(eslint@9.21.0)': + dependencies: + eslint: 9.21.0 + eslint-visitor-keys: 3.4.3 + optional: true + '@eslint-community/regexpp@4.11.0': {} '@eslint-community/regexpp@4.12.1': {} @@ -18122,7 +18173,7 @@ snapshots: '@kwsites/promise-deferred@1.1.1': {} - '@langchain/community@0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0(@aws-sdk/client-sso-oidc@3.662.0(@aws-sdk/client-sts@3.662.0))(@aws-sdk/client-sts@3.662.0))(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(ws@8.18.0)': + '@langchain/community@0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0)(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(redis@4.7.0)(ws@8.18.0)': dependencies: '@langchain/core': 0.1.63(openai@4.52.6) '@langchain/openai': 0.0.28 @@ -18142,6 +18193,7 @@ snapshots: ioredis: 5.4.1 jsdom: 26.0.0 lodash: 4.17.21 + redis: 4.7.0 ws: 8.18.0 transitivePeerDependencies: - encoding @@ -18365,7 +18417,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/testing@10.4.12(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.2)(@nestjs/platform-express@10.4.9)': + '@nestjs/testing@10.4.12(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.9)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.9(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.2))': dependencies: '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.9)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -18675,7 +18727,7 @@ snapshots: '@nuxt/ui-templates@1.3.4': {} - '@nuxt/vite-builder@3.14.159(@types/node@20.17.24)(eslint@9.21.0(jiti@2.4.0))(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vue@3.5.13(typescript@5.6.3))(webpack-sources@3.2.3)': + '@nuxt/vite-builder@3.14.159(@types/node@20.17.24)(eslint@9.21.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vue@3.5.13(typescript@5.6.3))(webpack-sources@3.2.3)': dependencies: '@nuxt/kit': 3.14.159(magicast@0.3.5)(rollup@4.34.9) '@rollup/plugin-replace': 6.0.1(rollup@4.34.9) @@ -18709,7 +18761,7 @@ snapshots: unplugin: 1.15.0(webpack-sources@3.2.3) vite: 5.4.11(@types/node@20.17.24)(terser@5.31.3) vite-node: 2.1.4(@types/node@20.17.24)(terser@5.31.3) - vite-plugin-checker: 0.8.0(eslint@9.21.0(jiti@2.4.0))(optionator@0.9.4)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3)) + vite-plugin-checker: 0.8.0(eslint@9.21.0)(optionator@0.9.4)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3)) vue: 3.5.13(typescript@5.6.3) vue-bundle-renderer: 2.1.1 transitivePeerDependencies: @@ -19997,6 +20049,32 @@ snapshots: '@publint/pack@0.1.2': {} + '@redis/bloom@1.2.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/client@1.6.0': + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + + '@redis/graph@1.1.1(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/json@1.0.7(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/search@1.2.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + + '@redis/time-series@1.1.0(@redis/client@1.6.0)': + dependencies: + '@redis/client': 1.6.0 + '@redocly/ajv@8.11.2': dependencies: fast-deep-equal: 3.1.3 @@ -21641,7 +21719,7 @@ snapshots: throttleit: 2.1.0 undici: 5.28.4 - '@vercel/functions@2.0.0(@aws-sdk/credential-provider-web-identity@3.662.0(@aws-sdk/client-sts@3.662.0))': + '@vercel/functions@2.0.0(@aws-sdk/credential-provider-web-identity@3.662.0)': optionalDependencies: '@aws-sdk/credential-provider-web-identity': 3.662.0(@aws-sdk/client-sts@3.662.0) @@ -21787,6 +21865,11 @@ snapshots: vite: 5.4.11(@types/node@20.17.24)(terser@5.31.3) vue: 3.5.13(typescript@5.6.3) + '@vitejs/plugin-vue@5.2.0(vite@5.4.11(@types/node@22.7.4)(terser@5.31.3))(vue@3.5.13(typescript@5.6.3))': + dependencies: + vite: 5.4.11(@types/node@22.7.4)(terser@5.31.3) + vue: 3.5.13(typescript@5.6.3) + '@vitejs/plugin-vue@5.2.0(vite@6.0.3(@types/node@20.17.24)(jiti@2.4.0)(terser@5.31.3)(tsx@4.19.2)(yaml@2.7.0))(vue@3.3.8(typescript@5.6.3))': dependencies: vite: 6.0.3(@types/node@20.17.24)(jiti@2.4.0)(terser@5.31.3)(tsx@4.19.2)(yaml@2.7.0) @@ -24100,7 +24183,7 @@ snapshots: debug: 4.4.0(supports-color@9.4.0) enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.7.2 @@ -24134,7 +24217,7 @@ snapshots: debug: 4.4.0(supports-color@9.4.0) enhanced-resolve: 5.17.1 eslint: 9.21.0(jiti@2.4.0) - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.21.0(jiti@2.4.0)) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.21.0(jiti@2.4.0)))(eslint@9.21.0(jiti@2.4.0)) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.1)(eslint@9.21.0(jiti@2.4.0)) fast-glob: 3.3.2 get-tsconfig: 4.7.2 @@ -24146,7 +24229,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): + eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -24168,7 +24251,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.21.0(jiti@2.4.0)): + eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.21.0(jiti@2.4.0)))(eslint@9.21.0(jiti@2.4.0)): dependencies: debug: 3.2.7 optionalDependencies: @@ -24179,7 +24262,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -24201,7 +24284,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.21.0(jiti@2.4.0)): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.21.0(jiti@2.4.0)))(eslint@9.21.0(jiti@2.4.0)): dependencies: debug: 3.2.7 optionalDependencies: @@ -24222,7 +24305,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -24276,7 +24359,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.21.0(jiti@2.4.0) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.21.0(jiti@2.4.0)) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.21.0(jiti@2.4.0))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@9.21.0(jiti@2.4.0)))(eslint@9.21.0(jiti@2.4.0)) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -24502,6 +24585,46 @@ snapshots: transitivePeerDependencies: - supports-color + eslint@9.21.0: + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.19.2 + '@eslint/core': 0.12.0 + '@eslint/eslintrc': 3.3.0 + '@eslint/js': 9.21.0 + '@eslint/plugin-kit': 0.2.7 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.2 + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0(supports-color@9.4.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.2.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + optional: true + eslint@9.21.0(jiti@2.4.0): dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.21.0(jiti@2.4.0)) @@ -25103,6 +25226,8 @@ snapshots: dependencies: next: 15.2.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + generic-pool@3.9.0: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -26561,10 +26686,10 @@ snapshots: kolorist@1.8.0: {} - langchain@0.1.36(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0(@aws-sdk/client-sso-oidc@3.662.0(@aws-sdk/client-sts@3.662.0))(@aws-sdk/client-sts@3.662.0))(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(fast-xml-parser@4.4.1)(ignore@5.3.2)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(playwright@1.50.1)(ws@8.18.0): + langchain@0.1.36(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0)(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(fast-xml-parser@4.4.1)(ignore@5.3.2)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(playwright@1.50.1)(redis@4.7.0)(ws@8.18.0): dependencies: '@anthropic-ai/sdk': 0.9.1 - '@langchain/community': 0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0(@aws-sdk/client-sso-oidc@3.662.0(@aws-sdk/client-sts@3.662.0))(@aws-sdk/client-sts@3.662.0))(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(ws@8.18.0) + '@langchain/community': 0.0.57(@aws-crypto/sha256-js@5.2.0)(@aws-sdk/client-bedrock-runtime@3.663.0)(@aws-sdk/credential-provider-node@3.662.0)(@smithy/util-utf8@2.3.0)(@upstash/redis@1.34.3)(@vercel/kv@0.2.4)(ioredis@5.4.1)(jsdom@26.0.0)(lodash@4.17.21)(openai@4.52.6)(redis@4.7.0)(ws@8.18.0) '@langchain/core': 0.1.63(openai@4.52.6) '@langchain/openai': 0.0.28 '@langchain/textsplitters': 0.0.2(openai@4.52.6) @@ -26589,6 +26714,7 @@ snapshots: ioredis: 5.4.1 jsdom: 26.0.0 playwright: 1.50.1 + redis: 4.7.0 ws: 8.18.0 transitivePeerDependencies: - '@aws-crypto/sha256-js' @@ -27730,14 +27856,14 @@ snapshots: nuxi@3.15.0: {} - nuxt@3.14.159(@parcel/watcher@2.4.1)(@types/node@20.17.24)(@upstash/redis@1.34.3)(eslint@9.21.0(jiti@2.4.0))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3))(webpack-sources@3.2.3): + nuxt@3.14.159(@parcel/watcher@2.4.1)(@types/node@20.17.24)(@upstash/redis@1.34.3)(eslint@9.21.0)(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3))(webpack-sources@3.2.3): dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/devtools': 1.6.3(rollup@4.34.9)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3))(vue@3.5.13(typescript@5.6.3)) '@nuxt/kit': 3.14.159(magicast@0.3.5)(rollup@4.34.9) '@nuxt/schema': 3.14.159(magicast@0.3.5)(rollup@4.34.9) '@nuxt/telemetry': 2.6.0(magicast@0.3.5)(rollup@4.34.9) - '@nuxt/vite-builder': 3.14.159(@types/node@20.17.24)(eslint@9.21.0(jiti@2.4.0))(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vue@3.5.13(typescript@5.6.3))(webpack-sources@3.2.3) + '@nuxt/vite-builder': 3.14.159(@types/node@20.17.24)(eslint@9.21.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.34.9)(terser@5.31.3)(typescript@5.6.3)(vue@3.5.13(typescript@5.6.3))(webpack-sources@3.2.3) '@unhead/dom': 1.11.11 '@unhead/shared': 1.11.11 '@unhead/ssr': 1.11.11 @@ -28931,6 +29057,15 @@ snapshots: dependencies: redis-errors: 1.2.0 + redis@4.7.0: + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.6.0) + '@redis/client': 1.6.0 + '@redis/graph': 1.1.1(@redis/client@1.6.0) + '@redis/json': 1.0.7(@redis/client@1.6.0) + '@redis/search': 1.2.0(@redis/client@1.6.0) + '@redis/time-series': 1.1.0(@redis/client@1.6.0) + reflect-metadata@0.2.2: {} reflect.getprototypeof@1.0.4: @@ -29040,6 +29175,8 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 + resumable-stream@2.0.0: {} + ret@0.5.0: {} retry@0.13.1: {} @@ -30981,7 +31118,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.8.0(eslint@9.21.0(jiti@2.4.0))(optionator@0.9.4)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3)): + vite-plugin-checker@0.8.0(eslint@9.21.0)(optionator@0.9.4)(typescript@5.6.3)(vite@5.4.11(@types/node@20.17.24)(terser@5.31.3)): dependencies: '@babel/code-frame': 7.26.2 ansi-escapes: 4.3.2 @@ -30999,7 +31136,7 @@ snapshots: vscode-languageserver-textdocument: 1.0.11 vscode-uri: 3.0.8 optionalDependencies: - eslint: 9.21.0(jiti@2.4.0) + eslint: 9.21.0 optionator: 0.9.4 typescript: 5.6.3 @@ -31036,7 +31173,7 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-solid@2.7.2(solid-js@1.8.7): + vite-plugin-solid@2.7.2(solid-js@1.8.7)(vite@5.4.11(@types/node@22.7.4)(terser@5.31.3)): dependencies: '@babel/core': 7.26.0 '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) @@ -31045,7 +31182,8 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.8.7 solid-refresh: 0.5.3(solid-js@1.8.7) - vitefu: 0.2.5(vite@6.0.3(@types/node@22.7.4)(jiti@2.4.0)(terser@5.31.3)(tsx@4.19.2)(yaml@2.7.0)) + vite: 5.4.11(@types/node@22.7.4)(terser@5.31.3) + vitefu: 0.2.5(vite@5.4.11(@types/node@22.7.4)(terser@5.31.3)) transitivePeerDependencies: - supports-color @@ -31110,6 +31248,10 @@ snapshots: tsx: 4.19.2 yaml: 2.7.0 + vitefu@0.2.5(vite@5.4.11(@types/node@22.7.4)(terser@5.31.3)): + optionalDependencies: + vite: 5.4.11(@types/node@22.7.4)(terser@5.31.3) + vitefu@0.2.5(vite@6.0.3(@types/node@22.7.4)(jiti@2.4.0)(terser@5.31.3)(tsx@4.19.2)(yaml@2.7.0)): optionalDependencies: vite: 6.0.3(@types/node@22.7.4)(jiti@2.4.0)(terser@5.31.3)(tsx@4.19.2)(yaml@2.7.0)