e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
93 lines
2.5 KiB
TypeScript
93 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
import {
|
|
AssistantRuntimeProvider,
|
|
type ThreadMessage,
|
|
} from "@assistant-ui/react";
|
|
import { HttpAgent } from "@ag-ui/client";
|
|
import { useAgUiRuntime } from "@assistant-ui/react-ag-ui";
|
|
|
|
type StoredThread = {
|
|
id: string;
|
|
messages: readonly ThreadMessage[];
|
|
};
|
|
|
|
/**
|
|
* AG-UI runtime with threadList adapter for multi-thread support.
|
|
*/
|
|
export function MyRuntimeProvider({
|
|
children,
|
|
}: Readonly<{ children: ReactNode }>) {
|
|
const agentUrl =
|
|
(process.env.NEXT_PUBLIC_AGUI_AGENT_URL as string | undefined) ??
|
|
"http://localhost:8000/agent";
|
|
|
|
// Simple in-memory thread storage
|
|
const threadsRef = useRef<Map<string, StoredThread>>(new Map());
|
|
const [currentThreadId, setCurrentThreadId] = useState<string>(() => {
|
|
const id = crypto.randomUUID();
|
|
threadsRef.current.set(id, { id, messages: [] });
|
|
return id;
|
|
});
|
|
|
|
const agent = useMemo(() => {
|
|
return new HttpAgent({
|
|
url: agentUrl,
|
|
threadId: currentThreadId,
|
|
headers: {
|
|
Accept: "text/event-stream",
|
|
},
|
|
});
|
|
}, [agentUrl, currentThreadId]);
|
|
|
|
const threadListAdapter = useMemo(
|
|
() => ({
|
|
threadId: currentThreadId,
|
|
onSwitchToNewThread: async () => {
|
|
const newId = crypto.randomUUID();
|
|
threadsRef.current.set(newId, { id: newId, messages: [] });
|
|
setCurrentThreadId(newId);
|
|
console.debug("[agui] Switched to new thread:", newId);
|
|
},
|
|
onSwitchToThread: async (threadId: string) => {
|
|
const thread = threadsRef.current.get(threadId);
|
|
if (!thread) {
|
|
throw new Error(`Thread ${threadId} not found`);
|
|
}
|
|
setCurrentThreadId(threadId);
|
|
console.debug("[agui] Switched to thread:", threadId);
|
|
return { messages: thread.messages };
|
|
},
|
|
}),
|
|
[currentThreadId],
|
|
);
|
|
|
|
const runtime = useAgUiRuntime({
|
|
agent,
|
|
logger: {
|
|
debug: (...a: any[]) => console.debug("[agui]", ...a),
|
|
error: (...a: any[]) => console.error("[agui]", ...a),
|
|
},
|
|
adapters: {
|
|
threadList: threadListAdapter,
|
|
},
|
|
});
|
|
|
|
// Persist messages to threadsRef when they change
|
|
useEffect(() => {
|
|
return runtime.thread.subscribe(() => {
|
|
threadsRef.current.set(currentThreadId, {
|
|
id: currentThreadId,
|
|
messages: runtime.thread.getState().messages,
|
|
});
|
|
});
|
|
}, [runtime, currentThreadId]);
|
|
|
|
return (
|
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
{children}
|
|
</AssistantRuntimeProvider>
|
|
);
|
|
}
|