ai-sdk for solid (createMutable)
Is this implementation correct, following the svelte and vue implementations?
replaceMessage
doesn't seem to work as no signals on chat.messages
are ever triggered on updates.
import {
createMutable,
modifyMutable,
produce,
reconcile,
unwrap,
} from "solid-js/store"
import {
AbstractChat,
type ChatInit,
type ChatState,
type ChatStatus,
type UIMessage,
} from "ai"
class SolidChatState<UI_MESSAGE extends UIMessage>
implements ChatState<UI_MESSAGE>
{
private readonly _data: {
messages: UI_MESSAGE[]
status: ChatStatus
error: Error | undefined
}
constructor(messages: UI_MESSAGE[] = []) {
this._data = createMutable({
messages,
status: "ready",
error: undefined,
})
}
get messages(): UI_MESSAGE[] {
return this._data.messages
}
set messages(messages: UI_MESSAGE[]) {
this._data.messages = messages
}
get status(): ChatStatus {
return this._data.status
}
set status(status: ChatStatus) {
this._data.status = status
}
get error(): Error | undefined {
return this._data.error
}
set error(error: Error | undefined) {
this._data.error = error
}
pushMessage = (m: UI_MESSAGE) => {
this._data.messages.push(createMutable(m))
}
popMessage = () => {
this._data.messages.pop()
}
replaceMessage = (i: number, m: UI_MESSAGE) => {
modifyMutable(this._data.messages[i], reconcile(m, { merge: true }))
}
snapshot = <T>(value: T): T => unwrap(value)
}
export class Chat<
UI_MESSAGE extends UIMessage = UIMessage,
> extends AbstractChat<UI_MESSAGE> {
constructor(init: ChatInit<UI_MESSAGE>) {
super({
...init,
state: new SolidChatState(init.messages ?? []),
})
}
}
import {
createMutable,
modifyMutable,
produce,
reconcile,
unwrap,
} from "solid-js/store"
import {
AbstractChat,
type ChatInit,
type ChatState,
type ChatStatus,
type UIMessage,
} from "ai"
class SolidChatState<UI_MESSAGE extends UIMessage>
implements ChatState<UI_MESSAGE>
{
private readonly _data: {
messages: UI_MESSAGE[]
status: ChatStatus
error: Error | undefined
}
constructor(messages: UI_MESSAGE[] = []) {
this._data = createMutable({
messages,
status: "ready",
error: undefined,
})
}
get messages(): UI_MESSAGE[] {
return this._data.messages
}
set messages(messages: UI_MESSAGE[]) {
this._data.messages = messages
}
get status(): ChatStatus {
return this._data.status
}
set status(status: ChatStatus) {
this._data.status = status
}
get error(): Error | undefined {
return this._data.error
}
set error(error: Error | undefined) {
this._data.error = error
}
pushMessage = (m: UI_MESSAGE) => {
this._data.messages.push(createMutable(m))
}
popMessage = () => {
this._data.messages.pop()
}
replaceMessage = (i: number, m: UI_MESSAGE) => {
modifyMutable(this._data.messages[i], reconcile(m, { merge: true }))
}
snapshot = <T>(value: T): T => unwrap(value)
}
export class Chat<
UI_MESSAGE extends UIMessage = UIMessage,
> extends AbstractChat<UI_MESSAGE> {
constructor(init: ChatInit<UI_MESSAGE>) {
super({
...init,
state: new SolidChatState(init.messages ?? []),
})
}
}
1 Reply
For anyone looking for a working implementation:
import {
createStore,
reconcile,
unwrap,
type SetStoreFunction,
type Store,
} from "solid-js/store"
import {
AbstractChat,
type ChatInit,
type ChatState,
type ChatStatus,
type UIMessage,
} from "ai"
interface SolidChatStateData<UI_MESSAGE extends UIMessage> {
messages: UI_MESSAGE[]
status: ChatStatus
error: Error | undefined
}
class SolidChatState<UI_MESSAGE extends UIMessage>
implements ChatState<UI_MESSAGE>
{
private readonly _state: Store<SolidChatStateData<UI_MESSAGE>>
private readonly _setData: SetStoreFunction<SolidChatStateData<UI_MESSAGE>>
constructor(messages: UI_MESSAGE[] = []) {
;[this._state, this._setData] = createStore<SolidChatStateData<UI_MESSAGE>>(
{
messages,
status: "ready",
error: undefined,
},
)
}
get messages(): UI_MESSAGE[] {
return this._state.messages
}
set messages(messages: UI_MESSAGE[]) {
this._setData("messages", messages)
}
get status(): ChatStatus {
return this._state.status
}
set status(status: ChatStatus) {
this._setData("status", status)
}
get error(): Error | undefined {
return this._state.error
}
set error(error: Error | undefined) {
this._setData("error", error)
}
pushMessage = (m: UI_MESSAGE) => {
this._setData("messages", this._state.messages.length, clone(m))
}
popMessage = () => {
this._setData("messages", (prev: UI_MESSAGE[]) => prev.slice(0, -1))
}
replaceMessage = (i: number, m: UI_MESSAGE) => {
this._setData("messages", i, reconcile(m, { merge: true }))
}
snapshot = <T>(value: T): T => clone(value)
}
export class Chat<
UI_MESSAGE extends UIMessage = UIMessage,
> extends AbstractChat<UI_MESSAGE> {
constructor(init: ChatInit<UI_MESSAGE>) {
super({
...init,
state: new SolidChatState(init.messages),
})
}
}
const clone = <T>(value: T): T => structuredClone(unwrap(value))
import {
createStore,
reconcile,
unwrap,
type SetStoreFunction,
type Store,
} from "solid-js/store"
import {
AbstractChat,
type ChatInit,
type ChatState,
type ChatStatus,
type UIMessage,
} from "ai"
interface SolidChatStateData<UI_MESSAGE extends UIMessage> {
messages: UI_MESSAGE[]
status: ChatStatus
error: Error | undefined
}
class SolidChatState<UI_MESSAGE extends UIMessage>
implements ChatState<UI_MESSAGE>
{
private readonly _state: Store<SolidChatStateData<UI_MESSAGE>>
private readonly _setData: SetStoreFunction<SolidChatStateData<UI_MESSAGE>>
constructor(messages: UI_MESSAGE[] = []) {
;[this._state, this._setData] = createStore<SolidChatStateData<UI_MESSAGE>>(
{
messages,
status: "ready",
error: undefined,
},
)
}
get messages(): UI_MESSAGE[] {
return this._state.messages
}
set messages(messages: UI_MESSAGE[]) {
this._setData("messages", messages)
}
get status(): ChatStatus {
return this._state.status
}
set status(status: ChatStatus) {
this._setData("status", status)
}
get error(): Error | undefined {
return this._state.error
}
set error(error: Error | undefined) {
this._setData("error", error)
}
pushMessage = (m: UI_MESSAGE) => {
this._setData("messages", this._state.messages.length, clone(m))
}
popMessage = () => {
this._setData("messages", (prev: UI_MESSAGE[]) => prev.slice(0, -1))
}
replaceMessage = (i: number, m: UI_MESSAGE) => {
this._setData("messages", i, reconcile(m, { merge: true }))
}
snapshot = <T>(value: T): T => clone(value)
}
export class Chat<
UI_MESSAGE extends UIMessage = UIMessage,
> extends AbstractChat<UI_MESSAGE> {
constructor(init: ChatInit<UI_MESSAGE>) {
super({
...init,
state: new SolidChatState(init.messages),
})
}
}
const clone = <T>(value: T): T => structuredClone(unwrap(value))