使用场景
AIChatDialogue 组件可搭配 AIChatInput 使用,实现更丰富的、功能覆盖更全面、定制更加便捷的 AI 会话场景。组件消息格式以 OpenAI 的 Response Object 为原型,默认支持 OpenAI 社区 Response / Chat Completion 格式标准,对 GPT-5、GPT-4o 系列模型的响应均支持开箱即用,详见消息数据转换。
代码演示
如何引入
import { AIChatDialogue } from '@fibrous/semi-ai-solid';
基本用法
通过设置 chats 和 onChatsChange 实现基础对话显示和交互。更新时传入新的消息数组:消息数组保存在 createSignal 中时,原地修改(如 push)不会重新渲染,需要替换为新的数组。
使用 align 属性可以设置对话的布局,支持左右分布(leftRight, 默认)和左对齐(leftAlign)。
Hello, I'm your AI assistant.
给一个 Semi Design 的 Button 组件的使用示例
以下是一个 Semi 代码的使用示例:
jsx
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message, type AIChatDialogueProps } from '@fibrous/semi-ai-solid';
import { Radio, RadioGroup, type RadioChangeEvent } from '@fibrous/semi-ui-solid';
const defaultMessages: Message[] = [
{
role: 'system',
id: '1',
createdAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createdAt: 1715676751919,
content: '给一个 Semi Design 的 Button 组件的使用示例',
},
{
role: 'assistant',
id: '3',
createdAt: 1715676751919,
content: "以下是一个 Semi 代码的使用示例:\n```jsx \nimport React from 'react';\nimport { Button } from '@fibrous/semi-ui-react';\n\nconst MyComponent = () => {\n return (\n <Button>Click me</Button>\n );\n};\nexport default MyComponent;\n```\n",
},
];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const containerStyle = { display: 'flex', 'flex-direction': 'column', 'row-gap': '8px' } as const;
const rowStyle = { display: 'flex', 'align-items': 'center', 'column-gap': '10px' } as const;
const panelStyle = { border: '1px solid var(--semi-color-border)', 'border-radius': '12px', 'margin-top': '10px', padding: '20px' } as const;
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const [mode, setMode] = createSignal<NonNullable<AIChatDialogueProps['mode']>>('bubble');
const [align, setAlign] = createSignal<NonNullable<AIChatDialogueProps['align']>>('leftRight');
const onAlignChange = (e: RadioChangeEvent) => {
setAlign(e.target.value);
};
const onModeChange = (e: RadioChangeEvent) => {
setMode(e.target.value);
};
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
return (
<>
<span style={containerStyle}>
<span style={rowStyle}>
模式
<RadioGroup aria-label="模式" onChange={onModeChange} value={mode()} type="button">
<Radio value="bubble">气泡</Radio>
<Radio value="noBubble">非气泡</Radio>
<Radio value="userBubble">用户会话气泡</Radio>
</RadioGroup>
</span>
<span style={rowStyle}>
会话布局方式
<RadioGroup aria-label="会话布局方式" onChange={onAlignChange} value={align()} type="button">
<Radio value="leftRight">左右分布</Radio>
<Radio value="leftAlign">左对齐</Radio>
</RadioGroup>
</span>
</span>
<div style={panelStyle}>
<AIChatDialogue align={align()} mode={mode()} chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />
</div>
</>
);
}自动滚动
对话区高度固定时,新消息和流式输出的内容会让列表保持在底部。任何向上的滚动(滚轮、拖动滚动条、键盘)都会停止跟随;滚回底部或发送新消息后恢复。在 chats 头部插入更早的消息(如滚到顶部时加载历史)不会滚动列表,已显示的消息保持在原位置。
点击下方按钮发送消息,回复会以流式方式逐步生成。
代码
import { createSignal, onCleanup } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-solid';
import { Button } from '@fibrous/semi-ui-solid';
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
};
const REPLY = '这是一段模拟的流式回复,每隔一小段时间追加几个字。回复生成期间,向上滚动(滚轮、拖动滚动条、PageUp 或方向键)会停止自动跟随,停在你滚到的位置;滚回底部后恢复跟随;发送新消息时也会回到底部。'.repeat(3);
const defaultMessages: Message[] = [{ role: 'assistant', id: 'welcome', content: '点击下方按钮发送消息,回复会以流式方式逐步生成。' }];
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const [streaming, setStreaming] = createSignal(false);
let timer: ReturnType<typeof setInterval> | undefined;
onCleanup(() => clearInterval(timer));
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
const send = () => {
const replyId = `reply-${Date.now()}`;
setMessages((prev) => [...prev, { role: 'user', id: `user-${Date.now()}`, content: '请生成一段较长的回复' }, { role: 'assistant', id: replyId, content: '', status: 'in_progress' }]);
setStreaming(true);
let length = 0;
timer = setInterval(() => {
length += 6;
const done = length >= REPLY.length;
setMessages((prev) => prev.map((message) => (message.id === replyId ? { ...message, content: REPLY.slice(0, length), status: done ? 'completed' : 'in_progress' } : message)));
if (done) {
clearInterval(timer);
setStreaming(false);
}
}, 100);
};
return (
<>
<Button onClick={send} disabled={streaming()} style={{ 'margin-bottom': '12px' }}>
发送消息
</Button>
<div style={{ height: '320px' }}>
<AIChatDialogue chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />
</div>
</>
);
}消息状态
chats 类型为 Message[], Message 包含对话的各种信息,如角色 role、内容 content、状态 status 、唯一标识 id、创建时间 createdAt 等,具体见 Message。其中 status 和 Response API Status 相同,存在 6 种状态,对应 3 种官方样式(成功 / 请求中 / 失败)。
请求成功
请求错误
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-solid';
const defaultMessages: Message[] = [
{
role: 'assistant',
id: '1',
createdAt: 1715676751919,
content: '请求成功',
},
{
id: 'loading',
role: 'assistant',
status: 'in_progress', // renders like queued
},
{
role: 'assistant',
id: 'error',
content: '请求错误',
status: 'failed', // renders like cancelled
},
];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
return <AIChatDialogue chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />;
}消息展示
消息内容展示的类型为 ContentItem[],支持文本 text、文件 file、图片 image、代码 code、思考块 reasoning、参考来源 annotation、工具调用 tool call 等消息块的展示,同时提供 AIChatDialogue.Step 组件用于步骤等信息的分步展示。
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message, type DialogueContentItemRendererMap } from '@fibrous/semi-ai-solid';
import { IconBriefStroked, IconCodeStroked, IconSearchStroked } from '@fibrous/semi-icons-solid';
interface PlanStep {
type?: string;
summary?: string;
description?: string;
}
interface PlanBlock {
summary?: string;
steps?: PlanStep[];
}
const defaultMessages: Message[] = [
{
role: 'assistant',
id: '1',
createdAt: 1715676751919,
content: '普通文本',
},
{
id: '2',
role: 'user',
content: [
{
type: 'message',
content: [
{
type: 'input_text',
text: '帮我生成类似的图片',
},
{
type: 'input_image',
image_url: '/assets/root-web-sites/edit-bag.jpeg',
file_id: 'demo-file-id',
},
{
type: 'input_text',
text: '以下是文件展示',
},
{
type: 'input_file',
file_url: 'https://www.semi.pdf',
filename: 'semi.pdf',
size: '100KB',
},
{
type: 'input_file',
file_url: 'https://www.semi.json',
filename: 'semi.json',
size: '100KB',
},
{
type: 'input_file',
file_url: 'https://www.semi.docx',
filename: 'semi.docx',
size: '100KB',
},
],
},
],
status: 'completed',
},
{
id: '3',
role: 'assistant',
content: [
{
type: 'reasoning',
status: 'completed',
summary: [
{
type: 'summary_text',
text: '\n我需要思考并回答用户关于什么是 Semi 组件库的问题...',
},
],
},
{
type: 'message',
content: [
{
type: 'output_text',
text: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。',
},
],
status: 'completed',
},
{
id: 'fc_12345xyz',
call_id: 'call_12345xyz',
type: 'function_call',
name: 'get_weather',
status: 'completed',
arguments: "{'location':'Paris, France'}",
},
{
type: 'message',
content: [
{
type: 'output_text',
text: '恭喜你,你已经掌握了 semi design 的所有知识!',
annotations: [
{
title: 'semi.design',
url: 'https://semi.design/',
detail: 'semi design page',
logo: '/assets/placeholder/logo.svg',
},
{
title: 'semi.design',
url: 'https://semi.design/',
detail: 'semi design page',
logo: '/assets/placeholder/logo.svg',
},
],
},
],
},
{
type: 'plan',
content: [
{
summary: '创建一份全面的北京旅游攻略,包含景点、住宿、交通、美食和实用旅行建议',
steps: [
{
summary: '搜索北京旅游景点介绍及门票信息',
description: '正在搜索: 北京旅游景点介绍及门票信息',
type: 'search',
},
{
summary: '读取指定文件的指定行内容',
description: '正在创建文档: 北京旅游攻略',
type: 'docs',
},
{
summary: '创建包含北京旅游攻略的文件',
description: '正在创建代码文件: beijing_travel_guide.html',
type: 'code',
},
],
},
{
summary: '总结北京旅游攻略的创建成果并呈现给用户',
steps: [],
},
],
},
],
status: 'completed',
},
];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
function stepIcon(type?: string) {
switch (type) {
case 'search':
return <IconSearchStroked />;
case 'docs':
return <IconBriefStroked />;
case 'code':
return <IconCodeStroked />;
default:
return undefined;
}
}
function toActions(steps: PlanStep[] = []) {
return steps.map((item) => ({
summary: item.summary,
description: item.description,
icon: stepIcon(item.type),
}));
}
const customRender: DialogueContentItemRendererMap = {
plan: (item) => {
const steps = (item.content as PlanBlock[]).map((plan) => ({
summary: plan.summary,
actions: toActions(plan.steps),
status: 'completed',
}));
return <AIChatDialogue.Step steps={steps} />;
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
return <AIChatDialogue chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} renderDialogueContentItem={customRender} />;
}引用
通过 references 字段定义当前消息引用的文件或者文本, showReference 配置当前消息是否显示可被引用样式, onReferenceClick 配置引用按钮点击回调。具体和 AIChatInput 的搭配使用见 AI 组件构建对话
当前消息为引用 demo 的示例
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-solid';
const defaultMessages: Message[] = [
{
id: '1',
role: 'user',
content: '当前消息为引用 demo 的示例',
references: [
{
id: '1',
type: 'text',
content: '测试文本,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字',
},
{
id: '2',
name: '飞书文档.docx',
},
{
id: '3',
name: 'Music.mp4',
},
{
id: '4',
name: 'Image.jpeg',
url: '/assets/placeholder/avatar-b.svg',
},
{
id: '5',
name: 'code.json',
},
],
},
];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
const onReferenceClick = () => {
console.log('You click the reference button!');
};
return <AIChatDialogue chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} showReference onReferenceClick={onReferenceClick} />;
}选择
Hello, I'm your AI assistant.
给一个 Semi Design 的 Button 组件的使用示例
以下是一个 Semi 代码的使用示例:
jsx
代码
import { createSignal, onMount } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueElement, type AIChatDialogueMessage as Message, type AIChatDialogueProps } from '@fibrous/semi-ai-solid';
import { Radio, RadioGroup, type RadioChangeEvent } from '@fibrous/semi-ui-solid';
const defaultMessages: Message[] = [
{
role: 'system',
id: '1',
createdAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createdAt: 1715676751919,
content: '给一个 Semi Design 的 Button 组件的使用示例',
},
{
role: 'assistant',
id: '3',
createdAt: 1715676751919,
content: "以下是一个 Semi 代码的使用示例:\n```jsx \nimport React from 'react';\nimport { Button } from '@fibrous/semi-ui-react';\n\nconst MyComponent = () => {\n return (\n <Button>Click me</Button>\n );\n};\nexport default MyComponent;\n```\n",
},
];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const containerStyle = { display: 'flex', 'flex-direction': 'column', 'row-gap': '8px' } as const;
const rowStyle = { display: 'flex', 'align-items': 'center', 'column-gap': '10px' } as const;
const panelStyle = { border: '1px solid var(--semi-color-border)', 'border-radius': '12px', 'margin-top': '10px', padding: '20px' } as const;
const onSelect = (selectionIds: string[]) => {
console.log('onSelect', selectionIds);
};
export default function AIChatDialogueDemo() {
let dialogue: AIChatDialogueElement | undefined;
const [messages] = createSignal(defaultMessages);
const [align, setAlign] = createSignal<AIChatDialogueProps['align']>('leftRight');
const [select, setSelect] = createSignal(true);
const [selection, setSelection] = createSignal('allSelect');
onMount(() => {
dialogue?.selectAll();
});
const onSelectChange = (e: RadioChangeEvent) => {
setSelect(e.target.value);
};
const onSelectionChange = (e: RadioChangeEvent) => {
if (e.target.value === 'allSelect') {
dialogue?.selectAll();
} else {
dialogue?.deselectAll();
}
setSelection(e.target.value);
};
const onAlignChange = (e: RadioChangeEvent) => {
setAlign(e.target.value);
};
return (
<div>
<span style={containerStyle}>
<span style={rowStyle}>
会话布局方式
<RadioGroup onChange={onAlignChange} value={align()} type="button">
<Radio value="leftRight">左右分布</Radio>
<Radio value="leftAlign">左对齐</Radio>
</RadioGroup>
</span>
<span style={rowStyle}>
是否开启选择
<RadioGroup onChange={onSelectChange} value={select()} type="button">
<Radio value={true}>开启</Radio>
<Radio value={false}>关闭</Radio>
</RadioGroup>
</span>
<span style={rowStyle}>
选择方式
<RadioGroup onChange={onSelectionChange} value={selection()} type="button">
<Radio value="allSelect">全选</Radio>
<Radio value="cancelSelect">取消全选</Radio>
</RadioGroup>
</span>
</span>
<div style={panelStyle}>
<AIChatDialogue ref={(element) => (dialogue = element)} align={align()} mode="bubble" chats={messages()} selecting={select()} onSelect={onSelect} roleConfig={roleConfig} />
</div>
</div>
);
}提示
通过 hints 可设置提示区域内容, 点击提示内容后,提示内容将成为新的用户输入内容,并触发 onHintClick 回调。
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-solid';
const defaultMessages: Message[] = [
{
role: 'assistant',
id: '1',
createdAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
},
];
const hintsExample = ['Semi 组件库有哪些常用组件?', '能否展示一个使用 Semi 组件库构建的页面示例?', 'Semi 组件库有官方文档吗?'];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const [hints, setHints] = createSignal(hintsExample);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
const onHintClick = () => {
setHints([]);
};
return <AIChatDialogue align="leftRight" mode="bubble" chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} hints={hints()} onHintClick={onHintClick} />;
}自定义渲染提示
通过 renderHintBox 可自定义提示区域内容, 参数如下
type renderHintBox = (props: { content: string; index: number; onHintClick: () => void }) => JSX.Element;
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。
代码
import { type JSX, createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-solid';
import { IconArrowRight } from '@fibrous/semi-icons-solid';
const commonHintStyle: JSX.CSSProperties = {
border: '1px solid var(--semi-color-border)',
padding: '10px',
'border-radius': '10px',
'background-color': 'transparent',
color: 'var( --semi-color-text-1)',
display: 'flex',
'justify-content': 'space-between',
'align-items': 'center',
cursor: 'pointer',
font: 'inherit',
'font-size': '14px',
};
const defaultMessages: Message[] = [
{
role: 'assistant',
id: '1',
createdAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
},
];
const hintsExample = ['Semi 组件库有哪些常用组件?', '能否展示一个使用 Semi 组件库构建的页面示例?', 'Semi 组件库有官方文档吗?'];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const [hints, setHints] = createSignal(hintsExample);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
const onHintClick = () => {
setHints([]);
};
return (
<AIChatDialogue
align="leftRight"
mode="bubble"
chats={messages()}
roleConfig={roleConfig}
onChatsChange={onChatsChange}
hints={hints()}
onHintClick={onHintClick}
renderHintBox={(props) => (
<button type="button" style={commonHintStyle} onClick={props.onHintClick}>
{props.content}
<IconArrowRight style={{ 'margin-left': '10px' }} />
</button>
)}
/>
);
}自定义渲染会话框
import type { JSX } from 'solid-js';
export interface RenderTitleProps {
message?: Message;
role?: Metadata;
/* 创建默认标题,在插入处调用 */
defaultTitle?: () => JSX.Element;
}
export interface RenderAvatarProps {
message?: Message;
role?: Metadata;
/* 创建默认头像,在插入处调用 */
defaultAvatar?: () => JSX.Element;
}
export interface RenderContentProps {
message?: Message;
role?: Metadata;
/* 创建默认内容,在插入处调用 */
defaultContent?: () => JSX.Element;
class?: string;
}
/* 默认按钮的渲染函数,与默认操作栏显示的按钮一致,按钮不显示时(如消息未完成)对应的键不存在;在插入处调用 */
export interface DefaultActionNodeObj {
copyNode?: () => JSX.Element;
dislikeNode?: () => JSX.Element;
editNode?: () => JSX.Element;
likeNode?: () => JSX.Element;
moreNode?: () => JSX.Element;
resetNode?: () => JSX.Element;
shareNode?: () => JSX.Element;
}
export interface RenderActionProps {
message?: Message;
/* 按显示顺序排列的默认按钮渲染函数,在插入处逐个调用 */
defaultActions?: (() => JSX.Element)[];
class: string;
defaultActionsObj?: DefaultActionNodeObj;
}
/* 各默认节点的渲染函数,在插入处调用;没有调用的节点不会创建 */
export interface FullDialogueNodes {
avatar?: () => JSX.Element;
title?: () => JSX.Element;
content?: () => JSX.Element;
action?: () => JSX.Element;
}
export interface RenderFullDialogueProps {
message?: Message;
role?: Metadata;
defaultNodes?: FullDialogueNodes;
class: string;
}
export interface DialogueRenderConfig {
/* 自定义渲染消息操作栏 */
renderDialogueAction?: (props: RenderActionProps) => JSX.Element;
/* 自定义渲染头像 */
renderDialogueAvatar?: (props: RenderAvatarProps) => JSX.Element;
/* 自定义渲染内容区域 */
renderDialogueContent?: (props: RenderContentProps) => JSX.Element;
/* 自定义渲染标题 */
renderDialogueTitle?: (props: RenderTitleProps) => JSX.Element;
/* 完全自定义渲染整个聊天框 */
renderFullDialogue?: (props: RenderFullDialogueProps) => JSX.Element;
}
通过 renderDialogueAvatar 和 renderDialogueTitle 可自定义头像和标题。渲染函数参数中的 role、message、defaultActions、class 等是访问器,在 JSX 中直接读取(如 props.role?.name),不要解构。defaultTitle、defaultAvatar、defaultContent 和 defaultNodes 的各项是渲染函数,在插入处调用(如 {props.defaultTitle?.()}),没有调用的节点不会创建。
Hello, I'm your AI assistant.
给一个 Semi Design 的 Button 组件的使用示例
以下是一个 Semi 代码的使用示例:
jsx
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueMessage as Message, type DialogueRenderConfig } from '@fibrous/semi-ai-solid';
import { Avatar } from '@fibrous/semi-ui-solid';
const defaultMessages: Message[] = [
{
role: 'system',
id: '1',
createdAt: 1715676751919,
content: "Hello, I'm your AI assistant.",
},
{
role: 'user',
id: '2',
createdAt: 1715676751919,
content: '给一个 Semi Design 的 Button 组件的使用示例',
},
{
role: 'assistant',
id: '3',
createdAt: 1715676751919,
content: "以下是一个 Semi 代码的使用示例:\n```jsx \nimport React from 'react';\nimport { Button } from '@fibrous/semi-ui-react';\n\nconst MyComponent = () => {\n return (\n <Button>Click me</Button>\n );\n};\nexport default MyComponent;\n```\n",
},
];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const renderConfig: DialogueRenderConfig = {
renderDialogueTitle: (props) => <div class="semi-ai-chat-dialogue-title">My-{props.role?.name}</div>,
renderDialogueAvatar: (props) => <Avatar src={props.role?.avatar} alt="" size="extra-small" shape="square" />,
renderDialogueAction: (props) => <div class={props.class}>{props.defaultActions?.[0]?.()}</div>,
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
return <AIChatDialogue align="leftRight" mode="bubble" chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} dialogueRenderConfig={renderConfig} />;
}自定义渲染消息内容
通过 renderDialogueContentItem 按照消息类型返回内容渲染,映射的键是内容项的 type。工具调用先按 type 再按 name 索引(如 function_call 下的 create_travel_guide);内容为纯文本的消息用键 default 渲染。渲染函数中可以复用 AIChatDialogue.Reasoning 与 AIChatDialogue.Annotation,见下文 Static Properties。用法如下


