iTick AI Chat SDK — 为金融资讯平台提供即插即用的 AI 对话能力。
- ShadowDOM 样式隔离 — 组件样式完全隔离,不受宿主页面 CSS 影响
- 虚拟滚动 — 支持万级消息量的高性能渲染
- Markdown 渲染 — 内置 GFM 格式渲染,支持表格、代码高亮、流式输出
- 多语言 — 内置简体中文、繁体中文、英文
- 主题切换 — 支持亮色/暗色/跟随系统三种模式
- 双模式 — 极速模式(fast)与智能分析模式(think),支持运行时切换
- 流式 SSE — 基于 Server-Sent Events 的实时流式输出,支持取消
- 消息分段 — 支持 think 与 content 交替流式输出,每个 think 模块独立状态
- 框架无关 — 核心纯 TypeScript,同时提供 React / Vue 3 封装
# 核心包(必需)
npm install @itick/chat-core
# React 封装(可选)
npm install @itick/chat-react
# Vue 3 封装(可选)
npm install @itick/chat-vue也可以通过 <script> 标签直接引入 UMD 包:
<script src="https://unpkg.com/@itick/chat-core/dist/chat-core.umd.js"></script>
<script>
const sdk = new ChatSDK.ChatSDK({ /* ... */ });
</script>import { ChatSDK } from '@itick/chat-core';
const sdk = new ChatSDK({
apiUrl: 'https://agent.itick.org/agent/token/stream',
token: 'your-api-token',
container: document.getElementById('chat-container'),
width: '100%',
height: '600px',
});
// 监听事件
sdk.on('message:receive', ({ message }) => {
console.log('收到消息:', message.content);
});
sdk.on('error', ({ error }) => {
console.error('请求出错:', error);
});
// 挂载到页面
sdk.mount();import { ChatWidget, type ChatWidgetRef } from '@itick/chat-react';
import { useRef } from 'react';
function App() {
const chatRef = useRef<ChatWidgetRef>(null);
return (
<ChatWidget
ref={chatRef}
apiUrl="https://agent.itick.org/agent/token/stream"
token="your-api-token"
width="100%"
height="600px"
locale="zh-CN"
onMessageReceive={({ message }) => console.log(message)}
/>
);
}<template>
<ChatWidget
ref="chatRef"
:apiUrl="apiUrl"
:token="token"
width="100%"
height="600px"
locale="zh-CN"
@messageReceive="onReceive"
/>
</template>
<script setup>
import { ref } from 'vue';
import { ChatWidget } from '@itick/chat-vue';
const chatRef = ref();
const apiUrl = 'https://agent.itick.org/agent/token/stream';
const token = 'your-api-token';
function onReceive({ message }) {
console.log(message);
}
</script>| 字段 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
apiUrl |
string |
是 | - | 流式接口地址 |
token |
string |
是 | - | 认证 token,通过 HTTP Header token 字段传递 |
container |
HTMLElement |
是 | - | 宿主 DOM 节点 |
width |
number | string |
否 | 自适应容器 | 宽度,支持数字(px)或字符串 |
height |
number | string |
否 | 自适应容器 | 高度 |
themeColor |
string |
否 | "#4F46E5" |
主题色 |
theme |
'light' | 'dark' | 'auto' |
否 | "light" |
主题模式 |
customCSS |
string |
否 | - | 注入自定义 CSS |
locale |
'zh-CN' | 'zh-TW' | 'en-US' |
否 | - | 界面语言 |
placeholder |
string |
否 | 由 i18n 决定 | 输入框占位文本 |
mode |
'fast' | 'think' |
否 | "think" |
聊天模式 |
actions |
ActionsConfig |
否 | 全部显示 | 操作按钮显隐配置 |
disclaimer |
string |
否 | 由 i18n 决定 | 免责声明文本 |
welcomeTitle |
string |
否 | 由 i18n 决定 | 空状态欢迎标题 |
welcomeDescription |
string |
否 | 由 i18n 决定 | 空状态欢迎描述 |
presetQuestions |
string[] |
否 | 6 个金融问题 | 空状态预设问题 |
onQuestionClick |
(q: string) => void |
否 | - | 预设问题点击回调(SDK 已自动发送消息) |
onError |
(error: Error) => void |
否 | - | 错误回调 |
renderHistoryMessage |
(msg: ChatMessage) => HTMLElement | string |
否 | - | 历史消息自定义渲染 |
renderActions |
(msg: ChatMessage, callbacks: ActionCallbacks) => HTMLElement | undefined |
否 | - | 自定义操作按钮(追加到默认按钮后) |
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
showCopy |
boolean |
true |
显示复制按钮 |
showRegenerate |
boolean |
true |
显示重新生成按钮 |
showLike |
boolean |
true |
显示点赞按钮 |
showShare |
boolean |
true |
显示分享按钮 |
| 方法 | 描述 |
|---|---|
mount() |
挂载到容器,创建 ShadowDOM 并渲染 UI。已销毁时抛错 |
unmount() |
移除 ShadowDOM,取消流式请求,保留实例和消息数据 |
destroy() |
彻底销毁实例,清空所有资源和事件监听 |
| 方法 | 描述 |
|---|---|
sendMessage(content: string): Promise<void> |
发送消息,自动创建用户和 AI 消息,发起流式请求 |
clearMessages() |
清空所有消息 |
getMessages(): ChatMessage[] |
获取消息列表副本 |
setHistoryMessages(messages: ChatMessage[]) |
设置历史消息列表 |
| 方法 | 描述 |
|---|---|
updateConfig(partial: Partial<ChatConfig>) |
动态修改配置,触发 UI 更新 |
getConfig(): Readonly<ChatConfig> |
获取当前配置的只读副本 |
| 方法 | 描述 |
|---|---|
registerPlugin(plugin: MessageRenderPlugin) |
注册消息渲染插件 |
unregisterPlugin(name: string) |
注销插件 |
| 属性 | 类型 | 描述 |
|---|---|---|
isMounted |
boolean |
是否已挂载 |
isDestroyed |
boolean |
是否已销毁 |
通过 sdk.on(event, handler) 注册事件监听,sdk.off(event, handler) 移除。
| 事件 | 参数 | 触发时机 |
|---|---|---|
mount |
无 | mount() 完成后 |
unmount |
无 | unmount() 完成后 |
destroy |
无 | destroy() 完成后 |
| 事件 | 参数 | 触发时机 |
|---|---|---|
message:send |
{ content: string } |
用户发送消息时 |
message:receive |
{ message: ChatMessage } |
AI 消息流式完成后 |
message:streaming |
{ message: ChatMessage; chunk: string } |
每次收到 content chunk |
message:thinking |
{ message: ChatMessage; chunk: string } |
每次收到 think chunk |
message:done |
{ message: ChatMessage } |
流式输出完成(含主动取消) |
| 事件 | 参数 | 触发时机 |
|---|---|---|
actions:copy |
{ messageId, content, message } |
点击复制按钮 |
actions:regenerate |
{ messageId, message } |
点击重新生成按钮 |
actions:like |
{ messageId, content, message } |
点击点赞按钮 |
actions:share |
{ messageId, content, message } |
点击分享按钮 |
| 事件 | 参数 | 触发时机 |
|---|---|---|
error |
{ error: Error } |
流式请求出错时 |
config:change |
{ config: ChatConfig } |
updateConfig() 调用后 |
sdk.on('message:streaming', ({ chunk }) => {
console.log('收到流式片段:', chunk);
});
sdk.on('message:done', ({ message }) => {
console.log('消息完成:', message.content);
});
sdk.on('actions:copy', ({ content }) => {
console.log('用户复制了:', content);
});interface ChatMessage {
id: string; // 唯一标识
role: 'user' | 'assistant' | 'system';
segments: MessageSegment[]; // think 与 content 按流顺序混合排列
content: string; // 所有 content 段拼接(向后兼容)
timestamp: number; // 时间戳
status?: 'sending' | 'streaming' | 'done' | 'error';
metadata?: Record<string, unknown>;
}
interface MessageSegment {
type: 'think' | 'content';
content: string;
status: 'streaming' | 'done';
}segments 数组按流式到达顺序存储 think 和 content 块。例如:
[think] → [content] → [think] → [content] → ...
每个 think 模块拥有独立的 streaming/done 状态,完成后自动折叠。
// 初始化时设置
const sdk = new ChatSDK({ theme: 'auto', /* ... */ });
// 运行时切换
sdk.updateConfig({ theme: 'dark' });支持三种模式:light(亮色)、dark(暗色)、auto(跟随系统)。
const sdk = new ChatSDK({ locale: 'zh-CN', /* ... */ });
// 运行时切换
sdk.updateConfig({ locale: 'en-US' });支持:zh-CN(简体中文)、zh-TW(繁体中文)、en-US(英文)。
const sdk = new ChatSDK({
customCSS: `
.chat-message-user .chat-message-bubble {
border-radius: 20px;
}
`,
/* ... */
});CSS 通过 ShadowDOM 注入,不会影响宿主页面。可通过 CSS 变量覆盖主题色:
--chat-primary: #4F46E5;
--chat-primary-hover: #4338CA;const sdk = new ChatSDK({
// 隐藏部分按钮
actions: {
showLike: false,
showShare: false,
},
// 添加自定义按钮
renderActions: (message, callbacks) => {
const btn = document.createElement('button');
btn.textContent = '收藏';
btn.addEventListener('click', () => {
callbacks.onCopy(message.content);
});
return btn;
},
/* ... */
});用户可在输入框左下角切换极速模式(fast)和智能分析模式(think)。think 模式下会展示 AI 的思考过程。
// 初始化设置
const sdk = new ChatSDK({ mode: 'fast', /* ... */ });
// 运行时切换
sdk.updateConfig({ mode: 'think' });插件可以覆盖默认的 Markdown 渲染行为:
interface MessageRenderPlugin {
name: string;
match: (message: ChatMessage) => boolean;
render: (message: ChatMessage, shadowRoot: ShadowRoot) => HTMLElement;
update?: (element: HTMLElement, message: ChatMessage) => void;
}sdk.registerPlugin({
name: 'code-highlight',
match: (msg) => msg.content.includes('```'),
render: (msg) => {
const el = document.createElement('div');
el.innerHTML = /* 自定义渲染逻辑 */;
return el;
},
update: (el, msg) => {
el.innerHTML = /* 流式更新逻辑 */;
},
});
// 注销插件
sdk.unregisterPlugin('code-highlight');注意:注册插件会覆盖内置的 Markdown 渲染。插件按注册顺序从后往前匹配,后注册的优先级更高。
const sdk = new ChatSDK({
renderHistoryMessage: (message) => {
// 返回 HTML 字符串或 HTMLElement
return `<div class="custom-message">${message.content}</div>`;
},
/* ... */
});const sdk = new ChatSDK({
presetQuestions: [
'今日美股三大指数行情如何?',
'BTC/USDT 当前价格和24小时涨跌幅?',
],
onQuestionClick: (question) => {
console.log('用户点击了问题:', question);
// SDK 已自动发送消息,此处仅作为通知钩子
},
/* ... */
});| 属性 | 类型 | 必填 | 默认值 |
|---|---|---|---|
apiUrl |
string |
是 | - |
token |
string |
是 | - |
width |
number | string |
否 | "100%" |
height |
number | string |
否 | "600px" |
themeColor |
string |
否 | "#4F46E5" |
customCSS |
string |
否 | - |
locale |
'zh-CN' | 'zh-TW' | 'en-US' |
否 | "zh-CN" |
placeholder |
string |
否 | - |
renderHistoryMessage |
(msg: ChatMessage) => HTMLElement | string |
否 | - |
onMessageSend |
(payload: { content: string }) => void |
否 | - |
onMessageReceive |
(payload: { message: ChatMessage }) => void |
否 | - |
onMessageStreaming |
(payload: { message: ChatMessage; chunk: string }) => void |
否 | - |
onMessageDone |
(payload: { message: ChatMessage }) => void |
否 | - |
onError |
(payload: { error: Error }) => void |
否 | - |
onConfigChange |
(payload: { config: ChatConfig }) => void |
否 | - |
| 方法 | 签名 |
|---|---|
sendMessage |
(content: string) => Promise<void> |
clearMessages |
() => void |
getMessages |
() => ChatMessage[] |
setHistoryMessages |
(messages: ChatMessage[]) => void |
registerPlugin |
(plugin: MessageRenderPlugin) => void |
unregisterPlugin |
(name: string) => void |
updateConfig |
(partial: Partial<ChatConfig>) => void |
getSDK |
() => ChatSDK | null |
注意:
apiUrl、token、width、height的 prop 变化不会触发热更新,需要重新挂载组件。themeColor、customCSS、locale、placeholder支持热更新。
与 React 相同(不含回调类 props)。
| 事件 | 参数 |
|---|---|
messageSend |
{ content: string } |
messageReceive |
{ message: ChatMessage } |
messageStreaming |
{ message: ChatMessage; chunk: string } |
messageDone |
{ message: ChatMessage } |
error |
{ error: Error } |
configChange |
{ config: ChatConfig } |
与 React ChatWidgetRef 完全一致,额外包含 getSDK()。
SDK 发送 POST 请求到 apiUrl:
Headers:
Content-Type: application/json
token: {your-token}
Accept: text/event-stream
Body:
{
"input": {
"messages": [
{ "type": "human", "content": "用户消息内容" }
]
},
"config": {
"configurable": {
"thread_id": "{uuid}",
"enable_thinking": true
}
}
}
thread_id在实例化时自动生成,用于服务端会话保持enable_thinking根据mode配置决定(think为true,fast为false)- 只发送
role === 'user'的消息
服务端需返回 Content-Type: text/event-stream,每条数据格式:
data: {"type": "think", "content": "思考过程..."}
data: {"type": "content", "content": "回复内容..."}
data: [DONE]
type: "think"— 思考过程,SDK 将其渲染为可折叠的 think 模块type: "content"— 正式回复内容,SDK 渲染为 Markdown 气泡[DONE]— 流结束标记
同时兼容 OpenAI 格式(choices[0].delta.content)。
# 安装依赖
npm install
# 构建所有包
npm run build
# 代码检查
npm run lint通过 customCSS 配置注入自定义 CSS,或通过 themeColor 设置主题色。所有样式通过 ShadowDOM 隔离,CSS 变量名以 --chat- 为前缀。
通过 onError 配置或 error 事件监听:
const sdk = new ChatSDK({
onError: (error) => { /* 处理错误 */ },
/* ... */
});
sdk.on('error', ({ error }) => {
console.error(error);
});用户点击发送按钮(流式输出中会变为停止按钮)即可取消。程序化取消可通过 sdk.unmount() 或 sdk.destroy()。
thread_id 在实例化时生成,只要不调用 destroy() 重建实例,同一个实例的多次对话会共享 thread_id,服务端可据此保持会话上下文。
不影响。虚拟滚动仅优化 DOM 节点的创建和回收,消息内容通过 segments 和 content 字段完整保留,操作按钮和推荐问题在消息状态变为 done 时自动追加。