@infinilabs/ai-chat
v0.0.6
Published
一个「开箱即用」的 AI Chat 组件库,用于在 React 应用中快速集成带有:
Readme
@infinilabs/ai-chat
一个「开箱即用」的 AI Chat 组件库,用于在 React 应用中快速集成带有:
- 会话历史列表(选择、重命名、删除、刷新、搜索)
- 小助手列表(选择、翻页、搜索)
- AI 聊天区域(支持流式回答、思考过程展示、引用来源等)
安装与构建
- 项目使用 pnpm 进行依赖管理和构建。
- 最终以 npm 包形式对外发布。
pnpm add @infinilabs/ai-chatPeer Dependencies
在使用本组件库之前,请确保你的项目中安装了以下依赖(通常这些也是你项目的基础依赖):
react>= 18.3.1react-dom>= 18.3.1antd>= 6
本包默认内置:
- React 组件 + Zustand 状态管理
- Tailwind v4 + 一套内置样式(JS 自动注入,无需手动引入 CSS)
react-i18next国际化配置(中英文文案)- 依赖
antd的message组件做操作反馈 axios,dayjs,lucide-react等工具库
开发与构建:
pnpm dev # 本地开发预览
pnpm build # 构建 npm 产物(dist)
pnpm lint # 代码检查导出组件一览
Chat:核心 AI 聊天区域(流式回答、思考过程、引用文档等)。History:会话历史列表(选择、重命名、删除、刷新、搜索)。AssistantList:小助手选择列表(支持翻页、搜索、限制 ID)。ChatInput:独立输入框组件(高级用法,可单独组合 UI)。
通常推荐的组合方式是:左侧 AssistantList + History,右侧 Chat。
快速开始示例
一个最小可用的布局示例(假设你已经在后端实现了对应的接口,见后文「后端接口约定」):
import { useRef } from "react";
import {
Chat,
History,
AssistantList,
} from "@infinilabs/ai-chat";
// Chat 组件内部的 ref 方法定义(仅类型示意,见下文详细说明)
interface ChatAIRef {
init: (params: { message?: string; attachments?: string[] }) => void;
cancelChat: () => void;
clearChat: () => void;
}
function AIChatApp() {
const chatRef = useRef<ChatAIRef | null>(null);
const BaseUrl = "https://your-api-endpoint.example.com";
const Token = "your-api-token";
return (
<div className="flex h-full">
<div className="w-72 border-r flex flex-col gap-3 p-3">
<AssistantList BaseUrl={BaseUrl} Token={Token} />
<History BaseUrl={BaseUrl} Token={Token} />
</div>
<div className="flex-1 flex flex-col p-3">
<Chat ref={chatRef} BaseUrl={BaseUrl} />
</div>
</div>
);
}要点说明:
- 必须提供后端 HTTP 地址
BaseUrl,用于请求/chat/*、/assistant/*等接口。 - 如果后端需要鉴权,可以通过
Token传入History(下文有解释)。 Chat组件通过ref暴露方法来发送消息、取消对话等。- 历史、助手列表和 Chat 使用一个共享的 Zustand store 做状态同步。
Chat 组件
Props 定义
interface ChatAIProps {
BaseUrl: string;
Token?: string;
formatUrl?: (data: IChunkData) => string;
locale?: string;
t?: TFunction;
}export interface IChunkData {
message_chunk?: string;
[key: string]: unknown;
}必填 / 选填一览
| Prop | 是否必填 | 类型 | 说明 |
|-----------|----------|-----------------------------------|------|
| BaseUrl | 必填 | string | 后端服务基础地址,例如 https://api.example.com。用于文件访问等。网络请求本身通过 localStorage 中的配置读取,见「后端接口约定」。 |
| Token | 选填 | string | 预留的鉴权 Token。当前版本主要通过 History 组件统一写入 localStorage 中的 headers,供所有请求复用。 |
| formatUrl | 选填 | (data: IChunkData) => string | 用于把引用文档等 chunk 数据转换为链接地址,传递给底层 @infinilabs/chat-message。 |
| locale | 选填 | string | 语言标识(如 "en"、"zh")。不传时使用 react-i18next 默认语言。 |
| t | 选填 | TFunction | 自定义翻译函数;不传时使用组件内部的 useTranslation。 |
注意:
BaseUrl目前主要用接口请求,可以知道使用的哪个 coco-server 服务。
Ref 方法
export interface SendMessageParams {
message?: string;
attachments?: string[];
}
export interface ChatAIRef {
init: (params: SendMessageParams) => void;
cancelChat: () => void;
clearChat: () => void;
onSelectChat: (chat: Chat) => void;
}方法说明:
| 方法名 | 参数 | 说明 |
|----------------|------------------------------|------|
| init | SendMessageParams | 发送一条消息:如果当前还没有会话,则创建新会话并调用 /chat/_create;如果有正在使用的会话,则调用 /chat/{chatId}/_chat。 |
| cancelChat | 无 | 取消当前正在进行的回答,内部会请求 /chat/{chatId}/_cancel?message_id={curId},并重置流式状态。 |
| clearChat | 无 | 清空当前激活会话,在有 _id 时会调用 /chat/{chatId}/_close,然后清空 activeChat。 |
| onSelectChat | chat: Chat | 手动选择一个会话作为当前会话。内部会自动拉取该会话的历史记录。 |
Chat 内部使用了多个 Hook 来处理流式 chunk、思考过程、工具调用等(useMessageChunkData、useMessageHandler),并把这些数据传给 ChatContent 再由 @infinilabs/chat-message 渲染。
History 组件
Props 定义
interface HistoryProps {
BaseUrl: string;
Token?: string;
locale?: string;
t?: TFunction;
}功能与行为
- 初始化时根据
BaseUrl、Token写入:localStorage["app-store"] = { state: { endpoint_http: BaseUrl } }localStorage["headers"] = { "X-API-TOKEN": Token }(仅当传入Token时)
- 使用
GET /chat/_history拉取会话列表并写入 Zustand store:- 默认请求参数:
{ from: 0, size: 100, keyword } - 当没有当前激活会话时,会自动选中第一条。
- 默认请求参数:
- 渲染内部的
HistoryList,支持:- 搜索(关键字筛选)
- 手动刷新
- 选择会话
- 重命名会话(
PUT /chat/{chatId}) - 删除会话(
DELETE /chat/{chatId})
使用示例
import { History } from "@infinilabs/ai-chat";
function Sidebar() {
return (
<History
BaseUrl="https://api.example.com"
Token="your-api-token"
locale="zh"
/>
);
}推荐做法:
- 统一通过
History组件初始化BaseUrl和Token,后续所有Get/Post/streamPost请求都会复用这两个配置。
AssistantList 组件
Props 定义
interface AssistantListProps {
BaseUrl: string;
Token?: string;
assistantIDs?: string[];
locale?: string;
t?: TFunction;
}注意:当前实现中,BaseUrl、Token 同样依赖 axiosRequest.ts 中从 localStorage 读取的配置,因此实际生效的仍然是 History 写入的值;在布局中同时使用 History 和 AssistantList 即可。
功能与行为
- 使用
POST /assistant/_search获取小助手列表,支持:- 分页:
current(页码)、pageSize(每页数量,默认 10) - 搜索:
keyword - 过滤:
ids(通过assistantIDs传入一组助手 ID)
- 分页:
- 将获取到的助手列表写入 Zustand store。
- 当列表不为空且当前没有已选助手时,自动选择第一条。
- 支持滚动加载更多(触底 +
hasMore控制)。 - UI 方面:
- 顶部按钮显示当前助手 name 与 icon。
- 点击后浮层中展示搜索输入框 + 列表,可切换助手。
使用示例
import { AssistantList } from "@infinilabs/ai-chat";
function AssistantSelector() {
return (
<AssistantList
BaseUrl="https://api.example.com"
Token="your-api-token"
assistantIDs={["assistant-1", "assistant-2"]}
locale="zh"
/>
);
}Chat 组件在创建新会话时,会从 store 中读取当前助手的 _id,并作为 assistant_id 写入 /chat/_create 请求体。
ChatInput 组件(可选)
该组件内部已被 Chat 使用,一般不需要单独使用。如果你希望自定义布局、只复用输入框,可以单独引入:
interface ChatInputProps {
onSend: (params: SendMessageParams) => void;
disabled: boolean;
isChatMode: boolean;
inputValue: string;
changeInput: (val: string) => void;
isDeepThinkActive: boolean;
setIsDeepThinkActive: () => void;
chatPlaceholder?: string;
searchPlaceholder?: string;
returnToInputShortcut?: string; // 默认 "i"
}特性:
- 自动高度 textarea。
- 支持 Enter 发送、Shift+Enter 换行。
- 支持「按 i 聚焦输入框」的快捷键提示。
- 底部附加一些模式切换、深度思考开关等控制按钮。
类型定义汇总
export interface ChatMessageSource {
type: string;
assistant_id?: string;
message: string;
question?: string;
[key: string]: unknown;
}
export interface ChatMessageItem {
_id: string;
_source: ChatMessageSource;
}
export interface Chat {
_id: string;
messages?: ChatMessageItem[];
_source?: {
id?: string;
[key: string]: unknown;
};
}你可以在自己的代码中根据需要复制这些接口,用于约束后端返回的数据结构。
小结与推荐接入方式
- 在应用中引入
AssistantList、History、Chat三个组件,组成完整的 AI Chat 页面。 - 使用
History组件统一配置BaseUrl、Token,确保所有请求都能访问到后端。 - 如需手动发送消息或控制对话流程,可以通过
Chat的ref调用init、cancelChat、clearChat等方法。 - 如需深度定制 UI,仅复用其中部分组件(例如只用
Chat或ChatInput),也完全支持。
