技术手册 · SDK
React 集成
使用 CharxProvider、公开 Hooks 和 Thread,管理消息状态与订阅生命周期。
React 入口是 @ai_wanjia/sdk/react。以下示例按仓库 SDK 1.0.10 编译核对,适用于连接真实宿主的 React 页面。React starter 自己实现了 mock 选择与界面状态;其 mock 不会自动注入到 CharxProvider。
Provider 与最小聊天页
CharxProvider 负责初始化 SDK,握手完成后渲染子组件。fallback 显示等待状态,errorFallback 接收初始化错误。它没有 sdk 或 mock 注入参数。
以下代码可以作为 React 浏览器项目的 App.tsx。普通浏览器标签页没有宿主握手时会显示连接失败;先通过本地调试界面加载它。
import { useState } from 'react'
import { CharxProvider, Thread, useChat } from '@ai_wanjia/sdk/react'
function ChatPage() {
const chat = useChat()
const [text, setText] = useState('')
const [actionError, setActionError] = useState<string | null>(null)
const busy = chat.status === 'sending' || chat.status === 'streaming'
const run = (action: () => Promise<void>) => {
setActionError(null)
void action().catch((error: unknown) => {
setActionError(error instanceof Error ? error.message : String(error))
})
}
return (
<main>
<Thread.MessageList
messages={chat.messages}
isStreaming={chat.status === 'streaming'}
onLoadMore={chat.hasMore ? () => run(chat.loadMore) : undefined}
/>
<form onSubmit={(event) => {
event.preventDefault()
const message = text.trim()
if (!message || busy) return
chat.send(message)
setText('')
}}>
<input aria-label="消息" value={text} onChange={(event) => setText(event.target.value)} />
<button type="submit" disabled={busy || !text.trim()}>发送</button>
<button type="button" disabled={!busy} onClick={() => run(chat.stop)}>停止</button>
</form>
{chat.error && <p role="alert">{chat.error.message}</p>}
{actionError && <p role="alert">{actionError}</p>}
</main>
)
}
export default function App() {
return (
<CharxProvider
fallback={<p>正在连接平台…</p>}
errorFallback={(error) => <p role="alert">连接失败:{error.message}</p>}
>
<ChatPage />
</CharxProvider>
)
}Hooks
| Hook | 返回值与边界 |
|---|---|
useCharx() | 当前 SDK 实例,必须位于 Provider 内 |
useChat() | 消息、请求状态、错误、发送与消息操作、历史分页 |
useVariable(key, defaultValue) | [value, set];首次读取并订阅变量变化,set 返回 Promise |
useVariables() | 完整变量表,首次读取并订阅变化 |
useCharacter() | 角色信息,加载前为 null |
usePersona() | 人设或 null;null 也可能表示尚未加载 |
useStorage(key, defaultValue) | [value, set, remove];读取并维护本组件状态,不提供跨组件 storage 订阅 |
useModels() | 模型列表、当前模型、加载/选择状态、错误、refresh() 和 select() |
变量和 storage 的默认值用于显示缺值状态,不会自动持久化。useCharacter()、usePersona() 和变量、storage Hooks 没有独立的错误返回字段;若界面需要准确区分读取失败与空值,可使用 useCharx() 调用对应模块并自行处理错误。
Thread 与消息状态
Thread 只包含 MessageList 和 MessageItem,不包含输入框或自动发送逻辑。MessageList 接收 messages、isStreaming、onLoadMore 和可选 renderMessage(message, isStreaming)。默认内容按文本展示;自定义展示可使用公开的 MdRenderer。
MdRenderer 是轻量渲染器:先转义 HTML,再处理粗体、斜体、行内代码和换行。它不提供完整 Markdown、原始 HTML 执行或任意组件解析。
useChat().status 是当前 Hook 的请求状态:idle、sending、streaming、error。它与单条消息可选的 generationStatus 不同。不要因为 Hook 回到 idle,就假定每条历史消息都已完成。
Hook 会订阅宿主消息新增、更新、编辑、删除和会话切换,刷新历史,并处理本地占位消息与宿主消息 ID 的合并。分页使用 loadMore() 和 hasMore。currentRequestId 和 currentAssistantMessageId 可用于关联当前生成,但可能为 null。
删除操作会回读宿主历史;识别到用户取消时不会把消息当作已删除。回溯成功后会刷新消息状态。swipeMessage() 仍是兼容占位,当前宿主可能拒绝,不能作为已支持的切换功能。
清理订阅
SDK 自带 Hooks 会清理自己创建的订阅。自行调用 onMessageUpdated()、onChatChanged()、variable.watch() 或 model.onChanged() 时,需要在 Effect 清理函数中调用取消订阅函数。
import { useEffect } from 'react'
import { useCharx } from '@ai_wanjia/sdk/react'
export function MessageObserver() {
const sdk = useCharx()
useEffect(() => {
const unsubscribe = sdk.chat.onMessageUpdated((message) => {
console.log('宿主消息已更新', message.id)
})
return unsubscribe
}, [sdk])
return null
}Provider 使用共享单例,当前实现不会在卸载时自动调用 destroy()。不要在任意子组件清理时销毁共享实例,否则会影响其他使用者。只有页面级所有者确认不再有调用方时,才调用 sdk.destroy() 释放底层监听并终止等待中的 RPC;销毁后需通过 getInstance() 获取新实例再初始化。