UNPKG

@ai-sdk/solid

Version:

> **Warning** `@ai-sdk/solid` has been deprecated and will be removed in AI SDK 5

849 lines (841 loc) 26.8 kB
"use strict"; 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/index.ts var src_exports = {}; __export(src_exports, { experimental_useObject: () => experimental_useObject, useAssistant: () => useAssistant, useChat: () => useChat, useCompletion: () => useCompletion }); module.exports = __toCommonJS(src_exports); // src/use-chat.ts var import_ui_utils = require("@ai-sdk/ui-utils"); var import_solid_js3 = require("solid-js"); var import_store = require("solid-js/store"); // src/utils/convert-to-accessor-options.ts var import_solid_js = require("solid-js"); function convertToAccessorOptions(options) { const resolvedOptions = typeof options === "function" ? options() : options; return Object.entries(resolvedOptions).reduce( (reactiveOptions, [key, value]) => { reactiveOptions[key] = (0, import_solid_js.createMemo)(() => value); return reactiveOptions; }, {} ); } // src/utils/reactive-lru.ts var import_solid_js2 = require("solid-js"); var import_trigger = require("@solid-primitives/trigger"); var $KEYS = Symbol("track-keys"); var ReactiveLRU = class extends Map { #keyTriggers = new import_trigger.TriggerCache(); #valueTriggers = new import_trigger.TriggerCache(); #maxSize; #accessList = []; constructor(maxSize = 10, initial) { super(); this.#maxSize = maxSize; if (initial) { for (const [key, value] of initial) { this.set(key, value); } } } #recordAccess(key) { const index = this.#accessList.indexOf(key); if (index > -1) { this.#accessList.splice(index, 1); } this.#accessList.push(key); if (this.#accessList.length > this.#maxSize) { const lru = this.#accessList.shift(); this.delete(lru); } } // reads has(key) { this.#keyTriggers.track(key); const exists = super.has(key); if (exists) { this.#recordAccess(key); } return exists; } get(key) { this.#valueTriggers.track(key); const value = super.get(key); if (value !== void 0) { this.#recordAccess(key); } return value; } get size() { this.#keyTriggers.track($KEYS); return super.size; } *keys() { for (const key of super.keys()) { this.#keyTriggers.track(key); yield key; } this.#keyTriggers.track($KEYS); } *values() { for (const [key, v] of super.entries()) { this.#valueTriggers.track(key); yield v; } this.#keyTriggers.track($KEYS); } *entries() { for (const entry of super.entries()) { this.#valueTriggers.track(entry[0]); yield entry; } this.#keyTriggers.track($KEYS); } // writes set(key, value) { (0, import_solid_js2.batch)(() => { if (super.has(key)) { if (super.get(key) === value) { this.#recordAccess(key); return; } } else { this.#keyTriggers.dirty(key); this.#keyTriggers.dirty($KEYS); } this.#valueTriggers.dirty(key); super.set(key, value); this.#recordAccess(key); }); return this; } delete(key) { const r = super.delete(key); if (r) { (0, import_solid_js2.batch)(() => { this.#keyTriggers.dirty(key); this.#keyTriggers.dirty($KEYS); this.#valueTriggers.dirty(key); const index = this.#accessList.indexOf(key); if (index > -1) { this.#accessList.splice(index, 1); } }); } return r; } clear() { if (super.size) { (0, import_solid_js2.batch)(() => { for (const v of super.keys()) { this.#keyTriggers.dirty(v); this.#valueTriggers.dirty(v); } super.clear(); this.#accessList = []; this.#keyTriggers.dirty($KEYS); }); } } // callback forEach(callbackfn) { this.#keyTriggers.track($KEYS); for (const [key, v] of super.entries()) { this.#valueTriggers.track(key); this.#recordAccess(key); callbackfn(v, key, this); } } [Symbol.iterator]() { return this.entries(); } }; // src/use-chat.ts var chatCache = new ReactiveLRU(); function useChat(rawUseChatOptions = {}) { const resolvedOptions = (0, import_solid_js3.createMemo)( () => convertToAccessorOptions(rawUseChatOptions) ); const prepareFn = (0, import_solid_js3.createMemo)(() => { const opts = resolvedOptions(); return opts.experimental_prepareRequestBody?.(); }); const useChatOptions = (0, import_solid_js3.createMemo)(() => ({ ...resolvedOptions(), experimental_prepareRequestBody: prepareFn })); const api = (0, import_solid_js3.createMemo)(() => useChatOptions().api?.() ?? "/api/chat"); const generateId2 = (0, import_solid_js3.createMemo)( () => useChatOptions().generateId?.() ?? import_ui_utils.generateId ); const chatId = (0, import_solid_js3.createMemo)(() => useChatOptions().id?.() ?? generateId2()()); const chatKey = (0, import_solid_js3.createMemo)(() => `${api()}|${chatId()}|messages`); const _messages = (0, import_solid_js3.createMemo)( () => chatCache.get(chatKey()) ?? useChatOptions().initialMessages?.() ?? [] ); const [messagesStore, setMessagesStore] = (0, import_store.createStore)( (0, import_ui_utils.fillMessageParts)(_messages()) ); (0, import_solid_js3.createEffect)(() => { setMessagesStore((0, import_store.reconcile)((0, import_ui_utils.fillMessageParts)(_messages()), { merge: true })); }); const mutate = (messages) => { chatCache.set(chatKey(), messages); }; const [error, setError] = (0, import_solid_js3.createSignal)(void 0); const [streamData, setStreamData] = (0, import_solid_js3.createSignal)( void 0 ); const [status, setStatus] = (0, import_solid_js3.createSignal)("ready"); let messagesRef = (0, import_ui_utils.fillMessageParts)(_messages()) || []; (0, import_solid_js3.createEffect)(() => { messagesRef = (0, import_ui_utils.fillMessageParts)(_messages()) || []; }); let abortController = null; let extraMetadata = { credentials: useChatOptions().credentials?.(), headers: useChatOptions().headers?.(), body: useChatOptions().body?.() }; (0, import_solid_js3.createEffect)(() => { extraMetadata = { credentials: useChatOptions().credentials?.(), headers: useChatOptions().headers?.(), body: useChatOptions().body?.() }; }); const triggerRequest = async (chatRequest) => { setError(void 0); setStatus("submitted"); const messageCount = messagesRef.length; const maxStep = (0, import_ui_utils.extractMaxToolInvocationStep)( chatRequest.messages[chatRequest.messages.length - 1]?.toolInvocations ); try { abortController = new AbortController(); const streamProtocol = useChatOptions().streamProtocol?.() ?? "data"; const onFinish = useChatOptions().onFinish?.(); const onResponse = useChatOptions().onResponse?.(); const onToolCall = useChatOptions().onToolCall?.(); const sendExtraMessageFields = useChatOptions().sendExtraMessageFields?.(); const keepLastMessageOnError = useChatOptions().keepLastMessageOnError?.() ?? true; const experimental_prepareRequestBody = useChatOptions().experimental_prepareRequestBody?.(); const previousMessages = messagesRef; const chatMessages = (0, import_ui_utils.fillMessageParts)(chatRequest.messages); mutate(chatMessages); const existingStreamData = streamData() ?? []; const constructedMessagesPayload = sendExtraMessageFields ? chatMessages : chatMessages.map( ({ role, content, experimental_attachments, data, annotations, toolInvocations, parts }) => ({ role, content, ...experimental_attachments !== void 0 && { experimental_attachments }, ...data !== void 0 && { data }, ...annotations !== void 0 && { annotations }, ...toolInvocations !== void 0 && { toolInvocations }, ...parts !== void 0 && { parts } }) ); await (0, import_ui_utils.callChatApi)({ api: api(), body: experimental_prepareRequestBody?.({ id: chatId(), messages: chatMessages, requestData: chatRequest.data, requestBody: chatRequest.body }) ?? { id: chatId(), messages: constructedMessagesPayload, data: chatRequest.data, ...extraMetadata.body, ...chatRequest.body }, streamProtocol, credentials: extraMetadata.credentials, headers: { ...extraMetadata.headers, ...chatRequest.headers }, abortController: () => abortController, restoreMessagesOnFailure() { if (!keepLastMessageOnError) { mutate(previousMessages); } }, onResponse, onUpdate({ message, data, replaceLastMessage }) { setStatus("streaming"); mutate([ ...replaceLastMessage ? chatMessages.slice(0, chatMessages.length - 1) : chatMessages, message ]); if (data?.length) { setStreamData([...existingStreamData, ...data]); } }, onToolCall, onFinish, generateId: generateId2(), fetch: useChatOptions().fetch?.(), lastMessage: chatMessages[chatMessages.length - 1] }); abortController = null; setStatus("ready"); } catch (err) { if (err.name === "AbortError") { abortController = null; setStatus("ready"); return null; } const onError = useChatOptions().onError?.(); if (onError && err instanceof Error) { onError(err); } setError(err); setStatus("error"); } const maxSteps = useChatOptions().maxSteps?.() ?? 1; const messages = messagesRef; if ((0, import_ui_utils.shouldResubmitMessages)({ originalMaxToolInvocationStep: maxStep, originalMessageCount: messageCount, maxSteps, messages })) { await triggerRequest({ messages }); } }; const append = async (message, { data, headers, body, experimental_attachments = message.experimental_attachments } = {}) => { const attachmentsForRequest = await (0, import_ui_utils.prepareAttachmentsForRequest)( experimental_attachments ); const messages = messagesRef.concat({ ...message, id: message.id ?? generateId2()(), createdAt: message.createdAt ?? /* @__PURE__ */ new Date(), experimental_attachments: attachmentsForRequest.length > 0 ? attachmentsForRequest : void 0, parts: (0, import_ui_utils.getMessageParts)(message) }); return triggerRequest({ messages, headers, body, data }); }; const reload = async ({ data, headers, body } = {}) => { if (messagesRef.length === 0) { return null; } const lastMessage = messagesRef[messagesRef.length - 1]; return triggerRequest({ messages: lastMessage.role === "assistant" ? messagesRef.slice(0, -1) : messagesRef, headers, body, data }); }; const stop = () => { if (abortController) { abortController.abort(); abortController = null; } }; const setMessages = (messagesArg) => { if (typeof messagesArg === "function") { messagesArg = messagesArg(messagesRef); } const messagesWithParts = (0, import_ui_utils.fillMessageParts)(messagesArg); mutate(messagesWithParts); messagesRef = messagesWithParts; }; const setData = (dataArg) => { if (typeof dataArg === "function") { dataArg = dataArg(streamData()); } setStreamData(dataArg); }; const [input, setInput] = (0, import_solid_js3.createSignal)( useChatOptions().initialInput?.() || "" ); const handleSubmit = async (event, options = {}, metadata) => { event?.preventDefault?.(); const inputValue = input(); if (!inputValue && !options.allowEmptySubmit) return; const attachmentsForRequest = await (0, import_ui_utils.prepareAttachmentsForRequest)( options.experimental_attachments ); if (metadata) { extraMetadata = { ...extraMetadata, ...metadata }; } triggerRequest({ messages: messagesRef.concat({ id: generateId2()(), role: "user", content: inputValue, createdAt: /* @__PURE__ */ new Date(), experimental_attachments: attachmentsForRequest.length > 0 ? attachmentsForRequest : void 0, parts: [{ type: "text", text: inputValue }] }), headers: options.headers, body: options.body, data: options.data }); setInput(""); }; const handleInputChange = (e) => { setInput(e.target.value); }; const addToolResult = ({ toolCallId, result }) => { const currentMessages = messagesRef ?? []; (0, import_ui_utils.updateToolCallResult)({ messages: currentMessages, toolCallId, toolResult: result }); mutate(currentMessages); if (status() === "submitted" || status() === "streaming") { return; } const lastMessage = currentMessages[currentMessages.length - 1]; if ((0, import_ui_utils.isAssistantMessageWithCompletedToolCalls)(lastMessage)) { triggerRequest({ messages: currentMessages }); } }; const isLoading = (0, import_solid_js3.createMemo)( () => status() === "submitted" || status() === "streaming" ); return { // TODO next major release: replace with direct message store access (breaking change) messages: () => messagesStore, id: chatId(), append, error, reload, stop, setMessages, input, setInput, handleInputChange, handleSubmit, isLoading, status, data: streamData, setData, addToolResult }; } // src/use-completion.ts var import_ui_utils2 = require("@ai-sdk/ui-utils"); var import_solid_js4 = require("solid-js"); var completionCache = new ReactiveLRU(); function useCompletion(rawUseCompletionOptions = {}) { const useCompletionOptions = (0, import_solid_js4.createMemo)( () => convertToAccessorOptions(rawUseCompletionOptions) ); const api = (0, import_solid_js4.createMemo)( () => useCompletionOptions().api?.() ?? "/api/completion" ); const idKey = (0, import_solid_js4.createMemo)( () => useCompletionOptions().id?.() ?? `completion-${(0, import_solid_js4.createUniqueId)()}` ); const completionKey = (0, import_solid_js4.createMemo)(() => `${api()}|${idKey()}|completion`); const completion = (0, import_solid_js4.createMemo)( () => completionCache.get(completionKey()) ?? useCompletionOptions().initialCompletion?.() ?? "" ); const mutate = (data) => { completionCache.set(completionKey(), data); }; const [error, setError] = (0, import_solid_js4.createSignal)(void 0); const [streamData, setStreamData] = (0, import_solid_js4.createSignal)( void 0 ); const [isLoading, setIsLoading] = (0, import_solid_js4.createSignal)(false); const [abortController, setAbortController] = (0, import_solid_js4.createSignal)(null); let extraMetadata = { credentials: useCompletionOptions().credentials?.(), headers: useCompletionOptions().headers?.(), body: useCompletionOptions().body?.() }; (0, import_solid_js4.createEffect)(() => { extraMetadata = { credentials: useCompletionOptions().credentials?.(), headers: useCompletionOptions().headers?.(), body: useCompletionOptions().body?.() }; }); const complete = async (prompt, options) => { const existingData = streamData() ?? []; return (0, import_ui_utils2.callCompletionApi)({ api: api(), prompt, credentials: useCompletionOptions().credentials?.(), headers: { ...extraMetadata.headers, ...options?.headers }, body: { ...extraMetadata.body, ...options?.body }, streamProtocol: useCompletionOptions().streamProtocol?.(), setCompletion: mutate, setLoading: setIsLoading, setError, setAbortController, onResponse: useCompletionOptions().onResponse?.(), onFinish: useCompletionOptions().onFinish?.(), onError: useCompletionOptions().onError?.(), onData: (data) => { setStreamData([...existingData, ...data ?? []]); }, fetch: useCompletionOptions().fetch?.() }); }; const stop = () => { if (abortController()) { abortController().abort(); } }; const setCompletion = (completion2) => { mutate(completion2); }; const [input, setInput] = (0, import_solid_js4.createSignal)( useCompletionOptions().initialInput?.() ?? "" ); const handleInputChange = (event) => { setInput(event.target.value); }; const handleSubmit = (event) => { event?.preventDefault?.(); const inputValue = input(); return inputValue ? complete(inputValue) : void 0; }; return { completion, complete, error, stop, setCompletion, input, setInput, handleInputChange, handleSubmit, isLoading, data: streamData }; } // src/use-object.ts var import_provider_utils = require("@ai-sdk/provider-utils"); var import_ui_utils3 = require("@ai-sdk/ui-utils"); var import_solid_js5 = require("solid-js"); var getOriginalFetch = () => fetch; var objectCache = new ReactiveLRU(); function useObject(rawUseObjectOptions) { const useObjectOptions = (0, import_solid_js5.createMemo)( () => convertToAccessorOptions(rawUseObjectOptions) ); const idKey = (0, import_solid_js5.createMemo)( () => useObjectOptions().id?.() ?? `object-${(0, import_solid_js5.createUniqueId)()}` ); const data = (0, import_solid_js5.createMemo)( () => objectCache.get(idKey()) ?? useObjectOptions().initialValue?.() ); const mutate = (value) => { objectCache.set(idKey(), value); }; const [error, setError] = (0, import_solid_js5.createSignal)(); const [isLoading, setIsLoading] = (0, import_solid_js5.createSignal)(false); let abortControllerRef = null; const stop = () => { try { abortControllerRef?.abort(); } catch (ignored) { } finally { setIsLoading(false); abortControllerRef = null; } }; const submit = async (input) => { try { mutate(void 0); setIsLoading(true); setError(void 0); const abortController = new AbortController(); abortControllerRef = abortController; const actualFetch = fetch ?? getOriginalFetch(); const response = await actualFetch(useObjectOptions().api(), { method: "POST", headers: { "Content-Type": "application/json", ...useObjectOptions().headers?.() }, credentials: useObjectOptions().credentials?.(), signal: abortController.signal, body: JSON.stringify(input) }); if (!response.ok) { throw new Error( await response.text() ?? "Failed to fetch the response." ); } if (response.body == null) { throw new Error("The response body is empty."); } let accumulatedText = ""; let latestObject = void 0; await response.body.pipeThrough(new TextDecoderStream()).pipeTo( new WritableStream({ write(chunk) { accumulatedText += chunk; const { value } = (0, import_ui_utils3.parsePartialJson)(accumulatedText); const currentObject = value; if (!(0, import_ui_utils3.isDeepEqualData)(latestObject, currentObject)) { latestObject = currentObject; mutate(currentObject); } }, close() { setIsLoading(false); abortControllerRef = null; const onFinish = useObjectOptions().onFinish?.(); if (onFinish != null) { const validationResult = (0, import_provider_utils.safeValidateTypes)({ value: latestObject, schema: (0, import_ui_utils3.asSchema)(useObjectOptions().schema()) }); onFinish( validationResult.success ? { object: validationResult.value, error: void 0 } : { object: void 0, error: validationResult.error } ); } } }) ); } catch (error2) { if ((0, import_provider_utils.isAbortError)(error2)) { return; } const onError = useObjectOptions().onError?.(); if (onError && error2 instanceof Error) { onError(error2); } setIsLoading(false); setError(error2 instanceof Error ? error2 : new Error(String(error2))); } }; return { submit, object: data, error, isLoading, stop }; } var experimental_useObject = useObject; // src/use-assistant.ts var import_provider_utils2 = require("@ai-sdk/provider-utils"); var import_ui_utils4 = require("@ai-sdk/ui-utils"); var import_solid_js6 = require("solid-js"); var import_store2 = require("solid-js/store"); var getOriginalFetch2 = () => fetch; function useAssistant(rawUseAssistantOptions) { const useAssistantOptions = (0, import_solid_js6.createMemo)( () => convertToAccessorOptions(rawUseAssistantOptions) ); const [messages, setMessages] = (0, import_store2.createStore)([]); const [input, setInput] = (0, import_solid_js6.createSignal)(""); const [currentThreadId, setCurrentThreadId] = (0, import_solid_js6.createSignal)(); const [status, setStatus] = (0, import_solid_js6.createSignal)("awaiting_message"); const [error, setError] = (0, import_solid_js6.createSignal)(); const handleInputChange = (event) => { setInput(event.target.value); }; let abortControllerRef = null; const stop = () => { if (abortControllerRef) { abortControllerRef?.abort(); abortControllerRef = null; } }; const append = async (message, requestOptions) => { setStatus("in_progress"); setMessages((messages2) => [ ...messages2, { ...message, id: message.id ?? (0, import_ui_utils4.generateId)() } ]); setInput(""); const abortController = new AbortController(); try { abortControllerRef = abortController; const actualFetch = fetch ?? getOriginalFetch2(); const response = await actualFetch(useAssistantOptions().api(), { method: "POST", credentials: useAssistantOptions().credentials?.(), signal: abortController.signal, headers: { "Content-Type": "application/json", ...useAssistantOptions().headers?.() }, body: JSON.stringify({ ...useAssistantOptions().body?.(), // always use user-provided threadId when available: threadId: useAssistantOptions().threadId?.() ?? currentThreadId(), message: message.content, // optional request data: data: requestOptions?.data }) }); if (!response.ok) { throw new Error( await response.text() ?? "Failed to fetch the assistant response." ); } if (response.body == null) { throw new Error("The response body is empty."); } await (0, import_ui_utils4.processAssistantStream)({ stream: response.body, onAssistantMessagePart(value) { setMessages((messages2) => [ ...messages2, { id: value.id, role: value.role, content: value.content[0].text.value, parts: [] } ]); }, onTextPart(value) { setMessages((messages2) => { const lastMessage = messages2[messages2.length - 1]; return [ ...messages2.slice(0, messages2.length - 1), { id: lastMessage.id, role: lastMessage.role, content: lastMessage.content + value, parts: lastMessage.parts } ]; }); }, onAssistantControlDataPart(value) { setCurrentThreadId(value.threadId); setMessages((messages2) => { const lastMessage = messages2[messages2.length - 1]; lastMessage.id = value.messageId; return [...messages2.slice(0, messages2.length - 1), lastMessage]; }); }, onDataMessagePart(value) { setMessages((messages2) => [ ...messages2, { id: value.id ?? (0, import_ui_utils4.generateId)(), role: "data", content: "", data: value.data, parts: [] } ]); }, onErrorPart(value) { setError(new Error(value)); } }); } catch (error2) { if ((0, import_provider_utils2.isAbortError)(error2) && abortController.signal.aborted) { abortControllerRef = null; return; } const onError = useAssistantOptions().onError?.(); if (onError && error2 instanceof Error) { onError(error2); } setError(error2); } finally { abortControllerRef = null; setStatus("awaiting_message"); } }; const submitMessage = async (event, requestOptions) => { event?.preventDefault?.(); if (input() === "") { return; } append({ role: "user", content: input(), parts: [] }, requestOptions); }; const setThreadId = (threadId) => { setCurrentThreadId(threadId); setMessages([]); }; return { append, messages, setMessages, threadId: currentThreadId, setThreadId, input, setInput, handleInputChange, submitMessage, status, error, stop }; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { experimental_useObject, useAssistant, useChat, useCompletion }); //# sourceMappingURL=index.js.map