@assistant-ui/react
Version:
Typescript/React library for AI Chat
455 lines (454 loc) • 15.7 kB
JavaScript
"use strict";
"use client";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/runtimes/remote-thread-list/RemoteThreadListThreadListRuntimeCore.tsx
var RemoteThreadListThreadListRuntimeCore_exports = {};
__export(RemoteThreadListThreadListRuntimeCore_exports, {
RemoteThreadListThreadListRuntimeCore: () => RemoteThreadListThreadListRuntimeCore
});
module.exports = __toCommonJS(RemoteThreadListThreadListRuntimeCore_exports);
var import_internal = require("../../internal.js");
var import_RemoteThreadListHookInstanceManager = require("./RemoteThreadListHookInstanceManager.js");
var import_BaseSubscribable = require("./BaseSubscribable.js");
var import_EMPTY_THREAD_CORE = require("./EMPTY_THREAD_CORE.js");
var import_OptimisticState = require("./OptimisticState.js");
var import_react = require("react");
var import_zustand = require("zustand");
var import_assistant_stream = require("assistant-stream");
var import_RuntimeAdapterProvider = require("../adapters/RuntimeAdapterProvider.js");
var import_jsx_runtime = require("react/jsx-runtime");
function createThreadMappingId(id) {
return id;
}
var getThreadData = (state, threadIdOrRemoteId) => {
const idx = state.threadIdMap[threadIdOrRemoteId];
if (idx === void 0) return void 0;
return state.threadData[idx];
};
var updateStatusReducer = (state, threadIdOrRemoteId, newStatus) => {
const data = getThreadData(state, threadIdOrRemoteId);
if (!data) return state;
const { threadId, remoteId, status: lastStatus } = data;
if (lastStatus === newStatus) return state;
const newState = { ...state };
switch (lastStatus) {
case "new":
newState.newThreadId = void 0;
break;
case "regular":
newState.threadIds = newState.threadIds.filter((t) => t !== threadId);
break;
case "archived":
newState.archivedThreadIds = newState.archivedThreadIds.filter(
(t) => t !== threadId
);
break;
default: {
const _exhaustiveCheck = lastStatus;
throw new Error(`Unsupported state: ${_exhaustiveCheck}`);
}
}
switch (newStatus) {
case "regular":
newState.threadIds = [threadId, ...newState.threadIds];
break;
case "archived":
newState.archivedThreadIds = [threadId, ...newState.archivedThreadIds];
break;
case "deleted":
newState.threadData = Object.fromEntries(
Object.entries(newState.threadData).filter(([key]) => key !== threadId)
);
newState.threadIdMap = Object.fromEntries(
Object.entries(newState.threadIdMap).filter(
([key]) => key !== threadId && key !== remoteId
)
);
break;
default: {
const _exhaustiveCheck = newStatus;
throw new Error(`Unsupported state: ${_exhaustiveCheck}`);
}
}
if (newStatus !== "deleted") {
newState.threadData = {
...newState.threadData,
[threadId]: {
...data,
status: newStatus
}
};
}
return newState;
};
var RemoteThreadListThreadListRuntimeCore = class extends import_BaseSubscribable.BaseSubscribable {
constructor(options, contextProvider) {
super();
this.contextProvider = contextProvider;
this._state.subscribe(() => this._notifySubscribers());
this._hookManager = new import_RemoteThreadListHookInstanceManager.RemoteThreadListHookInstanceManager(
options.runtimeHook
);
this.useProvider = (0, import_zustand.create)(() => ({
Provider: options.adapter.unstable_Provider ?? import_react.Fragment
}));
this.__internal_setOptions(options);
this.switchToNewThread();
}
_options;
_hookManager;
_loadThreadsPromise;
_mainThreadId;
_state = new import_OptimisticState.OptimisticState({
isLoading: false,
newThreadId: void 0,
threadIds: [],
archivedThreadIds: [],
threadIdMap: {},
threadData: {}
});
getLoadThreadsPromise() {
if (!this._loadThreadsPromise) {
this._loadThreadsPromise = this._state.optimisticUpdate({
execute: () => this._options.adapter.list(),
loading: (state) => {
return {
...state,
isLoading: true
};
},
then: (state, l) => {
const newThreadIds = [];
const newArchivedThreadIds = [];
const newThreadIdMap = {};
const newThreadData = {};
for (const thread of l.threads) {
switch (thread.status) {
case "regular":
newThreadIds.push(thread.remoteId);
break;
case "archived":
newArchivedThreadIds.push(thread.remoteId);
break;
default: {
const _exhaustiveCheck = thread.status;
throw new Error(`Unsupported state: ${_exhaustiveCheck}`);
}
}
const mappingId = createThreadMappingId(thread.remoteId);
newThreadIdMap[thread.remoteId] = mappingId;
newThreadData[mappingId] = {
threadId: thread.remoteId,
remoteId: thread.remoteId,
externalId: thread.externalId,
status: thread.status,
title: thread.title,
initializeTask: Promise.resolve({
remoteId: thread.remoteId,
externalId: thread.externalId
})
};
}
return {
...state,
threadIds: newThreadIds,
archivedThreadIds: newArchivedThreadIds,
threadIdMap: {
...state.threadIdMap,
...newThreadIdMap
},
threadData: {
...state.threadData,
...newThreadData
}
};
}
}).then(() => {
});
}
return this._loadThreadsPromise;
}
useProvider;
__internal_setOptions(options) {
if (this._options === options) return;
this._options = options;
const Provider = options.adapter.unstable_Provider ?? import_react.Fragment;
if (Provider !== this.useProvider.getState().Provider) {
this.useProvider.setState({ Provider }, true);
}
this._hookManager.setRuntimeHook(options.runtimeHook);
}
__internal_load() {
this.getLoadThreadsPromise();
}
get threadIds() {
return this._state.value.threadIds;
}
get archivedThreadIds() {
return this._state.value.archivedThreadIds;
}
get newThreadId() {
return this._state.value.newThreadId;
}
get mainThreadId() {
return this._mainThreadId;
}
getMainThreadRuntimeCore() {
const result = this._hookManager.getThreadRuntimeCore(this._mainThreadId);
if (!result) return import_EMPTY_THREAD_CORE.EMPTY_THREAD_CORE;
return result;
}
getThreadRuntimeCore(threadIdOrRemoteId) {
const data = this.getItemById(threadIdOrRemoteId);
if (!data) throw new Error("Thread not found");
const result = this._hookManager.getThreadRuntimeCore(data.threadId);
if (!result) throw new Error("Thread not found");
return result;
}
getItemById(threadIdOrRemoteId) {
return getThreadData(this._state.value, threadIdOrRemoteId);
}
async switchToThread(threadIdOrRemoteId) {
const data = this.getItemById(threadIdOrRemoteId);
if (!data) throw new Error("Thread not found");
if (this._mainThreadId === data.threadId) return;
const task = this._hookManager.startThreadRuntime(data.threadId);
if (this.mainThreadId !== void 0) {
await task;
} else {
task.then(() => this._notifySubscribers());
}
if (data.status === "archived") await this.unarchive(data.threadId);
this._mainThreadId = data.threadId;
this._notifySubscribers();
}
async switchToNewThread() {
while (this._state.baseValue.newThreadId !== void 0 && this._state.value.newThreadId === void 0) {
await this._state.waitForUpdate();
}
const state = this._state.value;
let threadId = this._state.value.newThreadId;
if (threadId === void 0) {
do {
threadId = `__LOCALID_${(0, import_internal.generateId)()}`;
} while (state.threadIdMap[threadId]);
const mappingId = createThreadMappingId(threadId);
this._state.update({
...state,
newThreadId: threadId,
threadIdMap: {
...state.threadIdMap,
[threadId]: mappingId
},
threadData: {
...state.threadData,
[threadId]: {
status: "new",
threadId
}
}
});
}
return this.switchToThread(threadId);
}
initialize = async (threadId) => {
if (this._state.value.newThreadId !== threadId) {
const data = this.getItemById(threadId);
if (!data) throw new Error("Thread not found");
if (data.status === "new") throw new Error("Unexpected new state");
return data.initializeTask;
}
return this._state.optimisticUpdate({
execute: () => {
return this._options.adapter.initialize(threadId);
},
optimistic: (state) => {
return updateStatusReducer(state, threadId, "regular");
},
loading: (state, task) => {
const mappingId = createThreadMappingId(threadId);
return {
...state,
threadData: {
...state.threadData,
[mappingId]: {
...state.threadData[mappingId],
initializeTask: task
}
}
};
},
then: (state, { remoteId, externalId }) => {
const data = getThreadData(state, threadId);
if (!data) return state;
const mappingId = createThreadMappingId(threadId);
return {
...state,
threadIdMap: {
...state.threadIdMap,
[remoteId]: mappingId
},
threadData: {
...state.threadData,
[mappingId]: {
...data,
initializeTask: Promise.resolve({ remoteId, externalId }),
remoteId,
externalId
}
}
};
}
});
};
generateTitle = async (threadId) => {
const data = this.getItemById(threadId);
if (!data) throw new Error("Thread not found");
if (data.status === "new") throw new Error("Thread is not yet initialized");
const { remoteId } = await data.initializeTask;
const messages = this.getThreadRuntimeCore(threadId).messages;
const stream = await this._options.adapter.generateTitle(
remoteId,
messages
);
const messageStream = import_assistant_stream.AssistantMessageStream.fromAssistantStream(stream);
for await (const result of messageStream) {
const newTitle = result.parts.filter((c) => c.type === "text")[0]?.text ?? "New Thread";
const state = this._state.baseValue;
this._state.update({
...state,
threadData: {
...state.threadData,
[data.threadId]: {
...data,
title: newTitle
}
}
});
}
};
rename(threadIdOrRemoteId, newTitle) {
const data = this.getItemById(threadIdOrRemoteId);
if (!data) throw new Error("Thread not found");
if (data.status === "new") throw new Error("Thread is not yet initialized");
return this._state.optimisticUpdate({
execute: async () => {
const { remoteId } = await data.initializeTask;
return this._options.adapter.rename(remoteId, newTitle);
},
optimistic: (state) => {
const data2 = getThreadData(state, threadIdOrRemoteId);
if (!data2) return state;
return {
...state,
threadData: {
...state.threadData,
[data2.threadId]: {
...data2,
title: newTitle
}
}
};
}
});
}
async _ensureThreadIsNotMain(threadId) {
if (threadId === this._mainThreadId) {
await this.switchToNewThread();
}
}
async archive(threadIdOrRemoteId) {
const data = this.getItemById(threadIdOrRemoteId);
if (!data) throw new Error("Thread not found");
if (data.status !== "regular")
throw new Error("Thread is not yet initialized or already archived");
return this._state.optimisticUpdate({
execute: async () => {
await this._ensureThreadIsNotMain(data.threadId);
const { remoteId } = await data.initializeTask;
return this._options.adapter.archive(remoteId);
},
optimistic: (state) => {
return updateStatusReducer(state, data.threadId, "archived");
}
});
}
unarchive(threadIdOrRemoteId) {
const data = this.getItemById(threadIdOrRemoteId);
if (!data) throw new Error("Thread not found");
if (data.status !== "archived") throw new Error("Thread is not archived");
return this._state.optimisticUpdate({
execute: async () => {
try {
const { remoteId } = await data.initializeTask;
return await this._options.adapter.unarchive(remoteId);
} catch (error) {
await this._ensureThreadIsNotMain(data.threadId);
throw error;
}
},
optimistic: (state) => {
return updateStatusReducer(state, data.threadId, "regular");
}
});
}
async delete(threadIdOrRemoteId) {
const data = this.getItemById(threadIdOrRemoteId);
if (!data) throw new Error("Thread not found");
if (data.status !== "regular" && data.status !== "archived")
throw new Error("Thread is not yet initialized");
return this._state.optimisticUpdate({
execute: async () => {
await this._ensureThreadIsNotMain(data.threadId);
const { remoteId } = await data.initializeTask;
return await this._options.adapter.delete(remoteId);
},
optimistic: (state) => {
return updateStatusReducer(state, data.threadId, "deleted");
}
});
}
useBoundIds = (0, import_zustand.create)(() => []);
__internal_RenderComponent = () => {
const id = (0, import_react.useId)();
(0, import_react.useEffect)(() => {
this.useBoundIds.setState((s) => [...s, id], true);
return () => {
this.useBoundIds.setState((s) => s.filter((i) => i !== id), true);
};
}, [id]);
const boundIds = this.useBoundIds();
const { Provider } = this.useProvider();
const adapters = {
modelContext: this.contextProvider
};
return (boundIds.length === 0 || boundIds[0] === id) && // only render if the component is the first one mounted
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_RuntimeAdapterProvider.RuntimeAdapterProvider, { adapters, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
this._hookManager.__internal_RenderThreadRuntimes,
{
provider: Provider
}
) });
};
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
RemoteThreadListThreadListRuntimeCore
});
//# sourceMappingURL=RemoteThreadListThreadListRuntimeCore.js.map