name: bodhi-sdk-react-integration description: > 将React+Vite Web应用程序与bodhi-js-sdk集成,实现本地LLM集成。当用户要求时使用: “集成bodhi”、“添加bodhi sdk”、“连接到bodhi”、“设置bodhi提供程序”、 “bodhi react集成”、“将bodhi部署到GitHub Pages”,或解决bodhi-js-sdk连接/身份验证问题。 allowed-tools: Read, Grep, Glob, Edit, Write, Bash(npm:), Bash(npx:)
Bodhi JS SDK React集成
将React+Vite应用程序与bodhi-js-sdk集成的指南,通过Bodhi Browser生态系统启用本地LLM聊天功能。
何时使用此技能
- 用户希望将React应用程序与bodhi-js-sdk集成
- 用户需要向React+Vite应用程序添加聊天/LLM功能
- 用户正在将bodhi集成应用程序部署到GitHub Pages
- 用户正在解决SDK连接、身份验证或流式传输问题
快速集成清单
- 安装:
npm install @bodhiapp/bodhi-js-react - 注册:在https://developer.getbodhi.app创建OAuth客户端
- 包装应用:在应用周围添加
<BodhiProvider authClientId={...}> - 使用Hook:访问
useBodhi()获取客户端、身份验证状态和操作 - 构建UI:创建支持流式传输的聊天界面
核心概念
包架构
@bodhiapp/bodhi-js-react- Web应用程序的预设包(自动创建WebUIClient)@bodhiapp/bodhi-js-react-ext- Chrome扩展程序的预设包(自动创建ExtUIClient)- 两者都包含React绑定+OpenAI兼容API
连接模式
- 扩展模式:通过Bodhi Browser扩展(首选)
- 直接模式:直接HTTP到本地服务器(备用)
- SDK自动检测并切换模式
身份验证
- OAuth 2.0 + PKCE流程
- 两个身份验证服务器:
- 开发:
https://main-id.getbodhi.app/realms/bodhi(允许localhost) - 生产:
https://id.getbodhi.app/realms/bodhi(需要真实域名)
- 开发:
基本集成步骤
步骤1:安装包
npm install @bodhiapp/bodhi-js-react
步骤2:使用BodhiProvider包装应用
// App.tsx
import { BodhiProvider } from '@bodhiapp/bodhi-js-react';
import Chat from './Chat';
const CLIENT_ID = 'your-client-id-from-developer.getbodhi.app';
function App() {
return (
<BodhiProvider authClientId={CLIENT_ID}>
<div className="app">
<h1>我的Bodhi聊天应用</h1>
<Chat />
</div>
</BodhiProvider>
);
}
export default App;
步骤3:创建聊天组件
// Chat.tsx
import { useState, useEffect } from 'react';
import { useBodhi } from '@bodhiapp/bodhi-js-react';
function Chat() {
const { client, isOverallReady, isAuthenticated, login, showSetup } = useBodhi();
const [prompt, setPrompt] = useState('');
const [response, setResponse] = useState('');
const [loading, setLoading] = useState(false);
const [models, setModels] = useState<string[]>([]);
const [selectedModel, setSelectedModel] = useState('');
// 挂载时加载模型
useEffect(() => {
if (isOverallReady && isAuthenticated) {
loadModels();
}
}, [isOverallReady, isAuthenticated]);
const loadModels = async () => {
const modelList: string[] = [];
for await (const model of client.models.list()) {
modelList.push(model.id);
}
setModels(modelList);
if (modelList.length > 0) setSelectedModel(modelList[0]);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!prompt.trim() || !selectedModel) return;
setLoading(true);
setResponse('');
try {
const stream = client.chat.completions.create({
model: selectedModel,
messages: [{ role: 'user', content: prompt }],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices?.[0]?.delta?.content || '';
setResponse(prev => prev + content);
}
} catch (err) {
setResponse(`错误:${err instanceof Error ? err.message : String(err)}`);
} finally {
setLoading(false);
}
};
if (!isOverallReady) {
return <button onClick={showSetup}>打开设置</button>;
}
if (!isAuthenticated) {
return <button onClick={login}>登录</button>;
}
return (
<div>
<select value={selectedModel} onChange={e => setSelectedModel(e.target.value)}>
{models.map(model => (
<option key={model} value={model}>
{model}
</option>
))}
</select>
<form onSubmit={handleSubmit}>
<input value={prompt} onChange={e => setPrompt(e.target.value)} />
<button type="submit" disabled={loading}>
{loading ? '生成中...' : '发送'}
</button>
</form>
{response && <div>{response}</div>}
</div>
);
}
export default Chat;
关键API和Hooks
useBodhi() Hook
const {
client, // SDK客户端实例(OpenAI兼容API)
isOverallReady, // 客户端和服务器都准备就绪(最常用检查)
isAuthenticated, // 用户具有有效的OAuth令牌
login, // 启动OAuth登录流程
logout, // 注销并清除令牌
showSetup, // 打开设置向导模态框
// 附加属性
isReady, // 客户端已初始化(扩展或直接URL)
isServerReady, // 服务器状态为'ready'
isInitializing, // client.init()进行中
isExtension, // 使用扩展模式
isDirect, // 使用直接HTTP模式
canLogin, // isReady && !isAuthLoading
isAuthLoading, // 身份验证操作进行中
} = useBodhi();
客户端方法(OpenAI兼容)
// 列出模型(AsyncGenerator)
for await (const model of client.models.list()) {
console.log(model.id);
}
// 流式聊天
const stream = client.chat.completions.create({
model: 'gemma-3n-e4b-it',
messages: [{ role: 'user', content: '你好!' }],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.choices?.[0]?.delta?.content || '';
// 附加到响应
}
// 非流式聊天
const response = await client.chat.completions.create({
model: 'gemma-3n-e4b-it',
messages: [{ role: 'user', content: '你好!' }],
stream: false,
});
高级配置
自定义客户端配置
<BodhiProvider
authClientId={CLIENT_ID}
clientConfig={{
redirectUri: 'https://myapp.com/callback',
basePath: '/app',
logLevel: 'debug',
}}
>
<App />
</BodhiProvider>
子路径或GitHub Pages的basePath
当您的应用程序在子路径上运行时(例如,GitHub Pages在/repo-name/):
// Vite配置
export default defineConfig({
base: '/repo-name/',
});
// BodhiProvider
<BodhiProvider authClientId={CLIENT_ID} basePath="/repo-name" callbackPath="/repo-name/callback">
<App />
</BodhiProvider>;
常见模式
条件渲染
function App() {
const { isOverallReady, isAuthenticated, showSetup, login } = useBodhi();
if (!isOverallReady) {
return <button onClick={showSetup}>需要设置</button>;
}
if (!isAuthenticated) {
return <button onClick={login}>需要登录</button>;
}
return <ChatInterface />;
}
带缓存的模型加载
const loadModels = async () => {
const cached = localStorage.getItem('bodhi_models');
if (cached) {
const { models: cachedModels, expiry } = JSON.parse(cached);
if (Date.now() < expiry) {
setModels(cachedModels);
return;
}
}
const modelList: string[] = [];
for await (const model of client.models.list()) {
modelList.push(model.id);
}
setModels(modelList);
localStorage.setItem(
'bodhi_models',
JSON.stringify({
models: modelList,
expiry: Date.now() + 3600000, // 1小时
})
);
};
错误处理
try {
const stream = client.chat.completions.create({ ... });
for await (const chunk of stream) {
// 处理块
}
} catch (err) {
if (err instanceof Error) {
console.error('聊天错误:', err.message);
setError(err.message);
}
}
详细指南
有关特定主题的全面信息,请参阅支持文档:
- 快速入门指南 - 完整的5分钟集成演练
- OAuth设置 - 开发与生产环境、客户端注册
- GitHub Pages部署 - basePath配置、404 hack、工作流程
- 故障排除 - 常见问题和解决方案
- 代码示例 - 常见模式的复制粘贴代码片段
SDK文档参考
bodhi-js-sdk存储库包含全面的文档:
bodhi-js-sdk/docs/quick-start.md- 官方快速入门bodhi-js-sdk/docs/react-integration.md- React集成深入探讨bodhi-js-sdk/docs/authentication.md- OAuth流程详情bodhi-js-sdk/docs/streaming.md- 流式传输模式bodhi-js-sdk/docs/api-reference.md- 完整的API文档
实施方法
当用户要求集成bodhi-js-sdk时:
- 检查现有设置:查找package.json、现有React组件
- 安装包:运行
npm install @bodhiapp/bodhi-js-react - 添加BodhiProvider:使用提供程序包装根组件
- 创建/更新组件:向组件添加useBodhi() hook
- 测试连接:验证扩展检测或直接模式
- 添加OAuth:指导用户在developer.getbodhi.app注册
- 实现聊天:创建流式聊天界面
- 处理错误:添加适当的错误边界和用户反馈
故障排除时:
- 检查连接状态:使用
isOverallReady、isReady、isServerReady - 验证身份验证状态:检查
isAuthenticated、auth对象 - 检查日志:在控制台中查找以
[Bodhi/Web]为前缀的日志 - 审查配置:验证authClientId、redirectUri、basePath
- 测试后端:确保本地服务器在http://localhost:1135
- 检查扩展:验证Bodhi Browser扩展已安装
测试集成
集成后,验证:
- 扩展检测:检查控制台中的
[Bodhi/Web] Extension detected - 服务器连接:验证
[Bodhi/Web] Server ready - 设置流程:点击“打开设置”测试模态框
- 身份验证:点击“登录”测试OAuth流程
- 模型加载:验证模型是否填充在下拉列表中
- 流式传输:发送消息并验证实时响应流式传输
- 错误处理:测试服务器离线以验证错误状态
常见集成任务
当用户请求时:
- “将bodhi添加到我的React应用程序” → 遵循基本集成步骤
- “为bodhi设置OAuth” → 指导到oauth-setup.md、developer.getbodhi.app
- “部署到GitHub Pages” → 参考github-pages.md获取basePath和404 hack
- “聊天不工作” → 检查troubleshooting.md获取连接/身份验证问题
- “模型未加载” → 验证服务器、检查模型端点、审查异步迭代
- “流式传输损坏” → 验证stream:true、检查AsyncGenerator模式
要引用的关键文件
实施集成时:
bodhi-js-sdk/docs/quick-start.md- 主要集成指南bodhi-js-sdk/docs/react-integration.md- React特定模式bodhi-js-sdk/docs/- 全面的文档和示例
注意事项
- 仅关注React+Vite项目
- 始终使用
@bodhiapp/bodhi-js-react预设包(最简单) - 自动回调处理默认启用(
handleCallback={true}) - OAuth回调自动发生,无需自定义路由
- 默认后端:http://localhost:1135
- 扩展模式优于直接模式
- 流式传输的AsyncGenerator模式(OpenAI兼容)