@ai-sdk/solid
Advanced tools
Comparing version 0.0.7 to 0.0.8
module.exports = { | ||
root: true, | ||
extends: ['vercel-ai'], | ||
rules: { | ||
'react-hooks/rules-of-hooks': 'off', | ||
}, | ||
}; |
# @ai-sdk/solid | ||
## 0.0.8 | ||
### Patch Changes | ||
- c908f741: chore (ui/solid): update solidjs useChat and useCompletion to feature parity with React | ||
## 0.0.7 | ||
@@ -4,0 +10,0 @@ |
@@ -1,8 +0,8 @@ | ||
import { Message, CreateMessage, ChatRequestOptions, JSONValue, UseChatOptions, RequestOptions, UseCompletionOptions } from '@ai-sdk/ui-utils'; | ||
export { CreateMessage, Message, UseChatOptions, UseCompletionOptions } from '@ai-sdk/ui-utils'; | ||
import { Resource, Accessor, Setter } from 'solid-js'; | ||
import { Message, CreateMessage, ChatRequestOptions, JSONValue, UseChatOptions as UseChatOptions$1, RequestOptions, UseCompletionOptions } from '@ai-sdk/ui-utils'; | ||
export { CreateMessage, Message, UseCompletionOptions } from '@ai-sdk/ui-utils'; | ||
import { Accessor, Setter, JSX } from 'solid-js'; | ||
type UseChatHelpers = { | ||
/** Current messages in the chat */ | ||
messages: Resource<Message[]>; | ||
messages: Accessor<Message[]>; | ||
/** The error object of the API request */ | ||
@@ -37,2 +37,4 @@ error: Accessor<undefined | Error>; | ||
setInput: Setter<string>; | ||
/** An input/textarea-ready onChange handler to control the value of the input */ | ||
handleInputChange: JSX.ChangeEventHandlerUnion<HTMLInputElement | HTMLTextAreaElement, Event>; | ||
/** Form submission handler to automatically reset input and append a user message */ | ||
@@ -47,7 +49,27 @@ handleSubmit: (event?: { | ||
}; | ||
declare function useChat({ api, id, initialMessages, initialInput, sendExtraMessageFields, experimental_onFunctionCall, onResponse, onFinish, onError, credentials, headers, body, streamMode, generateId, }?: UseChatOptions): UseChatHelpers; | ||
type UseChatOptions = UseChatOptions$1 & { | ||
/** | ||
Maximal number of automatic roundtrips for tool calls. | ||
An automatic tool call roundtrip is a call to the server with the | ||
tool call results when all tool calls in the last assistant | ||
message have results. | ||
A maximum number is required to prevent infinite loops in the | ||
case of misconfigured tools. | ||
By default, it's set to 0, which will disable the feature. | ||
*/ | ||
maxToolRoundtrips?: number; | ||
}; | ||
declare function useChat(rawUseChatOptions?: UseChatOptions | Accessor<UseChatOptions>): UseChatHelpers & { | ||
addToolResult: ({ toolCallId, result, }: { | ||
toolCallId: string; | ||
result: any; | ||
}) => void; | ||
}; | ||
type UseCompletionHelpers = { | ||
/** The current completion result */ | ||
completion: Resource<string>; | ||
completion: Accessor<string>; | ||
/** The error object of the API request */ | ||
@@ -71,2 +93,4 @@ error: Accessor<undefined | Error>; | ||
setInput: Setter<string>; | ||
/** An input/textarea-ready onChange handler to control the value of the input */ | ||
handleInputChange: JSX.ChangeEventHandlerUnion<HTMLInputElement | HTMLTextAreaElement, Event>; | ||
/** | ||
@@ -89,4 +113,4 @@ * Form submission handler to automatically reset input and append a user message | ||
}; | ||
declare function useCompletion({ api, id, initialCompletion, initialInput, credentials, headers, body, streamMode, onResponse, onFinish, onError, }?: UseCompletionOptions): UseCompletionHelpers; | ||
declare function useCompletion(rawUseCompletionOptions?: UseCompletionOptions | Accessor<UseCompletionOptions>): UseCompletionHelpers; | ||
export { UseChatHelpers, UseCompletionHelpers, useChat, useCompletion }; | ||
export { UseChatHelpers, UseChatOptions, UseCompletionHelpers, useChat, useCompletion }; |
@@ -31,39 +31,76 @@ "use strict"; | ||
var import_solid_js = require("solid-js"); | ||
var import_solid_swr_store = require("solid-swr-store"); | ||
var import_swr_store = require("swr-store"); | ||
var uniqueId = 0; | ||
var store = {}; | ||
var chatApiStore = (0, import_swr_store.createSWRStore)({ | ||
get: async (key) => { | ||
var _a; | ||
return (_a = store[key]) != null ? _a : []; | ||
} | ||
}); | ||
function useChat({ | ||
api = "/api/chat", | ||
id, | ||
initialMessages = [], | ||
initialInput = "", | ||
sendExtraMessageFields, | ||
experimental_onFunctionCall, | ||
onResponse, | ||
onFinish, | ||
onError, | ||
credentials, | ||
headers, | ||
body, | ||
streamMode, | ||
generateId = import_ui_utils.generateId | ||
} = {}) { | ||
const chatId = id || `chat-${uniqueId++}`; | ||
const key = `${api}|${chatId}`; | ||
const messages = (0, import_solid_swr_store.useSWRStore)(chatApiStore, () => [key], { | ||
initialData: initialMessages | ||
var import_store = require("solid-js/store"); | ||
var getStreamedResponse = async (api, chatRequest, mutate, setStreamData, streamData, extraMetadata, messagesRef, abortController, generateId, streamMode, onFinish, onResponse, onToolCall, sendExtraMessageFields) => { | ||
var _a, _b, _c; | ||
const previousMessages = messagesRef; | ||
mutate(chatRequest.messages); | ||
const existingStreamData = (_a = streamData()) != null ? _a : []; | ||
const constructedMessagesPayload = sendExtraMessageFields ? chatRequest.messages : chatRequest.messages.map( | ||
({ role, content, name, data, annotations, toolInvocations }) => ({ | ||
role, | ||
content, | ||
...name !== void 0 && { name }, | ||
...data !== void 0 && { data }, | ||
...annotations !== void 0 && { annotations }, | ||
...toolInvocations !== void 0 && { toolInvocations } | ||
}) | ||
); | ||
return await (0, import_ui_utils.callChatApi)({ | ||
api, | ||
messages: constructedMessagesPayload, | ||
body: { | ||
messages: constructedMessagesPayload, | ||
data: chatRequest.data, | ||
...extraMetadata.body, | ||
...(_b = chatRequest.options) == null ? void 0 : _b.body | ||
}, | ||
streamMode, | ||
credentials: extraMetadata.credentials, | ||
headers: { | ||
...extraMetadata.headers, | ||
...(_c = chatRequest.options) == null ? void 0 : _c.headers | ||
}, | ||
abortController: () => abortController, | ||
restoreMessagesOnFailure() { | ||
mutate(previousMessages); | ||
}, | ||
onResponse, | ||
onUpdate(merged, data) { | ||
mutate([...chatRequest.messages, ...merged]); | ||
setStreamData([...existingStreamData, ...data != null ? data : []]); | ||
}, | ||
onToolCall, | ||
onFinish, | ||
generateId | ||
}); | ||
}; | ||
var [store, setStore] = (0, import_store.createStore)({}); | ||
function useChat(rawUseChatOptions = {}) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h; | ||
const useChatOptions = (0, import_solid_js.createMemo)( | ||
() => convertToAccessorOptions(rawUseChatOptions) | ||
); | ||
const api = (0, import_solid_js.createMemo)(() => { | ||
var _a2, _b2, _c2; | ||
return (_c2 = (_b2 = (_a2 = useChatOptions()).api) == null ? void 0 : _b2.call(_a2)) != null ? _c2 : "/api/chat"; | ||
}); | ||
const generateId = (0, import_solid_js.createMemo)( | ||
() => { | ||
var _a2, _b2, _c2; | ||
return (_c2 = (_b2 = (_a2 = useChatOptions()).generateId) == null ? void 0 : _b2.call(_a2)) != null ? _c2 : import_ui_utils.generateId; | ||
} | ||
); | ||
const idKey = (0, import_solid_js.createMemo)( | ||
() => { | ||
var _a2, _b2, _c2; | ||
return (_c2 = (_b2 = (_a2 = useChatOptions()).id) == null ? void 0 : _b2.call(_a2)) != null ? _c2 : `chat-${(0, import_solid_js.createUniqueId)()}`; | ||
} | ||
); | ||
const chatKey = (0, import_solid_js.createMemo)(() => `${api()}|${idKey()}|messages`); | ||
const messages = (0, import_solid_js.createMemo)(() => { | ||
var _a2, _b2, _c2, _d2; | ||
return (_d2 = (_c2 = store[chatKey()]) != null ? _c2 : (_b2 = (_a2 = useChatOptions()).initialMessages) == null ? void 0 : _b2.call(_a2)) != null ? _d2 : []; | ||
}); | ||
const mutate = (data) => { | ||
store[key] = data; | ||
return chatApiStore.mutate([key], { | ||
status: "success", | ||
data | ||
}); | ||
setStore(chatKey(), data); | ||
}; | ||
@@ -75,4 +112,23 @@ const [error, setError] = (0, import_solid_js.createSignal)(void 0); | ||
const [isLoading, setIsLoading] = (0, import_solid_js.createSignal)(false); | ||
let messagesRef = messages() || []; | ||
(0, import_solid_js.createEffect)(() => { | ||
messagesRef = messages() || []; | ||
}); | ||
let abortController = null; | ||
async function triggerRequest(messagesSnapshot, { options, data } = {}) { | ||
let extraMetadata = { | ||
credentials: (_b = (_a = useChatOptions()).credentials) == null ? void 0 : _b.call(_a), | ||
headers: (_d = (_c = useChatOptions()).headers) == null ? void 0 : _d.call(_c), | ||
body: (_f = (_e = useChatOptions()).body) == null ? void 0 : _f.call(_e) | ||
}; | ||
(0, import_solid_js.createEffect)(() => { | ||
var _a2, _b2, _c2, _d2, _e2, _f2; | ||
extraMetadata = { | ||
credentials: (_b2 = (_a2 = useChatOptions()).credentials) == null ? void 0 : _b2.call(_a2), | ||
headers: (_d2 = (_c2 = useChatOptions()).headers) == null ? void 0 : _d2.call(_c2), | ||
body: (_f2 = (_e2 = useChatOptions()).body) == null ? void 0 : _f2.call(_e2) | ||
}; | ||
}); | ||
const triggerRequest = async (chatRequest) => { | ||
var _a2, _b2, _c2, _d2, _e2, _f2, _g2; | ||
const messageCount = messagesRef.length; | ||
try { | ||
@@ -82,69 +138,27 @@ setError(void 0); | ||
abortController = new AbortController(); | ||
const getCurrentMessages = () => chatApiStore.get([key], { | ||
shouldRevalidate: false | ||
}); | ||
const previousMessages = getCurrentMessages(); | ||
mutate(messagesSnapshot); | ||
let chatRequest = { | ||
messages: messagesSnapshot, | ||
options, | ||
data | ||
}; | ||
await (0, import_ui_utils.processChatStream)({ | ||
getStreamedResponse: async () => { | ||
var _a; | ||
const existingData = (_a = streamData()) != null ? _a : []; | ||
const constructedMessagesPayload = sendExtraMessageFields ? chatRequest.messages : chatRequest.messages.map( | ||
({ | ||
role, | ||
content, | ||
name, | ||
data: data2, | ||
annotations, | ||
function_call | ||
}) => ({ | ||
role, | ||
content, | ||
...name !== void 0 && { name }, | ||
...data2 !== void 0 && { data: data2 }, | ||
...annotations !== void 0 && { annotations }, | ||
// outdated function/tool call handling (TODO deprecate): | ||
...function_call !== void 0 && { function_call } | ||
}) | ||
getStreamedResponse: () => { | ||
var _a3, _b3, _c3, _d3, _e3, _f3, _g3, _h2, _i, _j; | ||
return getStreamedResponse( | ||
api(), | ||
chatRequest, | ||
mutate, | ||
setStreamData, | ||
streamData, | ||
extraMetadata, | ||
messagesRef, | ||
abortController, | ||
generateId(), | ||
(_b3 = (_a3 = useChatOptions()).streamMode) == null ? void 0 : _b3.call(_a3), | ||
(_d3 = (_c3 = useChatOptions()).onFinish) == null ? void 0 : _d3.call(_c3), | ||
(_f3 = (_e3 = useChatOptions()).onResponse) == null ? void 0 : _f3.call(_e3), | ||
(_h2 = (_g3 = useChatOptions()).onToolCall) == null ? void 0 : _h2.call(_g3), | ||
(_j = (_i = useChatOptions()).sendExtraMessageFields) == null ? void 0 : _j.call(_i) | ||
); | ||
return await (0, import_ui_utils.callChatApi)({ | ||
api, | ||
messages: constructedMessagesPayload, | ||
body: { | ||
messages: constructedMessagesPayload, | ||
data: chatRequest.data, | ||
...body, | ||
...options == null ? void 0 : options.body | ||
}, | ||
streamMode, | ||
headers: { | ||
...headers, | ||
...options == null ? void 0 : options.headers | ||
}, | ||
abortController: () => abortController, | ||
credentials, | ||
onResponse, | ||
onUpdate(merged, data2) { | ||
mutate([...chatRequest.messages, ...merged]); | ||
setStreamData([...existingData, ...data2 != null ? data2 : []]); | ||
}, | ||
onFinish, | ||
restoreMessagesOnFailure() { | ||
if (previousMessages.status === "success") { | ||
mutate(previousMessages.data); | ||
} | ||
}, | ||
generateId | ||
}); | ||
}, | ||
experimental_onFunctionCall, | ||
experimental_onFunctionCall: (_b2 = (_a2 = useChatOptions()).experimental_onFunctionCall) == null ? void 0 : _b2.call(_a2), | ||
updateChatRequest(newChatRequest) { | ||
chatRequest = newChatRequest; | ||
}, | ||
getCurrentMessages: () => getCurrentMessages().data | ||
getCurrentMessages: () => messagesRef | ||
}); | ||
@@ -157,2 +171,3 @@ abortController = null; | ||
} | ||
const onError = (_d2 = (_c2 = useChatOptions()).onError) == null ? void 0 : _d2.call(_c2); | ||
if (onError && err instanceof Error) { | ||
@@ -165,22 +180,43 @@ onError(err); | ||
} | ||
} | ||
const append = async (message, options) => { | ||
var _a; | ||
const maxToolRoundtrips = (_g2 = (_f2 = (_e2 = useChatOptions()).maxToolRoundtrips) == null ? void 0 : _f2.call(_e2)) != null ? _g2 : 0; | ||
const messages2 = messagesRef; | ||
const lastMessage = messages2[messages2.length - 1]; | ||
if ( | ||
// ensure we actually have new messages (to prevent infinite loops in case of errors): | ||
messages2.length > messageCount && // ensure there is a last message: | ||
lastMessage != null && // check if the feature is enabled: | ||
maxToolRoundtrips > 0 && // check that roundtrip is possible: | ||
isAssistantMessageWithCompletedToolCalls(lastMessage) && // limit the number of automatic roundtrips: | ||
countTrailingAssistantMessages(messages2) <= maxToolRoundtrips | ||
) { | ||
await triggerRequest({ messages: messages2 }); | ||
} | ||
}; | ||
const append = async (message, { options, data } = {}) => { | ||
if (!message.id) { | ||
message.id = generateId(); | ||
message.id = generateId()(); | ||
} | ||
return triggerRequest( | ||
((_a = messages()) != null ? _a : []).concat(message), | ||
options | ||
); | ||
const chatRequest = { | ||
messages: messagesRef.concat(message), | ||
options, | ||
data | ||
}; | ||
return triggerRequest(chatRequest); | ||
}; | ||
const reload = async (options) => { | ||
const messagesSnapshot = messages(); | ||
if (!messagesSnapshot || messagesSnapshot.length === 0) | ||
const reload = async ({ options } = {}) => { | ||
if (messagesRef.length === 0) | ||
return null; | ||
const lastMessage = messagesSnapshot[messagesSnapshot.length - 1]; | ||
const lastMessage = messagesRef[messagesRef.length - 1]; | ||
if (lastMessage.role === "assistant") { | ||
return triggerRequest(messagesSnapshot.slice(0, -1), options); | ||
const chatRequest2 = { | ||
messages: messagesRef.slice(0, -1), | ||
options | ||
}; | ||
return triggerRequest(chatRequest2); | ||
} | ||
return triggerRequest(messagesSnapshot, options); | ||
const chatRequest = { | ||
messages: messagesRef, | ||
options | ||
}; | ||
return triggerRequest(chatRequest); | ||
}; | ||
@@ -195,7 +231,16 @@ const stop = () => { | ||
mutate(messages2); | ||
messagesRef = messages2; | ||
}; | ||
const [input, setInput] = (0, import_solid_js.createSignal)(initialInput); | ||
const handleSubmit = (event, options = {}) => { | ||
var _a; | ||
(_a = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a.call(event); | ||
const [input, setInput] = (0, import_solid_js.createSignal)( | ||
((_h = (_g = useChatOptions()).initialInput) == null ? void 0 : _h.call(_g)) || "" | ||
); | ||
const handleSubmit = (event, options = {}, metadata) => { | ||
var _a2; | ||
if (metadata) { | ||
extraMetadata = { | ||
...extraMetadata, | ||
...metadata | ||
}; | ||
} | ||
(_a2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a2.call(event); | ||
const inputValue = input(); | ||
@@ -214,2 +259,28 @@ if (!inputValue) | ||
}; | ||
const handleInputChange = (e) => { | ||
setInput(e.target.value); | ||
}; | ||
const addToolResult = ({ | ||
toolCallId, | ||
result | ||
}) => { | ||
var _a2; | ||
const messagesSnapshot = (_a2 = messages()) != null ? _a2 : []; | ||
const updatedMessages = messagesSnapshot.map( | ||
(message, index, arr) => ( | ||
// update the tool calls in the last assistant message: | ||
index === arr.length - 1 && message.role === "assistant" && message.toolInvocations ? { | ||
...message, | ||
toolInvocations: message.toolInvocations.map( | ||
(toolInvocation) => toolInvocation.toolCallId === toolCallId ? { ...toolInvocation, result } : toolInvocation | ||
) | ||
} : message | ||
) | ||
); | ||
mutate(updatedMessages); | ||
const lastMessage = updatedMessages[updatedMessages.length - 1]; | ||
if (isAssistantMessageWithCompletedToolCalls(lastMessage)) { | ||
triggerRequest({ messages: updatedMessages }); | ||
} | ||
}; | ||
return { | ||
@@ -224,7 +295,35 @@ messages, | ||
setInput, | ||
handleInputChange, | ||
handleSubmit, | ||
isLoading, | ||
data: streamData | ||
data: streamData, | ||
addToolResult | ||
}; | ||
} | ||
function isAssistantMessageWithCompletedToolCalls(message) { | ||
return message.role === "assistant" && message.toolInvocations && message.toolInvocations.length > 0 && message.toolInvocations.every((toolInvocation) => "result" in toolInvocation); | ||
} | ||
function countTrailingAssistantMessages(messages) { | ||
let count = 0; | ||
for (let i = messages.length - 1; i >= 0; i--) { | ||
if (messages[i].role === "assistant") { | ||
count++; | ||
} else { | ||
break; | ||
} | ||
} | ||
return count; | ||
} | ||
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; | ||
}, | ||
{} | ||
); | ||
} | ||
@@ -234,38 +333,31 @@ // src/use-completion.ts | ||
var import_solid_js2 = require("solid-js"); | ||
var import_solid_swr_store2 = require("solid-swr-store"); | ||
var import_swr_store2 = require("swr-store"); | ||
var uniqueId2 = 0; | ||
var store2 = {}; | ||
var completionApiStore = (0, import_swr_store2.createSWRStore)({ | ||
get: async (key) => { | ||
var _a; | ||
return (_a = store2[key]) != null ? _a : []; | ||
} | ||
}); | ||
function useCompletion({ | ||
api = "/api/completion", | ||
id, | ||
initialCompletion = "", | ||
initialInput = "", | ||
credentials, | ||
headers, | ||
body, | ||
streamMode, | ||
onResponse, | ||
onFinish, | ||
onError | ||
} = {}) { | ||
const completionId = id || `completion-${uniqueId2++}`; | ||
const key = `${api}|${completionId}`; | ||
const data = (0, import_solid_swr_store2.useSWRStore)(completionApiStore, () => [key], { | ||
initialData: initialCompletion | ||
}); | ||
const mutate = (data2) => { | ||
store2[key] = data2; | ||
return completionApiStore.mutate([key], { | ||
data: data2, | ||
status: "success" | ||
}); | ||
var import_store2 = require("solid-js/store"); | ||
var [store2, setStore2] = (0, import_store2.createStore)({}); | ||
function useCompletion(rawUseCompletionOptions = {}) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h, _i; | ||
const useCompletionOptions = (0, import_solid_js2.createMemo)( | ||
() => convertToAccessorOptions2(rawUseCompletionOptions) | ||
); | ||
const api = (0, import_solid_js2.createMemo)( | ||
() => { | ||
var _a2, _b2, _c2; | ||
return (_c2 = (_b2 = (_a2 = useCompletionOptions()).api) == null ? void 0 : _b2.call(_a2)) != null ? _c2 : "/api/completion"; | ||
} | ||
); | ||
const idKey = (0, import_solid_js2.createMemo)( | ||
() => { | ||
var _a2, _b2, _c2; | ||
return (_c2 = (_b2 = (_a2 = useCompletionOptions()).id) == null ? void 0 : _b2.call(_a2)) != null ? _c2 : `completion-${(0, import_solid_js2.createUniqueId)()}`; | ||
} | ||
); | ||
const completionKey = (0, import_solid_js2.createMemo)(() => `${api()}|${idKey()}|completion`); | ||
const completion = (0, import_solid_js2.createMemo)( | ||
() => { | ||
var _a2, _b2, _c2; | ||
return (_c2 = store2[completionKey()]) != null ? _c2 : (_b2 = (_a2 = useCompletionOptions()).initialCompletion) == null ? void 0 : _b2.call(_a2); | ||
} | ||
); | ||
const mutate = (data) => { | ||
setStore2(completionKey(), data); | ||
}; | ||
const completion = data; | ||
const [error, setError] = (0, import_solid_js2.createSignal)(void 0); | ||
@@ -276,30 +368,38 @@ const [streamData, setStreamData] = (0, import_solid_js2.createSignal)( | ||
const [isLoading, setIsLoading] = (0, import_solid_js2.createSignal)(false); | ||
let abortController = null; | ||
const [abortController, setAbortController] = (0, import_solid_js2.createSignal)(null); | ||
let extraMetadata = { | ||
credentials: (_b = (_a = useCompletionOptions()).credentials) == null ? void 0 : _b.call(_a), | ||
headers: (_d = (_c = useCompletionOptions()).headers) == null ? void 0 : _d.call(_c), | ||
body: (_f = (_e = useCompletionOptions()).body) == null ? void 0 : _f.call(_e) | ||
}; | ||
(0, import_solid_js2.createEffect)(() => { | ||
var _a2, _b2, _c2, _d2, _e2, _f2; | ||
extraMetadata = { | ||
credentials: (_b2 = (_a2 = useCompletionOptions()).credentials) == null ? void 0 : _b2.call(_a2), | ||
headers: (_d2 = (_c2 = useCompletionOptions()).headers) == null ? void 0 : _d2.call(_c2), | ||
body: (_f2 = (_e2 = useCompletionOptions()).body) == null ? void 0 : _f2.call(_e2) | ||
}; | ||
}); | ||
const complete = async (prompt, options) => { | ||
var _a; | ||
const existingData = (_a = streamData()) != null ? _a : []; | ||
var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2, _i2, _j, _k; | ||
const existingData = (_a2 = streamData()) != null ? _a2 : []; | ||
return (0, import_ui_utils2.callCompletionApi)({ | ||
api, | ||
api: api(), | ||
prompt, | ||
credentials, | ||
headers: { | ||
...headers, | ||
...options == null ? void 0 : options.headers | ||
}, | ||
credentials: (_c2 = (_b2 = useCompletionOptions()).credentials) == null ? void 0 : _c2.call(_b2), | ||
headers: { ...extraMetadata.headers, ...options == null ? void 0 : options.headers }, | ||
body: { | ||
...body, | ||
...extraMetadata.body, | ||
...options == null ? void 0 : options.body | ||
}, | ||
streamMode, | ||
streamMode: (_e2 = (_d2 = useCompletionOptions()).streamMode) == null ? void 0 : _e2.call(_d2), | ||
setCompletion: mutate, | ||
setLoading: setIsLoading, | ||
setError, | ||
setAbortController: (controller) => { | ||
abortController = controller; | ||
}, | ||
onResponse, | ||
onFinish, | ||
onError, | ||
onData: (data2) => { | ||
setStreamData([...existingData, ...data2 != null ? data2 : []]); | ||
setAbortController, | ||
onResponse: (_g2 = (_f2 = useCompletionOptions()).onResponse) == null ? void 0 : _g2.call(_f2), | ||
onFinish: (_i2 = (_h2 = useCompletionOptions()).onFinish) == null ? void 0 : _i2.call(_h2), | ||
onError: (_k = (_j = useCompletionOptions()).onError) == null ? void 0 : _k.call(_j), | ||
onData: (data) => { | ||
setStreamData([...existingData, ...data != null ? data : []]); | ||
} | ||
@@ -309,5 +409,4 @@ }); | ||
const stop = () => { | ||
if (abortController) { | ||
abortController.abort(); | ||
abortController = null; | ||
if (abortController()) { | ||
abortController().abort(); | ||
} | ||
@@ -318,6 +417,11 @@ }; | ||
}; | ||
const [input, setInput] = (0, import_solid_js2.createSignal)(initialInput); | ||
const [input, setInput] = (0, import_solid_js2.createSignal)( | ||
(_i = (_h = (_g = useCompletionOptions()).initialInput) == null ? void 0 : _h.call(_g)) != null ? _i : "" | ||
); | ||
const handleInputChange = (event) => { | ||
setInput(event.target.value); | ||
}; | ||
const handleSubmit = (event) => { | ||
var _a; | ||
(_a = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a.call(event); | ||
var _a2; | ||
(_a2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _a2.call(event); | ||
const inputValue = input(); | ||
@@ -334,2 +438,3 @@ return inputValue ? complete(inputValue) : void 0; | ||
setInput, | ||
handleInputChange, | ||
handleSubmit, | ||
@@ -340,2 +445,14 @@ isLoading, | ||
} | ||
function convertToAccessorOptions2(options) { | ||
const resolvedOptions = typeof options === "function" ? options() : options; | ||
return Object.entries(resolvedOptions).reduce( | ||
(reactiveOptions, [key, value]) => { | ||
reactiveOptions[key] = (0, import_solid_js2.createMemo)( | ||
() => value | ||
); | ||
return reactiveOptions; | ||
}, | ||
{} | ||
); | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
@@ -342,0 +459,0 @@ 0 && (module.exports = { |
{ | ||
"name": "@ai-sdk/solid", | ||
"version": "0.0.7", | ||
"version": "0.0.8", | ||
"license": "Apache-2.0", | ||
@@ -18,5 +18,3 @@ "sideEffects": false, | ||
"dependencies": { | ||
"@ai-sdk/ui-utils": "0.0.6", | ||
"swr-store": "0.10.6", | ||
"solid-swr-store": "0.10.7" | ||
"@ai-sdk/ui-utils": "0.0.6" | ||
}, | ||
@@ -23,0 +21,0 @@ "devDependencies": { |
@@ -5,5 +5,6 @@ import type { | ||
CreateMessage, | ||
IdGenerator, | ||
JSONValue, | ||
Message, | ||
UseChatOptions, | ||
UseChatOptions as SharedUseChatOptions, | ||
} from '@ai-sdk/ui-utils'; | ||
@@ -15,11 +16,18 @@ import { | ||
} from '@ai-sdk/ui-utils'; | ||
import { Accessor, Resource, Setter, createSignal } from 'solid-js'; | ||
import { useSWRStore } from 'solid-swr-store'; | ||
import { createSWRStore } from 'swr-store'; | ||
import { | ||
Accessor, | ||
JSX, | ||
Setter, | ||
createEffect, | ||
createMemo, | ||
createSignal, | ||
createUniqueId, | ||
} from 'solid-js'; | ||
import { createStore } from 'solid-js/store'; | ||
export type { CreateMessage, Message, UseChatOptions }; | ||
export type { CreateMessage, Message }; | ||
export type UseChatHelpers = { | ||
/** Current messages in the chat */ | ||
messages: Resource<Message[]>; | ||
messages: Accessor<Message[]>; | ||
/** The error object of the API request */ | ||
@@ -59,2 +67,7 @@ error: Accessor<undefined | Error>; | ||
setInput: Setter<string>; | ||
/** An input/textarea-ready onChange handler to control the value of the input */ | ||
handleInputChange: JSX.ChangeEventHandlerUnion< | ||
HTMLInputElement | HTMLTextAreaElement, | ||
Event | ||
>; | ||
/** Form submission handler to automatically reset input and append a user message */ | ||
@@ -71,43 +84,118 @@ handleSubmit: ( | ||
let uniqueId = 0; | ||
const getStreamedResponse = async ( | ||
api: string, | ||
chatRequest: ChatRequest, | ||
mutate: (data: Message[]) => void, | ||
setStreamData: Setter<JSONValue[] | undefined>, | ||
streamData: Accessor<JSONValue[] | undefined>, | ||
extraMetadata: any, | ||
messagesRef: Message[], | ||
abortController: AbortController | null, | ||
generateId: IdGenerator, | ||
streamMode?: 'stream-data' | 'text', | ||
onFinish?: UseChatOptions['onFinish'], | ||
onResponse?: UseChatOptions['onResponse'], | ||
onToolCall?: UseChatOptions['onToolCall'], | ||
sendExtraMessageFields?: boolean, | ||
) => { | ||
// Do an optimistic update to the chat state to show the updated messages | ||
// immediately. | ||
const previousMessages = messagesRef; | ||
mutate(chatRequest.messages); | ||
const store: Record<string, Message[] | undefined> = {}; | ||
const chatApiStore = createSWRStore<Message[], string[]>({ | ||
get: async (key: string) => { | ||
return store[key] ?? []; | ||
}, | ||
}); | ||
const existingStreamData = streamData() ?? []; | ||
export function useChat({ | ||
api = '/api/chat', | ||
id, | ||
initialMessages = [], | ||
initialInput = '', | ||
sendExtraMessageFields, | ||
experimental_onFunctionCall, | ||
onResponse, | ||
onFinish, | ||
onError, | ||
credentials, | ||
headers, | ||
body, | ||
streamMode, | ||
generateId = generateIdFunc, | ||
}: UseChatOptions = {}): UseChatHelpers { | ||
// Generate a unique ID for the chat if not provided. | ||
const chatId = id || `chat-${uniqueId++}`; | ||
const constructedMessagesPayload = sendExtraMessageFields | ||
? chatRequest.messages | ||
: chatRequest.messages.map( | ||
({ role, content, name, data, annotations, toolInvocations }) => ({ | ||
role, | ||
content, | ||
...(name !== undefined && { name }), | ||
...(data !== undefined && { data }), | ||
...(annotations !== undefined && { annotations }), | ||
...(toolInvocations !== undefined && { toolInvocations }), | ||
}), | ||
); | ||
const key = `${api}|${chatId}`; | ||
return await callChatApi({ | ||
api, | ||
messages: constructedMessagesPayload, | ||
body: { | ||
messages: constructedMessagesPayload, | ||
data: chatRequest.data, | ||
...extraMetadata.body, | ||
...chatRequest.options?.body, | ||
}, | ||
streamMode, | ||
credentials: extraMetadata.credentials, | ||
headers: { | ||
...extraMetadata.headers, | ||
...chatRequest.options?.headers, | ||
}, | ||
abortController: () => abortController, | ||
restoreMessagesOnFailure() { | ||
mutate(previousMessages); | ||
}, | ||
onResponse, | ||
onUpdate(merged, data) { | ||
mutate([...chatRequest.messages, ...merged]); | ||
setStreamData([...existingStreamData, ...(data ?? [])]); | ||
}, | ||
onToolCall, | ||
onFinish, | ||
generateId, | ||
}); | ||
}; | ||
// Because of the `initialData` option, the `data` will never be `undefined`: | ||
const messages = useSWRStore(chatApiStore, () => [key], { | ||
initialData: initialMessages, | ||
}) as Resource<Message[]>; | ||
// This store saves the messages for each chat ID | ||
const [store, setStore] = createStore<Record<string, Message[]>>({}); | ||
export type UseChatOptions = SharedUseChatOptions & { | ||
/** | ||
Maximal number of automatic roundtrips for tool calls. | ||
An automatic tool call roundtrip is a call to the server with the | ||
tool call results when all tool calls in the last assistant | ||
message have results. | ||
A maximum number is required to prevent infinite loops in the | ||
case of misconfigured tools. | ||
By default, it's set to 0, which will disable the feature. | ||
*/ | ||
maxToolRoundtrips?: number; | ||
}; | ||
export function useChat( | ||
rawUseChatOptions: UseChatOptions | Accessor<UseChatOptions> = {}, | ||
): UseChatHelpers & { | ||
addToolResult: ({ | ||
toolCallId, | ||
result, | ||
}: { | ||
toolCallId: string; | ||
result: any; | ||
}) => void; | ||
} { | ||
const useChatOptions = createMemo(() => | ||
convertToAccessorOptions(rawUseChatOptions), | ||
); | ||
const api = createMemo(() => useChatOptions().api?.() ?? '/api/chat'); | ||
const generateId = createMemo( | ||
() => useChatOptions().generateId?.() ?? generateIdFunc, | ||
); | ||
const idKey = createMemo( | ||
() => useChatOptions().id?.() ?? `chat-${createUniqueId()}`, | ||
); | ||
const chatKey = createMemo(() => `${api()}|${idKey()}|messages`); | ||
const messages = createMemo(() => { | ||
return store[chatKey()] ?? useChatOptions().initialMessages?.() ?? []; | ||
}); | ||
const mutate = (data: Message[]) => { | ||
store[key] = data; | ||
return chatApiStore.mutate([key], { | ||
status: 'success', | ||
data, | ||
}); | ||
setStore(chatKey(), data); | ||
}; | ||
@@ -121,7 +209,25 @@ | ||
let messagesRef: Message[] = messages() || []; | ||
createEffect(() => { | ||
messagesRef = messages() || []; | ||
}); | ||
let abortController: AbortController | null = null; | ||
async function triggerRequest( | ||
messagesSnapshot: Message[], | ||
{ options, data }: ChatRequestOptions = {}, | ||
) { | ||
let extraMetadata = { | ||
credentials: useChatOptions().credentials?.(), | ||
headers: useChatOptions().headers?.(), | ||
body: useChatOptions().body?.(), | ||
}; | ||
createEffect(() => { | ||
extraMetadata = { | ||
credentials: useChatOptions().credentials?.(), | ||
headers: useChatOptions().headers?.(), | ||
body: useChatOptions().body?.(), | ||
}; | ||
}); | ||
const triggerRequest = async (chatRequest: ChatRequest) => { | ||
const messageCount = messagesRef.length; | ||
try { | ||
@@ -133,79 +239,26 @@ setError(undefined); | ||
const getCurrentMessages = () => | ||
chatApiStore.get([key], { | ||
shouldRevalidate: false, | ||
}); | ||
// Do an optimistic update to the chat state to show the updated messages | ||
// immediately. | ||
const previousMessages = getCurrentMessages(); | ||
mutate(messagesSnapshot); | ||
let chatRequest: ChatRequest = { | ||
messages: messagesSnapshot, | ||
options, | ||
data, | ||
}; | ||
await processChatStream({ | ||
getStreamedResponse: async () => { | ||
const existingData = streamData() ?? []; | ||
const constructedMessagesPayload = sendExtraMessageFields | ||
? chatRequest.messages | ||
: chatRequest.messages.map( | ||
({ | ||
role, | ||
content, | ||
name, | ||
data, | ||
annotations, | ||
function_call, | ||
}) => ({ | ||
role, | ||
content, | ||
...(name !== undefined && { name }), | ||
...(data !== undefined && { data }), | ||
...(annotations !== undefined && { annotations }), | ||
// outdated function/tool call handling (TODO deprecate): | ||
...(function_call !== undefined && { function_call }), | ||
}), | ||
); | ||
return await callChatApi({ | ||
api, | ||
messages: constructedMessagesPayload, | ||
body: { | ||
messages: constructedMessagesPayload, | ||
data: chatRequest.data, | ||
...body, | ||
...options?.body, | ||
}, | ||
streamMode, | ||
headers: { | ||
...headers, | ||
...options?.headers, | ||
}, | ||
abortController: () => abortController, | ||
credentials, | ||
onResponse, | ||
onUpdate(merged, data) { | ||
mutate([...chatRequest.messages, ...merged]); | ||
setStreamData([...existingData, ...(data ?? [])]); | ||
}, | ||
onFinish, | ||
restoreMessagesOnFailure() { | ||
// Restore the previous messages if the request fails. | ||
if (previousMessages.status === 'success') { | ||
mutate(previousMessages.data); | ||
} | ||
}, | ||
generateId, | ||
}); | ||
}, | ||
experimental_onFunctionCall, | ||
getStreamedResponse: () => | ||
getStreamedResponse( | ||
api(), | ||
chatRequest, | ||
mutate, | ||
setStreamData, | ||
streamData, | ||
extraMetadata, | ||
messagesRef, | ||
abortController, | ||
generateId(), | ||
useChatOptions().streamMode?.(), | ||
useChatOptions().onFinish?.(), | ||
useChatOptions().onResponse?.(), | ||
useChatOptions().onToolCall?.(), | ||
useChatOptions().sendExtraMessageFields?.(), | ||
), | ||
experimental_onFunctionCall: | ||
useChatOptions().experimental_onFunctionCall?.(), | ||
updateChatRequest(newChatRequest) { | ||
chatRequest = newChatRequest; | ||
}, | ||
getCurrentMessages: () => getCurrentMessages().data, | ||
getCurrentMessages: () => messagesRef, | ||
}); | ||
@@ -221,2 +274,3 @@ | ||
const onError = useChatOptions().onError?.(); | ||
if (onError && err instanceof Error) { | ||
@@ -230,23 +284,60 @@ onError(err); | ||
} | ||
} | ||
const append: UseChatHelpers['append'] = async (message, options) => { | ||
const maxToolRoundtrips = useChatOptions().maxToolRoundtrips?.() ?? 0; | ||
// auto-submit when all tool calls in the last assistant message have results: | ||
const messages = messagesRef; | ||
const lastMessage = messages[messages.length - 1]; | ||
if ( | ||
// ensure we actually have new messages (to prevent infinite loops in case of errors): | ||
messages.length > messageCount && | ||
// ensure there is a last message: | ||
lastMessage != null && | ||
// check if the feature is enabled: | ||
maxToolRoundtrips > 0 && | ||
// check that roundtrip is possible: | ||
isAssistantMessageWithCompletedToolCalls(lastMessage) && | ||
// limit the number of automatic roundtrips: | ||
countTrailingAssistantMessages(messages) <= maxToolRoundtrips | ||
) { | ||
await triggerRequest({ messages }); | ||
} | ||
}; | ||
const append: UseChatHelpers['append'] = async ( | ||
message, | ||
{ options, data } = {}, | ||
) => { | ||
if (!message.id) { | ||
message.id = generateId(); | ||
message.id = generateId()(); | ||
} | ||
return triggerRequest( | ||
(messages() ?? []).concat(message as Message), | ||
const chatRequest: ChatRequest = { | ||
messages: messagesRef.concat(message as Message), | ||
options, | ||
); | ||
data, | ||
}; | ||
return triggerRequest(chatRequest); | ||
}; | ||
const reload: UseChatHelpers['reload'] = async options => { | ||
const messagesSnapshot = messages(); | ||
if (!messagesSnapshot || messagesSnapshot.length === 0) return null; | ||
const reload: UseChatHelpers['reload'] = async ({ options } = {}) => { | ||
if (messagesRef.length === 0) return null; | ||
const lastMessage = messagesSnapshot[messagesSnapshot.length - 1]; | ||
// Remove last assistant message and retry last user message. | ||
const lastMessage = messagesRef[messagesRef.length - 1]; | ||
if (lastMessage.role === 'assistant') { | ||
return triggerRequest(messagesSnapshot.slice(0, -1), options); | ||
const chatRequest: ChatRequest = { | ||
messages: messagesRef.slice(0, -1), | ||
options, | ||
}; | ||
return triggerRequest(chatRequest); | ||
} | ||
return triggerRequest(messagesSnapshot, options); | ||
const chatRequest: ChatRequest = { | ||
messages: messagesRef, | ||
options, | ||
}; | ||
return triggerRequest(chatRequest); | ||
}; | ||
@@ -263,10 +354,21 @@ | ||
mutate(messages); | ||
messagesRef = messages; | ||
}; | ||
const [input, setInput] = createSignal(initialInput); | ||
const [input, setInput] = createSignal( | ||
useChatOptions().initialInput?.() || '', | ||
); | ||
const handleSubmit = ( | ||
event?: { preventDefault?: () => void }, | ||
options: ChatRequestOptions = {}, | ||
const handleSubmit: UseChatHelpers['handleSubmit'] = ( | ||
event, | ||
options = {}, | ||
metadata?: Object, | ||
) => { | ||
if (metadata) { | ||
extraMetadata = { | ||
...extraMetadata, | ||
...metadata, | ||
}; | ||
} | ||
event?.preventDefault?.(); | ||
@@ -284,6 +386,43 @@ const inputValue = input(); | ||
); | ||
setInput(''); | ||
}; | ||
const handleInputChange: UseChatHelpers['handleInputChange'] = e => { | ||
setInput(e.target.value); | ||
}; | ||
const addToolResult = ({ | ||
toolCallId, | ||
result, | ||
}: { | ||
toolCallId: string; | ||
result: any; | ||
}) => { | ||
const messagesSnapshot = messages() ?? []; | ||
const updatedMessages = messagesSnapshot.map((message, index, arr) => | ||
// update the tool calls in the last assistant message: | ||
index === arr.length - 1 && | ||
message.role === 'assistant' && | ||
message.toolInvocations | ||
? { | ||
...message, | ||
toolInvocations: message.toolInvocations.map(toolInvocation => | ||
toolInvocation.toolCallId === toolCallId | ||
? { ...toolInvocation, result } | ||
: toolInvocation, | ||
), | ||
} | ||
: message, | ||
); | ||
mutate(updatedMessages); | ||
// auto-submit when all tool calls in the last assistant message have results: | ||
const lastMessage = updatedMessages[updatedMessages.length - 1]; | ||
if (isAssistantMessageWithCompletedToolCalls(lastMessage)) { | ||
triggerRequest({ messages: updatedMessages }); | ||
} | ||
}; | ||
return { | ||
@@ -298,6 +437,58 @@ messages, | ||
setInput, | ||
handleInputChange, | ||
handleSubmit, | ||
isLoading, | ||
data: streamData, | ||
addToolResult, | ||
}; | ||
} | ||
/** | ||
Check if the message is an assistant message with completed tool calls. | ||
The message must have at least one tool invocation and all tool invocations | ||
must have a result. | ||
*/ | ||
function isAssistantMessageWithCompletedToolCalls(message: Message) { | ||
return ( | ||
message.role === 'assistant' && | ||
message.toolInvocations && | ||
message.toolInvocations.length > 0 && | ||
message.toolInvocations.every(toolInvocation => 'result' in toolInvocation) | ||
); | ||
} | ||
/** | ||
Returns the number of trailing assistant messages in the array. | ||
*/ | ||
function countTrailingAssistantMessages(messages: Message[]) { | ||
let count = 0; | ||
for (let i = messages.length - 1; i >= 0; i--) { | ||
if (messages[i].role === 'assistant') { | ||
count++; | ||
} else { | ||
break; | ||
} | ||
} | ||
return count; | ||
} | ||
/** | ||
* Handle reactive and non-reactive useChatOptions | ||
*/ | ||
function convertToAccessorOptions( | ||
options: UseChatOptions | Accessor<UseChatOptions>, | ||
) { | ||
const resolvedOptions = typeof options === 'function' ? options() : options; | ||
return Object.entries(resolvedOptions).reduce( | ||
(reactiveOptions, [key, value]) => { | ||
reactiveOptions[key as keyof UseChatOptions] = createMemo( | ||
() => value, | ||
) as any; | ||
return reactiveOptions; | ||
}, | ||
{} as { | ||
[K in keyof UseChatOptions]: Accessor<UseChatOptions[K]>; | ||
}, | ||
); | ||
} |
@@ -7,5 +7,12 @@ import type { | ||
import { callCompletionApi } from '@ai-sdk/ui-utils'; | ||
import { Accessor, Resource, Setter, createSignal } from 'solid-js'; | ||
import { useSWRStore } from 'solid-swr-store'; | ||
import { createSWRStore } from 'swr-store'; | ||
import { | ||
Accessor, | ||
JSX, | ||
Setter, | ||
createEffect, | ||
createMemo, | ||
createSignal, | ||
createUniqueId, | ||
} from 'solid-js'; | ||
import { createStore } from 'solid-js/store'; | ||
@@ -16,3 +23,3 @@ export type { UseCompletionOptions }; | ||
/** The current completion result */ | ||
completion: Resource<string>; | ||
completion: Accessor<string>; | ||
/** The error object of the API request */ | ||
@@ -39,2 +46,8 @@ error: Accessor<undefined | Error>; | ||
setInput: Setter<string>; | ||
/** An input/textarea-ready onChange handler to control the value of the input */ | ||
handleInputChange: JSX.ChangeEventHandlerUnion< | ||
HTMLInputElement | HTMLTextAreaElement, | ||
Event | ||
>; | ||
/** | ||
@@ -56,43 +69,31 @@ * Form submission handler to automatically reset input and append a user message | ||
let uniqueId = 0; | ||
const [store, setStore] = createStore<Record<string, string>>({}); | ||
const store: Record<string, any> = {}; | ||
const completionApiStore = createSWRStore<any, string[]>({ | ||
get: async (key: string) => { | ||
return store[key] ?? []; | ||
}, | ||
}); | ||
export function useCompletion( | ||
rawUseCompletionOptions: | ||
| UseCompletionOptions | ||
| Accessor<UseCompletionOptions> = {}, | ||
): UseCompletionHelpers { | ||
const useCompletionOptions = createMemo(() => | ||
convertToAccessorOptions(rawUseCompletionOptions), | ||
); | ||
export function useCompletion({ | ||
api = '/api/completion', | ||
id, | ||
initialCompletion = '', | ||
initialInput = '', | ||
credentials, | ||
headers, | ||
body, | ||
streamMode, | ||
onResponse, | ||
onFinish, | ||
onError, | ||
}: UseCompletionOptions = {}): UseCompletionHelpers { | ||
const api = createMemo( | ||
() => useCompletionOptions().api?.() ?? '/api/completion', | ||
); | ||
// Generate an unique id for the completion if not provided. | ||
const completionId = id || `completion-${uniqueId++}`; | ||
const idKey = createMemo( | ||
() => useCompletionOptions().id?.() ?? `completion-${createUniqueId()}`, | ||
); | ||
const completionKey = createMemo(() => `${api()}|${idKey()}|completion`); | ||
const key = `${api}|${completionId}`; | ||
const data = useSWRStore(completionApiStore, () => [key], { | ||
initialData: initialCompletion, | ||
}); | ||
const completion = createMemo( | ||
() => | ||
store[completionKey()] ?? useCompletionOptions().initialCompletion?.(), | ||
); | ||
const mutate = (data: string) => { | ||
store[key] = data; | ||
return completionApiStore.mutate([key], { | ||
data, | ||
status: 'success', | ||
}); | ||
setStore(completionKey(), data); | ||
}; | ||
// Because of the `initialData` option, the `data` will never be `undefined`. | ||
const completion = data as Resource<string>; | ||
const [error, setError] = createSignal<undefined | Error>(undefined); | ||
@@ -104,4 +105,18 @@ const [streamData, setStreamData] = createSignal<JSONValue[] | undefined>( | ||
let abortController: AbortController | null = null; | ||
const [abortController, setAbortController] = | ||
createSignal<AbortController | null>(null); | ||
let extraMetadata = { | ||
credentials: useCompletionOptions().credentials?.(), | ||
headers: useCompletionOptions().headers?.(), | ||
body: useCompletionOptions().body?.(), | ||
}; | ||
createEffect(() => { | ||
extraMetadata = { | ||
credentials: useCompletionOptions().credentials?.(), | ||
headers: useCompletionOptions().headers?.(), | ||
body: useCompletionOptions().body?.(), | ||
}; | ||
}); | ||
const complete: UseCompletionHelpers['complete'] = async ( | ||
@@ -113,23 +128,18 @@ prompt: string, | ||
return callCompletionApi({ | ||
api, | ||
api: api(), | ||
prompt, | ||
credentials, | ||
headers: { | ||
...headers, | ||
...options?.headers, | ||
}, | ||
credentials: useCompletionOptions().credentials?.(), | ||
headers: { ...extraMetadata.headers, ...options?.headers }, | ||
body: { | ||
...body, | ||
...extraMetadata.body, | ||
...options?.body, | ||
}, | ||
streamMode, | ||
streamMode: useCompletionOptions().streamMode?.(), | ||
setCompletion: mutate, | ||
setLoading: setIsLoading, | ||
setError, | ||
setAbortController: controller => { | ||
abortController = controller; | ||
}, | ||
onResponse, | ||
onFinish, | ||
onError, | ||
setAbortController, | ||
onResponse: useCompletionOptions().onResponse?.(), | ||
onFinish: useCompletionOptions().onFinish?.(), | ||
onError: useCompletionOptions().onError?.(), | ||
onData: data => { | ||
@@ -142,5 +152,4 @@ setStreamData([...existingData, ...(data ?? [])]); | ||
const stop = () => { | ||
if (abortController) { | ||
abortController.abort(); | ||
abortController = null; | ||
if (abortController()) { | ||
abortController()!.abort(); | ||
} | ||
@@ -153,5 +162,12 @@ }; | ||
const [input, setInput] = createSignal(initialInput); | ||
const [input, setInput] = createSignal( | ||
useCompletionOptions().initialInput?.() ?? '', | ||
); | ||
const handleSubmit = (event?: { preventDefault?: () => void }) => { | ||
const handleInputChange: UseCompletionHelpers['handleInputChange'] = | ||
event => { | ||
setInput(event.target.value); | ||
}; | ||
const handleSubmit: UseCompletionHelpers['handleSubmit'] = event => { | ||
event?.preventDefault?.(); | ||
@@ -171,2 +187,3 @@ | ||
setInput, | ||
handleInputChange, | ||
handleSubmit, | ||
@@ -177,1 +194,22 @@ isLoading, | ||
} | ||
/** | ||
* Handle reactive and non-reactive useChatOptions | ||
*/ | ||
function convertToAccessorOptions( | ||
options: UseCompletionOptions | Accessor<UseCompletionOptions>, | ||
) { | ||
const resolvedOptions = typeof options === 'function' ? options() : options; | ||
return Object.entries(resolvedOptions).reduce( | ||
(reactiveOptions, [key, value]) => { | ||
reactiveOptions[key as keyof UseCompletionOptions] = createMemo( | ||
() => value, | ||
) as any; | ||
return reactiveOptions; | ||
}, | ||
{} as { | ||
[K in keyof UseCompletionOptions]: Accessor<UseCompletionOptions[K]>; | ||
}, | ||
); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
139238
2
2120
- Removedsolid-swr-store@0.10.7
- Removedswr-store@0.10.6
- Removeddequal@2.0.3(transitive)
- Removedsolid-swr-store@0.10.7(transitive)
- Removedswr-store@0.10.6(transitive)