代码
import { type JSX, createSignal } from 'solid-js';
import { AIChatDialogue, type AIChatDialogueAnnotation as Annotation, type AIChatDialogueMessage as Message, type DialogueContentItemRendererMap } from '@fibrous/semi-ai-solid';
import { MarkdownRender, Toast } from '@fibrous/semi-ui-solid';
const defaultMessages: Message[] = [
{
id: '1',
role: 'user',
content: '你好',
},
{
id: '2',
role: 'assistant',
content: '你好呀,请问有什么可以帮助你的吗~',
status: 'completed',
},
{
id: '3',
role: 'user',
content: [
{
type: 'message',
role: 'user',
content: [
{
type: 'input_text',
text: '帮我生成类似的图片',
},
{
type: 'input_image',
image_url: '/assets/root-web-sites/edit-bag.jpeg',
file_id: 'demo-file-id',
},
{
type: 'input_image',
image_url: '/assets/root-web-sites/edit-bag.jpeg',
file_id: 'demo-file-id',
},
],
},
],
},
{
id: '4',
role: 'assistant',
content: [
{
type: 'reasoning',
summary: [
{
type: 'summary_text',
text: '\n用户问需要我帮助他生成类似图片,我需要先分析图片内容,然后生成类似的图片...',
},
],
annotations: [
{
title: 'semi.design',
url: 'https://semi.design/',
detail: 'semi design page',
logo: '/assets/placeholder/logo.svg',
},
{
title: 'semi.design',
url: 'https://semi.design/',
detail: 'semi design page',
logo: '/assets/placeholder/logo.svg',
},
],
status: 'completed',
},
{
type: 'function_call',
name: 'create_travel_guide',
arguments: '{\n"city": "北京"\n}',
status: 'completed',
},
],
status: 'completed',
},
];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const userTextStyle: JSX.CSSProperties = {
'background-color': 'var(--semi-color-fill-1)',
color: 'var(--semi-color-text-0)',
'border-radius': '25px',
padding: '6px 16px',
};
const assistantStyle: JSX.CSSProperties = {
color: 'var(--semi-color-text-0)',
padding: '6px 16px',
};
const functionCallStyle: JSX.CSSProperties = {
'background-color': 'var(--semi-color-fill-1)',
padding: '6px 16px',
'border-radius': '25px',
};
const customRender: DialogueContentItemRendererMap = {
function_call: {
create_travel_guide: (item) => {
return (
<div style={functionCallStyle}>
Function Tool Call: {item.name} {item.arguments}
</div>
);
},
},
input_text: (item, message) =>
message?.role === 'user' ? (
<div style={userTextStyle} class="userTextStyle">
{item.text}
</div>
) : (
<div style={assistantStyle}>{item.text}</div>
),
reasoning: (item) => {
// the widget takes its annotations from the item; the item is read here instead of spreading it into the props
const annotations: Annotation[] = item.annotations ?? [];
return (
<AIChatDialogue.Reasoning
status={item.status}
summary={item.summary}
customRenderer={() => (
<>
<AIChatDialogue.Annotation
annotation={annotations}
description="参考资料"
maxCount={3}
onClick={(e) => {
e.stopPropagation();
Toast.success('Ready to open the sidebar!');
}}
/>
<div style={{ 'margin-top': '8px' }}>
<MarkdownRender format="md" raw={item.summary?.[0]?.text ?? ''} />
</div>
</>
)}
/>
);
},
default: (item, message) =>
message?.role === 'user' ? (
<div style={userTextStyle} class="userTextStyle">
{item}
</div>
) : (
<div style={assistantStyle}>{item}</div>
),
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
return <AIChatDialogue chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} renderDialogueContentItem={customRender} />;
}消息数据转换
当前组件的对话消息以 OpenAI 的 Response Object 为原型,为了支持用户更好地无缝集成 Chat Completion API 和 Response API,我们提供了四种 Adapter 转换函数,用户可直接使用该函数转换 API 的返回结果,得到可直接用于消息展示的数据,提供两种 Adapter 用于将 ChatInput 组件的数据处理成适配于 Response API 的 input Message 或者 Chat Completion API 中的 Input Message 格式。
import { chatCompletionToMessage, streamingChatCompletionToMessage, responseToMessage, streamingResponseToMessage, chatInputToMessage, chatInputToChatCompletion } from '@fibrous/semi-ai-solid';
// 将 Chat Completion API 返回的数据转换为 Chat Dialogue 中的 Message 格式
function chatCompletionToMessage(chatCompletion: ChatCompletion): Message[];
// 将 Chat Completion API 流式返回数据转换为 Chat Dialogue 中的 Message 格式
function streamingChatCompletionToMessage(chatCompletionChunks: ChatCompletionChunk[], state?: StreamingChatState): { messages: Message[]; state?: StreamingChatState };
// 将 Response API 返回的数据转换为 Chat Dialogue 中的 Message 格式
function responseToMessage(response: Response): Message;
// 将 Response API 返回流式数据转换为 Chat Dialogue 中的 Message 格式
// ResponseChunk 是按 type 区分的 Response 流式事件联合类型;chunks 为空时返回 null
function streamingResponseToMessage(chunks: ResponseChunk[], prevState?: StreamingResponseState): { message: Message | null; nextState: StreamingResponseState | null } | null;
// 将 Chat Input 数据转换为 Chat Dialogue 中的 Message 格式,(同 Response API Input Message 格式)
// 返回值不含 id,放进 chats 前需要自行补上
function chatInputToMessage(inputContent: MessageContent): Omit<Message, 'id'>;
// 将 Chat Input 数据转换为 Chat Completion API 中的 Input Message 格式
function chatInputToChatCompletion(inputContent: MessageContent): ChatCompletionInput;
比如,当用户使用 Chat Completion API 接口返回非流式数据时,可以通过 chatCompletionToMessage 函数将 Chat Completion Object 转换为 Dialogue Message 消息块格式。注意,因为 Chat Completion API 可以通过 n 来控制每条输入消息生成多少个结果所以该函数的返回值为数组。(注意:如果 n > 1,用户需要自行决定将哪条数据添加到 message 中展示)
Hello! How can I assist you today?
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, chatCompletionToMessage, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-solid';
const CHAT_COMPLETION_DATA = {
id: 'chatcmpl-B9MBs8CjcvOU2jLn4n570S5qMJKcT',
object: 'chat.completion',
created: 1741569952,
model: 'gpt-4.1-2025-04-14',
choices: [
{
index: 0,
message: {
role: 'assistant',
content: 'Hello! How can I assist you today?',
refusal: null,
annotations: [],
tool_calls: [
{
id: 'call_abc123',
type: 'function',
function: {
name: 'get_current_weather',
arguments: '{\n"location": "Boston, MA"\n}',
},
},
],
},
logprobs: null,
finish_reason: 'stop',
},
],
// ...
};
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal<Message[]>(chatCompletionToMessage(CHAT_COMPLETION_DATA));
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
return <AIChatDialogue align="leftRight" mode="bubble" chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />;
}比如,当用户使用 Chat Completion API 接口返回流式数据时,可以通过 streamingChatCompletionToMessage 函数将 Chat Completion Chunk Object List 转换为 Dialogue Message 消息块格式。
代码
import { createSignal, onCleanup, onMount } from 'solid-js';
import { AIChatDialogue, streamingChatCompletionToMessage, type AIChatDialogueMessage as Message, type StreamingChatState } from '@fibrous/semi-ai-solid';
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal<Message[]>([]);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
onMount(() => {
const total = STREAMING_CHAT_COMPLETION_DATA.length;
let i = 1;
let state: StreamingChatState | undefined;
const timer = setInterval(() => {
if (i > total) {
clearInterval(timer);
return;
}
const slice = STREAMING_CHAT_COMPLETION_DATA.slice(0, i);
const { messages: partialMessages, state: nextState } = streamingChatCompletionToMessage(slice, state);
state = nextState;
setMessages([partialMessages[0]]);
i += 1;
}, 100);
onCleanup(() => clearInterval(timer));
});
return <AIChatDialogue align="leftRight" mode="bubble" chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />;
}
const STREAMING_CHAT_COMPLETION_DATA = [
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011843,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 0, delta: { role: 'assistant', content: '', refusal: null }, finish_reason: null }],
obfuscation: 'ahPqlzj6DD',
},
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011843,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 0, delta: { content: '' }, finish_reason: null }],
obfuscation: 'i2PXRIwvc3D',
},
// index 0: output text delta
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011843,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 0, delta: { content: '我正在使用 ' }, finish_reason: null }],
obfuscation: '3sslO5QylW',
},
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011843,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 0, delta: { content: 'streamingChatCompletionToMessage' }, finish_reason: null }],
obfuscation: '3sslO5QylW',
},
// index 1: tool call delta (function_call / tool_calls)
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011845,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [
{
index: 1,
delta: {
tool_calls: [{ index: 0, id: 'call_1', function: { name: 'searchWeather', arguments: '{"city":"北京"' } }],
},
finish_reason: null,
},
],
obfuscation: 'T1',
},
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011846,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [
{
index: 1,
delta: { tool_calls: [{ index: 0, function: { arguments: ',"day":"today"}' } }] },
finish_reason: null,
},
],
obfuscation: 'T2',
},
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011844,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 0, delta: { content: ' 转换 Chat Completion Chunks' }, finish_reason: null }],
obfuscation: 'X1',
},
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011844,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 0, delta: { content: ' 🥳' }, finish_reason: null }],
obfuscation: 'X2',
},
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011843,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
obfuscation: 'n13SLf',
},
{
id: 'chatcmpl-COjljxurV5GKrRUsg1wd7mIyQCiiT',
object: 'chat.completion.chunk',
created: 1760011843,
model: 'o3-mini-2025-01-31',
service_tier: 'default',
system_fingerprint: 'fp_6c43dcef8c',
choices: [{ index: 1, delta: {}, finish_reason: 'stop' }],
obfuscation: 'jt9rDb',
},
];当用户使用 Response API 接口返回非流式数据时,可以通过 responseToMessage 函数将 Response Object 转换为 Dialogue Message 消息块格式。
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
代码
import { createSignal } from 'solid-js';
import { AIChatDialogue, responseToMessage, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-solid';
const RESPONSE_DATA = {
id: 'resp_67ccd3a9da748190baa7f1570fe91ac604becb25c45c1d41',
object: 'response',
created_at: 1741476777,
status: 'completed',
error: null,
incomplete_details: null,
instructions: null,
max_output_tokens: null,
model: 'gpt-4o-2024-08-06',
output: [
{
id: 'rs_6876cf02e0bc8192b74af0fb64b715ff06fa2fcced15a5ac',
type: 'reasoning',
status: 'completed',
summary: [
{
type: 'summary_text',
text: '**用户询问什么是 Semi Design** 用户问 “Semi Design”需整合多源信息。首先发现抖音的 Semi Design 是设计系统,支持多平台且含 Design Token 和代码转换工具。印度 Semi Design 专注半导体培训,但用户可能更关注抖音案例。其他结果涉及半定制设计,但关联性较低。需确认是否有其他解释,但当前信息已覆盖主要维度。虽然继续推理可能提高完备性,但现阶段已足够支撑答案,可以开始输出给用户。',
},
],
},
{
type: 'message',
id: 'msg_67ccd3acc8d48190a77525dc6de64b4104becb25c45c1d41',
status: 'completed',
role: 'assistant',
content: [
{
type: 'output_text',
text: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统',
annotations: [
{
title: 'Semi Design',
url: 'https://semi.design/zh-CN/start/getting-started',
detail: 'Semi Design 快速开始',
logo: '/assets/docs-icon.png',
},
{
title: 'Semi Design',
url: 'https://semi.design/zh-CN/start/getting-started',
detail: 'Semi Design 快速开始',
logo: '/assets/docs-icon.png',
},
{
title: 'Semi Design',
url: 'https://semi.design/zh-CN/start/getting-started',
detail: 'Semi Design 快速开始',
logo: '/assets/docs-icon.png',
},
],
},
],
},
{
id: 'fc_12345xyz',
call_id: 'call_12345xyz',
type: 'function_call',
name: 'get_semi_page',
status: 'completed',
arguments: '{"pageName":"AIChatDialogue"}',
},
],
// ...
};
const defaultMessages: Message[] = [responseToMessage(RESPONSE_DATA)];
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal(defaultMessages);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
return <AIChatDialogue align="leftRight" mode="bubble" chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />;
}当用户使用 Response API 接口返回流式数据时,可以通过 streamingResponseToMessage 函数将 Response Chunk Object List 转换为 Dialogue Message 消息块格式。
代码
import { batch, createEffect, createSignal, onCleanup } from 'solid-js';
import { AIChatDialogue, streamingResponseToMessage, type AIChatDialogueMessage as Message, type ResponseChunk, type StreamingResponseState } from '@fibrous/semi-ai-solid';
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
system: {
name: 'System',
avatar: '/assets/placeholder/logo.svg',
},
};
const FIXED_SHUFFLED_INDICES = [
0,
1,
2,
3,
4,
6, // chunk 5 skipped
6, // chunk 6 arrives twice
7,
5, // chunk 5 arrives late
8,
9,
10,
11,
12,
13,
14,
15,
16,
];
export default function AIChatDialogueDemo() {
const [messages, setMessages] = createSignal<Message[]>([]);
const [currentState, setCurrentState] = createSignal<StreamingResponseState | undefined>(undefined);
const [currentLength, setCurrentLength] = createSignal(0);
const onChatsChange = (chats: Message[]) => {
setMessages(chats);
};
// the state updates are batched: each one re-runs this effect, and a run per update would start a timer per update
createEffect(() => {
const length = currentLength();
const state = currentState();
if (length > FIXED_SHUFFLED_INDICES.length) {
return;
}
const timer = setTimeout(() => {
if (length === 0) {
setCurrentLength(1);
return;
}
const currentIndices = FIXED_SHUFFLED_INDICES.slice(0, length);
const currentChunks = currentIndices.map((index) => REASONING_CHUNKS[index]);
const result = streamingResponseToMessage(currentChunks, state);
batch(() => {
if (result) {
const { message: responseMessage, nextState } = result;
if (responseMessage) {
setMessages([responseMessage]);
setCurrentState(nextState ?? undefined);
}
}
setCurrentLength(length + 1);
});
}, 200);
onCleanup(() => clearTimeout(timer));
});
return <AIChatDialogue align="leftRight" mode="bubble" chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />;
}
const REASONING_CHUNKS: ResponseChunk[] = [
{
type: 'response.created',
sequence_number: 0,
response: {
id: 'resp_reason_001',
object: 'response',
created_at: 1760091777,
status: 'in_progress',
background: false,
error: null,
incomplete_details: null,
instructions: null,
max_output_tokens: null,
max_tool_calls: null,
model: 'o3-mini-2025-01-31',
output: [],
parallel_tool_calls: true,
previous_response_id: null,
prompt_cache_key: null,
reasoning: { effort: 'medium', summary: null },
safety_identifier: null,
service_tier: 'auto',
store: true,
temperature: 1.0,
text: { format: { type: 'text' }, verbosity: 'medium' },
tool_choice: 'auto',
tools: [],
top_logprobs: 0,
top_p: 1.0,
truncation: 'disabled',
usage: null,
user: null,
metadata: {},
},
},
{
type: 'response.in_progress',
sequence_number: 1,
response: {
id: 'resp_reason_001',
object: 'response',
created_at: 1760091777,
status: 'in_progress',
background: false,
error: null,
incomplete_details: null,
instructions: null,
max_output_tokens: null,
max_tool_calls: null,
model: 'o3-mini-2025-01-31',
output: [],
parallel_tool_calls: true,
previous_response_id: null,
prompt_cache_key: null,
reasoning: { effort: 'medium', summary: null },
safety_identifier: null,
service_tier: 'auto',
store: true,
temperature: 1.0,
text: { format: { type: 'text' }, verbosity: 'medium' },
tool_choice: 'auto',
tools: [],
top_logprobs: 0,
top_p: 1.0,
truncation: 'disabled',
usage: null,
user: null,
metadata: {},
},
},
// reasoning item (output index 0)
{
type: 'response.output_item.added',
sequence_number: 2,
output_index: 0,
item: { id: 'rs_reason_001', type: 'reasoning', summary: [] },
},
{
type: 'response.reasoning_summary_part.added',
sequence_number: 3,
output_index: 0,
summary_index: 0,
part: { type: 'reasoning', text: '' },
},
{
type: 'response.reasoning_summary_text.delta',
sequence_number: 4,
output_index: 0,
summary_index: 0,
delta: '思',
},
{
type: 'response.reasoning_summary_text.delta',
sequence_number: 5,
output_index: 0,
summary_index: 0,
delta: '考',
},
{
type: 'response.reasoning_summary_text.delta',
sequence_number: 6,
output_index: 0,
summary_index: 0,
delta: '完',
},
{
type: 'response.reasoning_summary_text.delta',
sequence_number: 7,
output_index: 0,
summary_index: 0,
delta: '成',
},
{
type: 'response.reasoning_summary_text.delta',
sequence_number: 8,
output_index: 0,
summary_index: 0,
delta: '!',
},
{
type: 'response.reasoning_summary_text.done',
sequence_number: 9,
output_index: 0,
summary_index: 0,
text: '思考完成!',
},
{
type: 'response.output_item.done',
sequence_number: 10,
output_index: 0,
item: { id: 'rs_reason_001', type: 'reasoning', summary: [{ type: 'reasoning', text: '思考完成!' }] },
},
// assistant message (output index 1)
{
type: 'response.output_item.added',
sequence_number: 11,
output_index: 1,
item: { id: 'msg_reason_001', type: 'message', status: 'in_progress', content: [], role: 'assistant' },
},
{
type: 'response.content_part.added',
sequence_number: 12,
output_index: 1,
content_index: 0,
part: { type: 'output_text', annotations: [], text: '' },
},
{
type: 'response.output_text.delta',
sequence_number: 13,
output_index: 1,
content_index: 0,
delta: '基于上述思考,',
},
{
type: 'response.output_text.delta',
sequence_number: 14,
output_index: 1,
content_index: 0,
delta: '结论如下:',
},
{
type: 'response.output_text.done',
sequence_number: 15,
output_index: 1,
content_index: 0,
text: '基于上述思考,结论如下:...',
},
{
type: 'response.completed',
sequence_number: 16,
response: {
id: 'resp_reason_001',
object: 'response',
created_at: 1760091777,
status: 'completed',
background: false,
error: null,
incomplete_details: null,
instructions: null,
max_output_tokens: null,
max_tool_calls: null,
model: 'o3-mini-2025-01-31',
output: [
{ id: 'rs_reason_001', type: 'reasoning', summary: [{ type: 'reasoning', text: '思考完成!' }] },
{
id: 'msg_reason_001',
type: 'message',
status: 'completed',
content: [{ type: 'output_text', annotations: [], text: '基于上述思考,结论如下:...' }],
role: 'assistant',
},
],
parallel_tool_calls: true,
previous_response_id: null,
prompt_cache_key: null,
reasoning: { effort: 'medium', summary: null },
safety_identifier: null,
service_tier: 'default',
store: true,
temperature: 1.0,
text: { format: { type: 'text' }, verbosity: 'medium' },
tool_choice: 'auto',
tools: [],
top_logprobs: 0,
top_p: 1.0,
truncation: 'disabled',
usage: {
input_tokens: 12,
input_tokens_details: { cached_tokens: 0 },
output_tokens: 120,
output_tokens_details: { reasoning_tokens: 16 },
total_tokens: 132,
},
user: null,
metadata: {},
},
},
];API 参考
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| align | 对话布局方式,可选 leftRight(左右分布)或 leftAlign(左对齐)。 | 'leftRight' | 'leftAlign' | "leftRight" |
| chats | 受控对话消息列表,配合 onChatsChange 使用。 | Message[] | - |
| dialogueRenderConfig | 自定义各区块的渲染,可替换头像、标题、内容、操作栏或整条对话。 | DialogueRenderConfig | - |
| disabledFileItemClick | 是否禁用文件卡片的点击。 | boolean | false |
| escapeHtml | 是否转义用户消息中的 HTML 标签,避免被 Markdown 解析器当作 HTML 处理而丢失内容。 | boolean | true |
| hintCls | 提示区最外层的类名。 | string | - |
| hints | 提示信息。点击某条提示后,它成为新的用户消息,并触发 onHintClick。 | string[] | - |
| hintStyle | 提示区最外层的样式。 | JSX.CSSProperties | string | - |
| markdownRenderProps | 透传给每条消息所用 MarkdownRender 的属性,raw 由消息决定,不可传入;components 与内置组件合并,同名的(如 code)覆盖内置组件。 | MarkdownRenderProps | - |
| messageEditRender | 用户消息处于编辑状态时显示的编辑器,参数为消息转换得到的输入内容。 | Type(props: MessageContent) => JSX.Element | - |
| mode | 对话模式,可选 bubble、noBubble 或 userBubble。 | 'bubble' | 'noBubble' | 'userBubble' | "bubble" |
| onAnnotationClick | 点击参考来源时调用,参数为被点击消息块的注释数组。 | Type(annotations: Annotation[]) => void | - |
| onChatsChange | 消息列表变化时调用,参数为新的消息数组。 | Type(chats: Message[]) => void | - |
| onFileClick | 点击文件卡片时调用,参数为该文件。 | Type(file?: InputFile) => void | - |
| onHintClick | 点击提示词时调用,参数为该提示文本。 | Type(hint: string) => void | - |
| onImageClick | 点击图片时调用,参数为该图片。 | Type(image?: InputImage) => void | - |
| onMessageBadFeedback | 对消息点踩时调用,参数为该消息。 | Type(message?: Message) => void | - |
| onMessageCopy | 复制消息时调用:文本写入剪贴板成功后才调用,复制的文本不含推理内容;没有可复制的文本时既不写入也不调用。 | Type(message?: Message) => void | - |
| onMessageDelete | 删除消息时调用,参数为该消息。 | Type(message?: Message) => void | - |
| onMessageEdit | 编辑消息时调用,参数为该消息。 | Type(message?: Message) => void | - |
| onMessageGoodFeedback | 对消息点赞时调用,参数为该消息。 | Type(message?: Message) => void | - |
| onMessageReset | 重置消息时调用,参数为该消息。 | Type(message?: Message) => void | - |
| onMessageShare | 分享消息时调用,参数为该消息。 | Type(message?: Message) => void | - |
| onReferenceClick | 引用按钮点击时调用。参数没有 id,加入 AIChatInput 的 references 前由使用方补上;文件项没有 type。 | Type(item: Partial<Reference>) => void | - |
| onSelect | 选中的消息变化时调用,参数为选中消息的 id 数组。 | Type(selectedIds: string[]) => void | - |
| ref | 接收根元素 div,其上带有下方 Methods 表中的方法。 | TypeRef<AIChatDialogueElement> | - |
| renderDialogueContentItem | 按内容项类型返回渲染内容的映射。嵌套的工具调用按名称索引,纯文本消息使用键 default。 | DialogueContentItemRendererMap | - |
| renderHintBox | 自定义提示项的渲染,参数含提示文本 content、序号 index 与点击回调 onHintClick。 | Type(props: RenderHintBoxProps) => JSX.Element | - |
| roleConfig | 各角色(user、assistant、system 等)的名称、头像与头像颜色,必填,不读取全局默认值。 | RoleConfig | - |
| selecting | 是否开启多选模式。 | boolean | false |
| showReference | 是否在文字或文件消息中展示可被引用的图标,仅对用户消息生效。 | boolean | false |
| showReset | 是否展示重置操作。 | boolean | true |
Methods
| 方法 | 说明 | 类型 |
|---|---|---|
| deselectAll | 取消全选所有消息。 | Type() => void |
| scrollToBottom | 滚动到最底部,参数为 true 时带动画。 | Type(animation: boolean) => void |
| scrollToTop | 滚动到最顶部,参数为 true 时带动画。 | Type(animation: boolean) => void |
| selectAll | 全选所有消息。 | Type() => void |
class、style、ref 与原生属性落在根元素 div 上。根元素带 aria-label 或 aria-labelledby 且没有传 role 时,role 为 group,使用方传入的 role 优先。消息列表是带名称的 role="log" 区域,回复生成期间带 aria-busy="true"。roleConfig 是必填 prop,不读取全局默认值。
RoleConfig
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| user | 用户信息 | Metadata | Map<string, Metadata> | - |
| assistant | 助手信息 | Metadata | Map<string, Metadata> | - |
| system | 系统信息 | Metadata | Map<string, Metadata> | - |
MetaData
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| name | 名称 | string | - |
| avatar | 头像 | string | - |
| color | 头像背景色,同 Avatar 组件的 color 参数, 支持 amber、 blue、 cyan、 green、 grey、 indigo、 light-blue、 light-green、 lime、 orange、 pink、 purple、 red、 teal、 violet、 yellow | AvatarColor | grey |
Message
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| role | 角色 | string | - |
| name | 名称 | string | - |
| id | 唯一标识 | string | - |
| content | 消息内容 | string| ContentItem[] | - |
| createdAt | 创建时间 | number | - |
| model | 模型名称 | string | - |
| status | 消息状态,可选值为 queued | in_progress | incomplete | completed | failed | cancelled | string | completed |
| editing | 是否处于编辑状态,由组件在编辑时写入 | boolean | - |
| like | 是否已点赞,由组件在点赞时写入 | boolean | - |
| dislike | 是否已点踩,由组件在点踩时写入 | boolean | - |
| references | 当前消息引用的文件或文本 | Reference[] | - |
| error | 流式响应的错误信息,由 streamingResponseToMessage 写入 | { code?: string; message?: string } | null | - |
Reference
与 AIChatInput 的 Reference 是同一类型,id、type 必填,其他字段原样保留。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| id | 唯一标识,必填 | string | - |
| type | 类型,必填 | string | - |
| name | 名称 | string | - |
| url | 地址 | string | - |
| content | 文本内容 | string | - |
Methods
方法列在上方 API 参考的 Methods 表中。通过 ref 拿到根元素后调用:
import { AIChatDialogue, type AIChatDialogueElement } from '@fibrous/semi-ai-solid';
let dialogue: AIChatDialogueElement | undefined;
const toBottom = () => dialogue?.scrollToBottom(true);
<AIChatDialogue ref={(element) => (dialogue = element)} chats={messages()} roleConfig={roleConfig} onChatsChange={onChatsChange} />;
Static Properties
| 属性 | 说明 | 类型 |
|---|---|---|
| defaultComponents | 默认的 Markdown 渲染组件集合,code 是增强版的代码块组件,支持代码语言标识和复制功能,可用于 markdownRenderProps.components 的二次封装 | { code: Component } |
| Reasoning | 推理块组件,API 见下方 AIChatDialogue.Reasoning | Component |
| Step | 步骤展示组件,API 见下方 AIChatDialogue.Step | Component |
| Annotation | 参考来源组件,API 见下方 AIChatDialogue.Annotation | Component |
当需要在自定义代码块渲染时保留默认功能,可以把默认组件包进自己的组件,再通过 markdownRenderProps.components 传入:
import type { ComponentProps } from 'solid-js';
import { AIChatDialogue } from '@fibrous/semi-ai-solid';
const DefaultCode = AIChatDialogue.defaultComponents.code;
function CustomCode(props: ComponentProps<typeof DefaultCode>) {
// 可以在此处添加自定义逻辑
return <DefaultCode {...props} />;
}
<AIChatDialogue chats={messages()} roleConfig={roleConfig} markdownRenderProps={{ components: { code: CustomCode } }} />;
AIChatDialogue.Reasoning
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| content | 推理内容,summary 为空时按换行拼接其中的文本作为推理正文。 | { text?: string; type?: string }[] | - |
| customRenderer | 替换推理正文的渲染。参数为推理组件的全部属性,返回的内容放在正文区域中。 | Type(props: ReasoningWidgetProps) => JSX.Element | - |
| markdownRenderProps | 透传给推理正文所用 MarkdownRender 的属性,raw 由推理正文决定。 | MarkdownRenderProps | - |
| status | 推理状态。为 completed 时标题显示已完成,且初次渲染时折叠;其他值显示思考中并展开。 | string | - |
| summary | 推理摘要,非空时按换行拼接其中的文本作为推理正文。 | { text?: string; type?: string }[] | - |
class、style、ref 与原生属性落在根元素 div 上。
AIChatDialogue.Step
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| steps | 步骤列表,必填。每个步骤含 summary、status 与 actions,status 为 completed 时显示完成图标,否则显示加载中;actions 每项含 summary、description 与 icon,步骤有操作时可点击或用 Enter、空格展开收起。 | Step[] | - |
class、style、ref 与原生属性落在根元素 div 上。
AIChatDialogue.Annotation
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| annotation | 参考来源列表,每项可含 title、logo、url、detail。必填。 | AnnotationItemProps[] | Annotation[] | - |
| description | 来源图标后的描述文字,不传时显示来源数量。 | string | - |
| maxCount | 最多显示的来源图标数,超出部分显示为 +N。必填。 | number | - |
| onClick | 点击或用 Enter、空格激活时调用,参数为事件与参考来源列表。 | Type(e: MouseEvent | KeyboardEvent, item: AnnotationItemProps[]) => void | - |
class、style、ref 与原生属性落在根元素 div 上。
ContentItem
ContentItem 支持所有 OpenAI Response InputItem 和 OutputItem 类型,具体类型定义如下
type ContentItem = InputContentItem | OutputContentItem;
type InputContentItem = InputMessage | ItemReference;
type OutputContentItem = OutputMessage | MCPToolCall | Reasoning FileSearchToolCall |
WebSearchToolCall | FunctionToolCall | CustomToolCall | ImageGenerationCall| CustomObject;
interface CommonContentItem {
id?: string;
type?: string;
status?: string;
role?: string
}
interface CustomObject {
[key: string]: any
}
// 输入条目
interface InputMessage extends CommonContentItem {
content?: string | (InputText | InputImage | InputFile)[]
}
interface InputText extends CommonContentItem {
text?: string
}
interface InputImage extends CommonContentItem {
detail?: string;
file_id?: string;
image_url?: string
}
interface InputFile extends CommonContentItem {
file_id?: string;
file_data?: string;
file_url?: string;
filename?: string;
size?: string;
file_type?: string;
fileInstance?: any
}
interface ItemReference extends CommonContentItem {
file_id?: string
}
// 输出条目
interface OutputMessage extends CommonContentItem {
content?: (OutputText | Refusal)[]
}
interface OutputText {
text?: string;
type?: string;
annotations?: Annotation[]
}
interface Refusal extends CommonContentItem {
refusal?: string;
type?: string
}
type Annotation = URLCitation | CustomObject;
interface URLCitation {
end_index?: number;
start_index?: number;
title?: string;
type?: string;
url?: string
}
interface Reasoning extends CommonContentItem {
summary?: {
text?: string;
type?: string
}[];
content?: {
text?: string;
type?: string
}[]
}
interface FileSearchToolCallResult {
attributes?: Map<string, string>;
score?: number;
file_id?: string;
filename?: string;
text?: string
}
interface FileSearchToolCall extends CommonContentItem {
queries?: string[];
results?: FileSearchToolCallResult[]
}
interface WebSearchToolCall extends CommonContentItem {
action?: SearchAction | OpenPageAction | FindAction
}
interface SearchAction {
type?: string;
query?: string;
sources?: { type: string; url: string }[]
}
interface OpenPageAction {
type?: string;
url?: string
}
interface FindAction {
action?: string;
type?: string;
query?: string
}
interface FunctionToolCall extends CommonContentItem {
call_id?: string;
name?: string;
arguments?: string
}
interface CustomToolCall extends CommonContentItem {
call_id?: string;
name?: string;
input?: string
}
interface ImageGenerationCall extends CommonContentItem {
result?: string
}
interface MCPToolCall extends CommonContentItem {
arguments?: string;
server_label?: string;
name?: string;
result?: string;
output?: string
}
interface FileUploadToolCall extends CommonContentItem {
result?: string
}
设计变量
| Sass 变量 | 值 | 用法 |
|---|---|---|
| $color-aiChatDialogue_wrapper-selected-bg | var(--semi-color-fill-0) | 外层选中背景色 |
| $color-aiChatDialogue_loading_circle_first-bg | rgba(83, 56, 255, 1) | 第一个加载圆点背景色 |
| $color-aiChatDialogue_loading_circle_second-bg | rgba(176, 48, 240, 1) | 第二个加载圆点背景色 |
| $color-aiChatDialogue_loading_circle_third-bg | rgba(231, 45, 255, 1) | 第三个加载圆点背景色 |
| $color-aiChatDialogue_loading_text | var(--semi-color-text-3) | 加载中文本颜色 |
| $color-aiChatDialogue_bubble-bg | var(--semi-color-fill-0) | 对话气泡背景色 |
| $color-aiChatDialogue_file-bg | var(--semi-color-fill-0) | 文件组件背景色 |
| $color-aiChatDialogue_file_title-text | var(--semi-color-text-0) | 文件标题文本颜色 |
| $color-aiChatDialogue_file_metadata-text | var(--semi-color-text-2) | 文件元数据文本颜色 |
| $color-aiChatDialogue_tool_call-bg | var(--semi-color-fill-0) | 工具调用组件背景色 |
| $color-aiChatDialogue_tool_call-text | var(--semi-color-text-2) | 工具调用组件文本颜色 |
| $color-aiChatDialogue_action_dropdown-text | var(--semi-color-danger) | 操作区下拉菜单文本颜色 |
| $color-aiChatDialogue_reasoning-border | var(--semi-color-border) | 推理组件边框色 |
| $color-aiChatDialogue_step-border | var(--semi-color-border) | 步骤组件边框色 |
| $color-aiChatDialogue_step_action-bg | var(--semi-color-fill-0) | 步骤组件 action 背景色 |
| $color-aiChatDialogue_step_completed-text | var(--semi-color-success) | 步骤组件完成文本颜色 |
| $color-aiChatDialogue_step_suffix-text | var(--semi-color-text-1) | 步骤组件后缀文本颜色 |
| $color-aiChatDialogue_step_action-text | var(--semi-color-text-2) | 步骤组件 action 文本颜色 |
| $color-aiChatDialogue_annotation-border | var(--semi-color-border) | 参考来源组件边框色 |
| $color-aiChatDialogue_annotation-bg-hover | var(--semi-color-fill-0) | 参考来源组件悬停背景色 |
| $color-aiChatDialogue_annotation-text | var(--semi-color-text-2) | 参考来源组件文本颜色 |
| $color-aiChatDialogue_reference-bg | var(--semi-color-fill-0) | 引用组件背景色 |
| $color-aiChatDialogue_reference-text | var(--semi-color-text-2) | 引用组件文本颜色 |
| $color-aiChatDialogue_icon_reference-text | var(--semi-color-text-0) | 用户消息引用图标颜色 |
| $color-aiChatDialogue_code-border | var(--semi-color-border) | 代码块边框色 |
| $color-aiChatDialogue_code_topSlot-bg | var(--semi-color-fill-0) | 代码块顶部槽背景色 |
| $color-aiChatDialogue_code_topSlot-text | var(--semi-color-text-0) | 代码块顶部槽文本颜色 |
| $color-aiChatDialogue_hint-bg | var(--semi-color-bg-0) | 提示项背景色 |
| $color-aiChatDialogue_hint-border | var(--semi-color-border) | 提示项边框色 |
| $color-aiChatDialogue_hint-bg-hover | var(--semi-color-fill-0) | 提示项悬停背景色 |
| $color-aiChatDialogue_hint-text | var(--semi-color-text-0) | 提示项文本颜色 |
| $color-aiChatDialogue_code_icon-bg | var(--semi-teal-4) | code icon 背景色 |
| $color-aiChatDialogue_word_icon-bg | var(--semi-blue-4) | word icon 背景色 |
| $color-aiChatDialogue_excel_icon-bg | var(--semi-green-4) | excel icon 背景色 |
| $color-aiChatDialogue_pdf_icon-bg | var(--semi-red-4) | pdf icon 背景色 |
| $color-aiChatDialogue_video_icon-bg | var(--semi-purple-4) | video icon 背景色 |
| $color-aiChatDialogue_default_icon-bg | var(--semi-grey-4) | default icon 背景色 |
| $color-aiChatDialogue_icon-text | var(--semi-color-white) | icon 文本颜色 |
| $color-aiChatDialogue_failed | var(--semi-color-danger) | 失败提示颜色 |
| $color-aiChatDialogue_code_content-bg | var(--semi-color-bg-0) | code content 背景色 |
| $width-aiChatDialogue_wrapper | 100% | 外层宽度 |
| $width-aiChatDialogue_inner | 100% | 气泡右侧展示内层宽度 |
| $width-aiChatDialogue_backBottom_button | 42px | 返回按钮宽度 |
| $height-aiChatDialogue_backBottom_button | 42px | 返回按钮高度 |
| $width-aiChatDialogue_avatar | 24px | 头像宽度 |
| $height-aiChatDialogue_avatar | 24px | 头像高度 |
| $width-aiChatDialogue_content_editing | 100% | 内容宽度 - 编辑状态 |
| $width-aiChatDialogue_content_inner | 100% | 内容内层宽度 |
| $width-aiChatDialogue_content_max | 100% | 内容最大宽度 |
| $width-aiChatDialogue_bubble_max | 100% | 气泡最大宽度 |
| $width-aiChatDialogue_loading_circle | 3px | 加载圆点宽度 |
| $height-aiChatDialogue_loading_circle | 3px | 加载圆点高度 |
| $width-aiChatDialogue_img | 256px | 图片宽度 |
| $height-aiChatDialogue_img | 256px | 图片高度 |
| $width-aiChatDialogue_img_list | 128px | 图片列表项宽度 |
| $height-aiChatDialogue_img_list | 128px | 图片列表项高度 |
| $width-aiChatDialogue_file | 240px | 文件组件宽度(含内边距) |
| $width-aiChatDialogue_file_icon_wrapper | 36px | 文件图标外层宽度 |
| $height-aiChatDialogue_file_icon_wrapper | 36px | 文件图标外层高度 |
| $width-aiChatDialogue_action_btn | 24px | 操作按钮宽度 |
| $height-aiChatDialogue_action_btn | 24px | 操作按钮高度 |
| $width-aiChatDialogue_reasoning | 100% | 推理组件宽度 |
| $width-aiChatDialogue_reasoning_wrapper | 100% | 推理组件外层宽度 |
| $width-aiChatDialogue_reasoning-border | 1px | 推理组件 border 宽度 |
| $width-aiChatDialogue_reasoning_prefix | 20px | 推理前缀图标宽度 |
| $height-aiChatDialogue_reasoning_prefix | 20px | 推理前缀图标高度 |
| $width-aiChatDialogue_step-border | 1px | 步骤组件 border 宽度 |
| $width-aiChatDialogue_step_prefix | 20px | 步骤前缀图标宽度 |
| $height-aiChatDialogue_step_prefix | 20px | 步骤前缀图标高度 |
| $width-aiChatDialogue_step_suffix | 20px | 步骤后缀图标宽度 |
| $height-aiChatDialogue_step_suffix | 20px | 步骤后缀图标高度 |
| $width-aiChatDialogue_step_line | 1px | 步骤左侧线宽度 |
| $width-aiChatDialogue_annotation | 100% | 参考来源宽度 |
| $width-aiChatDialogue_annotation-border | 1px | 参考来源组件 border 宽度 |
| $width-aiChatDialogue_annotation_logo | 16px | 参考来源logo宽度 |
| $height-aiChatDialogue_annotation_logo | 16px | 参考来源logo高度 |
| $width-aiChatDialogue_reference_icon | 16px | 引用图标宽度 |
| $height-aiChatDialogue_reference_icon | 16px | 引用图标高度 |
| $width-aiChatDialogue_reference_max | 200px | 引用组件最大宽度 |
| $width-aiChatDialogue_reference_name_max | 130px | 引用名称最大宽度 |
| $width-aiChatDialogue_code-border | 1px | 代码块组件 border 宽度 |
| $width-aiChatDialogue_hint-border | 1px | 提示项组件 border 宽度 |
| $width-aiChatDialogue_file_title_max | 180px | 文件标题最大宽度 |
| $width-aiChatDialogue_file_title_max-hover | 140px | 用户消息悬停时文件标题最大宽度(显示引用图标时) |
| $width-aiChatDialogue_action_copy | 24px | 复制按钮宽度 |
| $spacing-aiChatDialogue_wrapper-paddingY | 8px | 最外层垂直内边距 |
| $spacing-aiChatDialogue_wrapper-paddingX | 16px | 最外层水平内边距 |
| $spacing-aiChatDialogue_wrapper-marginTop | 2px | 最外层顶部外边距 |
| $spacing-aiChatDialogue_wrapper-columnGap | 16px | 最外层列间距 |
| $spacing-aiChatDialogue_container-columnGap | 12px | container 容器列间距 |
| $spacing-aiChatDialogue_content_failed-marginRight | 8px | 内容失败提示右外边距 |
| $spacing-aiChatDialogue_content_bubble-marginTop | 8px | 内容气泡顶部外边距 |
| $spacing-aiChatDialogue_content_noBubble-marginTop | 8px | 内容无气泡顶部外边距 |
| $spacing-aiChatDialogue_bubble-paddingY | 12px | 气泡垂直内边距 |
| $spacing-aiChatDialogue_bubble-paddingX | 16px | 气泡水平内边距 |
| $spacing-aiChatDialogue_content_loading-marginTop | 8px | 内容加载顶部外边距 |
| $spacing-aiChatDialogue_loading_item-marginY | 1px | 加载项垂直外边距 |
| $spacing-aiChatDialogue_loading_item-marginX | 1px | 加载项水平外边距 |
| $spacing-aiChatDialogue_loading_text-marginLeft | 8px | 加载文本左外边距 |
| $spacing-aiChatDialogue_img-marginTop | 8px | 图片顶部外边距 |
| $spacing-aiChatDialogue_img_list-marginRight | 4px | 图片列表右外边距 |
| $spacing-aiChatDialogue_file-paddingY | 8px | 文件组件垂直内边距 |
| $spacing-aiChatDialogue_file-paddingX | 8px | 文件组件水平内边距 |
| $spacing-aiChatDialogue_file-columnGap | 8px | 文件组件列间距 |
| $spacing-aiChatDialogue_file-marginTop | 8px | 文件组件顶部外边距 |
| $spacing-aiChatDialogue_file-marginRight | 8px | 文件组件右外边距 |
| $spacing-aiChatDialogue_tool_call-paddingY | 6px | 工具调用垂直内边距 |
| $spacing-aiChatDialogue_tool_call-paddingX | 8px | 工具调用水平内边距 |
| $spacing-aiChatDialogue_tool_call-columnGap | 4px | 工具调用列间距 |
| $spacing-aiChatDialogue_tool_call-marginTop | 8px | 工具调用顶部外边距 |
| $spacing-aiChatDialogue_custom_renderer-marginTop | 8px | 自定义渲染器顶部外边距 |
| $spacing-aiChatDialogue_action-marginTop | 8px | 操作顶部外边距 |
| $spacing-aiChatDialogue_reasoning_wrapper-marginTop | 8px | 推理组件外层顶部外边距 |
| $spacing-aiChatDialogue_reasoning_header-padding | 16px | 推理头部内边距 |
| $spacing-aiChatDialogue_reasoning_content-paddingY | 12px | 推理内容垂直内边距 |
| $spacing-aiChatDialogue_reasoning_content-paddingX | 16px | 推理内容水平内边距 |
| $spacing-aiChatDialogue_step_wrapper-marginTop | 4px | 步骤外层顶部外边距 |
| $spacing-aiChatDialogue_step-paddingY | 6px | 步骤垂直内边距 |
| $spacing-aiChatDialogue_step-paddingX | 12px | 步骤水平内边距 |
| $spacing-aiChatDialogue_step-marginTop | 12px | 步骤顶部外边距 |
| $spacing-aiChatDialogue_step_prefix-marginRight | 6px | 步骤前缀右外边距 |
| $spacing-aiChatDialogue_step_suffix-marginLeft | 8px | 步骤后缀左外边距 |
| $spacing-aiChatDialogue_step_panel-gap | 12px | 步骤面板间距 |
| $spacing-aiChatDialogue_step_line-marginLeft | 20px | 步骤连接线左外边距 |
| $spacing-aiChatDialogue_step_action-marginTop | 12px | 步骤 action 顶部外边距 |
| $spacing-aiChatDialogue_step_action_summary-marginBottom | 4px | 步骤 action 摘要底部外边距 |
| $spacing-aiChatDialogue_step_action_desc-paddingY | 6px | 步骤 action 描述垂直内边距 |
| $spacing-aiChatDialogue_step_action_desc-paddingX | 8px | 步骤 action 描述水平内边距 |
| $spacing-aiChatDialogue_step_action_desc-columnGap | 4px | 步骤 action 描述列间距 |
| $spacing-aiChatDialogue_annotation_wrapper-marginTop | 8px | 参考来源外层顶部外边距 |
| $spacing-aiChatDialogue_annotation_wrapper-paddingY | 8px | 参考来源外层垂直内边距 |
| $spacing-aiChatDialogue_annotation_wrapper-paddingX | 12px | 参考来源外层水平内边距 |
| $spacing-aiChatDialogue_annotation_content-columnGap | 8px | 参考来源内容列间距 |
| $spacing-aiChatDialogue_references-marginTop | 8px | 引用顶部外边距 |
| $spacing-aiChatDialogue_references-columnGap | 4px | 引用列间距 |
| $spacing-aiChatDialogue_references-rowGap | 4px | 引用行间距 |
| $spacing-aiChatDialogue_reference-paddingY | 8px | 引用垂直内边距 |
| $spacing-aiChatDialogue_reference-paddingX | 12px | 引用水平内边距 |
| $spacing-aiChatDialogue_reference-columnGap | 8px | 引用列间距 |
| $spacing-aiChatDialogue_reference_icon-marginRight | 4px | 引用图标右外边距 |
| $spacing-aiChatDialogue_reference_img-marginRight | 4px | 引用图片右外边距 |
| $spacing-aiChatDialogue_icon_reference-paddingY | 0px | 用户消息引用图标垂直内边距 |
| $spacing-aiChatDialogue_icon_reference-paddingX | 8px | 用户消息引用图标水平内边距 |
| $spacing-aiChatDialogue_code_topSlot-paddingY | 8px | 代码块顶部槽垂直内边距 |
| $spacing-aiChatDialogue_code_topSlot-paddingX | 16px | 代码块顶部槽水平内边距 |
| $spacing-aiChatDialogue_hints-rowGap | 8px | 提示行间距 |
| $spacing-aiChatDialogue_hints-marginTop | 4px | 提示顶部外边距 |
| $spacing-aiChatDialogue_hints-marginBottom | 12px | 提示底部外边距 |
| $spacing-aiChatDialogue_hints-marginLeft | 52px | 提示左外边距 |
| $spacing-aiChatDialogue_hint_item-paddingY | 8px | 提示项垂直内边距 |
| $spacing-aiChatDialogue_hint_item-paddingX | 16px | 提示项水平内边距 |
| $spacing-aiChatDialogue_hints_selecting-marginLeft | 68px | 提示项选择状态下左外边距 |
| $radius-aiChatDialogue_backBottom_button | 50% | 返回按钮圆角 |
| $radius-aiChatDialogue_wrapper_selected | 16px | 外层选中圆角 |
| $radius-aiChatDialogue_loading_circle | var(--semi-border-radius-circle) | 加载圆点圆角 |
| $radius-aiChatDialogue_bubble | var(--semi-border-radius-large) | 对话气泡圆角 |
| $radius-aiChatDialogue_img | var(--semi-border-radius-medium) | 图片圆角 |
| $radius-aiChatDialogue_file | var(--semi-border-radius-medium) | 文件组件圆角 |
| $radius-aiChatDialogue_icon_wrapper | var(--semi-border-radius-extra-small) | 图标外层圆角 |
| $radius-aiChatDialogue_tool_call | var(--semi-border-radius-medium) | 工具调用组件圆角 |
| $radius-aiChatDialogue_reasoning_wrapper | var(--semi-border-radius-large) | 推理组件外层圆角 |
| $radius-aiChatDialogue_step | var(--semi-border-radius-medium) | 步骤组件圆角 |
| $radius-aiChatDialogue_action_desc | var(--semi-border-radius-medium) | 操作描述圆角 |
| $radius-aiChatDialogue_annotation_wrapper | var(--semi-border-radius-medium) | 参考来源外层圆角 |
| $radius-aiChatDialogue_reference | var(--semi-border-radius-medium) | 引用组件圆角 |
| $radius-aiChatDialogue_reference_icon | 2px | 引用图标圆角 |
| $radius-aiChatDialogue_code | var(--semi-border-radius-large) | 代码块圆角 |
| $radius-aiChatDialogue_hint_item | var(--semi-border-radius-medium) | 提示项圆角 |
| $font-aiChatDialogue_title-fontSize | $font-size-header-6 | 标题字体大小 |
| $font-aiChatDialogue_title-fontWeight | $font-weight-regular | 标题字体粗细 |
| $font-aiChatDialogue_title-lineHeight | 20px | 标题行高 |
| $font-aiChatDialogue_content-fontSize | $font-size-regular | 内容字体大小 |
| $font-aiChatDialogue_loading_text-fontSize | $font-size-regular | 加载文本字体大小 |
| $font-aiChatDialogue_file_title-fontSize | $font-size-header-6 | 文件标题字体大小 |
| $font-aiChatDialogue_file_metadata-fontSize | $font-size-regular | 文件元数据字体大小 |
| $font-aiChatDialogue_reasoning_header_title-fontWeight | $font-weight-bold | 推理头部标题字体粗细 |
| $font-aiChatDialogue_reasoning_header_title-lineHeight | 22px | 推理头部标题行高 |
| $font-aiChatDialogue_dropdown_icon-fontSize | $font-size-header-6 | 下拉菜单图标字体大小 |
| $font-aiChatDialogue_step-fontSize | $font-size-regular | 步骤字体大小 |
| $font-aiChatDialogue_step-lineHeight | 20px | 步骤行高 |
| $font-aiChatDialogue_step_action-fontSize | $font-size-regular | 步骤 action 字体大小 |
| $font-aiChatDialogue_step_action-lineHeight | 20px | 步骤 action 行高 |
| $font-aiChatDialogue_annotation_content-fontSize | $font-size-regular | 参考来源内容字体大小 |
| $font-aiChatDialogue_references-fontSize | 12px | 引用字体大小 |
| $font-aiChatDialogue_references-lineHeight | $font-size-header-6 | 引用行高 |
| $font-aiChatDialogue_code_topSlot-fontSize | $font-size-regular | 代码块顶部槽字体大小 |
| $font-aiChatDialogue_code-lineHeight | 32px | 代码块行高 |
| $font-aiChatDialogue_hint_content-fontSize | $font-size-regular | 提示内容字体大小 |
| $z-aiChatDialogue_backBottom | 1 | 返回按钮z-index |