S
SolidJS2mo ago
kai

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
kai
kaiOP2mo ago
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))

Did you find this page helpful?