使用场景
AIChatDialogue 组件可搭配 AIChatInput 使用,实现更丰富的、功能覆盖更全面、定制更加便捷的 AI 会话场景。组件消息格式以 OpenAI 的 Response Object 为原型,默认支持 OpenAI 社区 Response / Chat Completion 格式标准,对 GPT-5、GPT-4o 系列模型的响应均支持开箱即用,详见消息数据转换。
代码演示
如何引入
<script setup lang="ts">
import { AIChatDialogue } from '@fibrous/semi-ai-vue';
</script>
基本用法
通过 v-model:chats 实现基础对话显示和交互:消息列表变化时组件触发 update:chats(同时触发 chatsChange,载荷相同)。组件也检测对 chats 的就地修改:向 reactive 数组 push 新消息、往最后一条消息的文本里追加内容,都会触发 chatsChange 并跟随滚动;只改 content 数组深处的文本、或单独改 status 会重新渲染,但不触发 chatsChange,也不跟随滚动,此时整体替换该消息或整个数组。
使用 align 属性可以设置对话的布局,支持左右分布(leftRight, 默认)和左对齐(leftAlign)。
Hello, I'm your AI assistant.
给一个 Semi Design 的 Button 组件的使用示例
以下是一个 Semi 代码的使用示例:
jsx
代码
<script setup lang="ts">
import { ref, shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message, type AIChatDialogueProps } from '@fibrous/semi-ai-vue';
import { Radio, RadioGroup, type RadioChangeEvent } from '@fibrous/semi-ui-vue';
type Mode = NonNullable<AIChatDialogueProps['mode']>;
type Align = NonNullable<AIChatDialogueProps['align']>;
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 messages = shallowRef<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 mode = ref<Mode>('bubble');
const align = ref<Align>('leftRight');
const onModeChange = (e: RadioChangeEvent) => {
mode.value = e.target.value as Mode;
};
const onAlignChange = (e: RadioChangeEvent) => {
align.value = e.target.value as Align;
};
</script>
<template>
<span :style="{ display: 'flex', flexDirection: 'column', rowGap: '8px' }">
<span :style="{ display: 'flex', alignItems: 'center', columnGap: '10px' }">
模式
<RadioGroup aria-label="模式" :modelValue="mode" type="button" @change="onModeChange">
<Radio value="bubble">气泡</Radio>
<Radio value="noBubble">非气泡</Radio>
<Radio value="userBubble">用户会话气泡</Radio>
</RadioGroup>
</span>
<span :style="{ display: 'flex', alignItems: 'center', columnGap: '10px' }">
会话布局方式
<RadioGroup aria-label="会话布局方式" :modelValue="align" type="button" @change="onAlignChange">
<Radio value="leftRight">左右分布</Radio>
<Radio value="leftAlign">左对齐</Radio>
</RadioGroup>
</span>
</span>
<div
:style="{
border: '1px solid var(--semi-color-border)',
borderRadius: '12px',
marginTop: '10px',
padding: '20px',
}"
>
<AIChatDialogue :key="align + mode" v-model:chats="messages" :align="align" :mode="mode" :roleConfig="roleConfig" />
</div>
</template>自动滚动
对话区高度固定时,新消息和流式输出的内容会让列表保持在底部。任何向上的滚动(滚轮、拖动滚动条、键盘)都会停止跟随;滚回底部或发送新消息后恢复。在 chats 头部插入更早的消息(如滚到顶部时加载历史)不会滚动列表,已显示的消息保持在原位置。
点击下方按钮发送消息,回复会以流式方式逐步生成。
代码
<script setup lang="ts">
import { onBeforeUnmount, ref, shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
import { Button } from '@fibrous/semi-ui-vue';
const roleConfig = {
user: {
name: 'User',
avatar: '/assets/docs-icon.png',
},
assistant: {
name: 'Assistant',
avatar: '/assets/placeholder/logo.svg',
},
};
const REPLY = '这是一段模拟的流式回复,每隔一小段时间追加几个字。回复生成期间,向上滚动(滚轮、拖动滚动条、PageUp 或方向键)会停止自动跟随,停在你滚到的位置;滚回底部后恢复跟随;发送新消息时也会回到底部。'.repeat(3);
const messages = shallowRef<Message[]>([{ role: 'assistant', id: 'welcome', content: '点击下方按钮发送消息,回复会以流式方式逐步生成。' }]);
const streaming = ref(false);
let timer: ReturnType<typeof setInterval> | undefined;
onBeforeUnmount(() => clearInterval(timer));
const send = () => {
const replyId = `reply-${Date.now()}`;
messages.value = [...messages.value, { role: 'user', id: `user-${Date.now()}`, content: '请生成一段较长的回复' }, { role: 'assistant', id: replyId, content: '', status: 'in_progress' }];
streaming.value = true;
let length = 0;
timer = setInterval(() => {
length += 6;
const done = length >= REPLY.length;
messages.value = messages.value.map((message) => (message.id === replyId ? { ...message, content: REPLY.slice(0, length), status: done ? 'completed' : 'in_progress' } : message));
if (done) {
clearInterval(timer);
streaming.value = false;
}
}, 100);
};
</script>
<template>
<Button :disabled="streaming" :style="{ marginBottom: '12px' }" @click="send">发送消息</Button>
<div :style="{ height: '320px' }">
<AIChatDialogue v-model:chats="messages" :roleConfig="roleConfig" />
</div>
</template>消息状态
chats 类型为 Message[], Message 包含对话的各种信息,如角色 role、内容 content、状态 status 、唯一标识 id、创建时间 createdAt 等,具体见 Message。其中 status 和 Response API Status 相同,存在 6 种状态,对应 3 种官方样式(成功 / 请求中 / 失败)。
请求成功
请求错误
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
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',
},
};
const messages = shallowRef(defaultMessages);
</script>
<template>
<AIChatDialogue v-model:chats="messages" :roleConfig="roleConfig" />
</template>消息展示
消息内容展示的类型为 ContentItem[],支持文本 text、文件 file、图片 image、代码 code、思考块 reasoning、参考来源 annotation、工具调用 tool call 等消息块的展示,同时提供 AIChatDialogue.Step 组件用于步骤等信息的分步展示。
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
import { IconBriefStroked, IconCodeStroked, IconSearchStroked } from '@fibrous/semi-icons-vue';
// A type alias, not an interface: only an alias is assignable to the step action's index signature
type PlanStep = {
type?: string;
summary?: string;
description?: string;
};
interface PlanItem {
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',
},
};
const messages = shallowRef(defaultMessages);
// plan is a user-defined content type; its icons come from the #actionIcon slot, so the steps carry data only
const toSteps = (item: unknown) =>
(item as { content: PlanItem[] }).content.map((plan) => ({
summary: plan.summary,
actions: plan.steps ?? [],
status: 'completed',
}));
</script>
<template>
<AIChatDialogue v-model:chats="messages" :roleConfig="roleConfig">
<template #contentItem="{ item, type }">
<AIChatDialogue.Step v-if="type === 'plan'" :steps="toSteps(item)">
<template #actionIcon="{ item: action }">
<IconSearchStroked v-if="action.type === 'search'" />
<IconBriefStroked v-else-if="action.type === 'docs'" />
<IconCodeStroked v-else-if="action.type === 'code'" />
</template>
</AIChatDialogue.Step>
</template>
</AIChatDialogue>
</template>引用
通过 references 字段定义当前消息引用的文件或者文本, showReference 配置当前消息是否显示可被引用样式, @referenceClick 监听引用按钮的点击。具体和 AIChatInput 的搭配使用见 AI 组件构建对话
当前消息为引用 demo 的示例
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
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',
},
};
const messages = shallowRef(defaultMessages);
const onReferenceClick = () => {
console.log('You click the reference button!');
};
</script>
<template>
<AIChatDialogue v-model:chats="messages" :roleConfig="roleConfig" showReference @referenceClick="onReferenceClick" />
</template>选择
Hello, I'm your AI assistant.
给一个 Semi Design 的 Button 组件的使用示例
以下是一个 Semi 代码的使用示例:
jsx
代码
<script setup lang="ts">
import { onMounted, ref, useTemplateRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueHandle, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
import { Radio, RadioGroup } from '@fibrous/semi-ui-vue';
const messages: 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',
},
};
// A Radio value of true cannot use the bare-attribute shorthand: the prop accepts strings, so it would be ''.
const selectOptions = [
{ label: '开启', value: true },
{ label: '关闭', value: false },
];
const dialogue = useTemplateRef<AIChatDialogueHandle>('dialogue');
const align = ref<'leftRight' | 'leftAlign'>('leftRight');
const select = ref(true);
const selection = ref<'allSelect' | 'cancelSelect'>('allSelect');
onMounted(() => dialogue.value?.selectAll());
const onSelectionChange = (value: unknown) => {
if (value === 'allSelect') {
dialogue.value?.selectAll();
selection.value = value;
} else if (value === 'cancelSelect') {
dialogue.value?.deselectAll();
selection.value = value;
}
};
const onAlignChange = (value: unknown) => {
if (value === 'leftRight' || value === 'leftAlign') align.value = value;
};
const onSelectChange = (value: unknown) => {
if (typeof value === 'boolean') select.value = value;
};
const onSelect = (selectionId: string[]) => {
console.log('onSelect', selectionId);
};
</script>
<template>
<div>
<span :style="{ display: 'flex', flexDirection: 'column', rowGap: '8px' }">
<span :style="{ display: 'flex', alignItems: 'center', columnGap: '10px' }">
会话布局方式
<RadioGroup :modelValue="align" type="button" @update:modelValue="onAlignChange">
<Radio value="leftRight">左右分布</Radio>
<Radio value="leftAlign">左对齐</Radio>
</RadioGroup>
</span>
<span :style="{ display: 'flex', alignItems: 'center', columnGap: '10px' }">
是否开启选择
<RadioGroup :modelValue="select" type="button" @update:modelValue="onSelectChange">
<Radio v-for="option in selectOptions" :key="option.label" :value="option.value">
{{ option.label }}
</Radio>
</RadioGroup>
</span>
<span :style="{ display: 'flex', alignItems: 'center', columnGap: '10px' }">
选择方式
<RadioGroup :modelValue="selection" type="button" @update:modelValue="onSelectionChange">
<Radio value="allSelect">全选</Radio>
<Radio value="cancelSelect">取消全选</Radio>
</RadioGroup>
</span>
</span>
<div
:style="{
border: '1px solid var(--semi-color-border)',
borderRadius: '12px',
marginTop: '10px',
padding: '20px',
}"
>
<AIChatDialogue ref="dialogue" :align="align" :chats="messages" mode="bubble" :roleConfig="roleConfig" :selecting="select" @select="onSelect" />
</div>
</div>
</template>提示
通过 hints 可设置提示区域内容, 点击提示内容后,提示内容将成为新的用户输入内容,并触发 hintClick 事件。
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
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 messages = shallowRef<Message[]>([
{
role: 'assistant',
id: '1',
createdAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
},
]);
const hints = shallowRef(['Semi 组件库有哪些常用组件?', '能否展示一个使用 Semi 组件库构建的页面示例?', 'Semi 组件库有官方文档吗?']);
</script>
<template>
<AIChatDialogue v-model:chats="messages" align="leftRight" :hints="hints" mode="bubble" :roleConfig="roleConfig" @hintClick="hints = []" />
</template>自定义渲染提示
通过 #hintBox 插槽可自定义提示区域内容,插槽参数为 { content, index, onHintClick },调用 onHintClick 即等同于点击该提示。
interface AIChatDialogueHintBoxSlotProps {
content: string;
index: number;
onHintClick: () => void;
}
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
import { IconArrowRight } from '@fibrous/semi-icons-vue';
const hintStyle = {
border: '1px solid var(--semi-color-border)',
padding: '10px',
borderRadius: '10px',
backgroundColor: 'transparent',
color: 'var(--semi-color-text-1)',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
cursor: 'pointer',
font: 'inherit',
fontSize: '14px',
};
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 messages = shallowRef<Message[]>([
{
role: 'assistant',
id: '1',
createdAt: 1715676751919,
content: 'Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。',
},
]);
const hints = shallowRef(['Semi 组件库有哪些常用组件?', '能否展示一个使用 Semi 组件库构建的页面示例?', 'Semi 组件库有官方文档吗?']);
</script>
<template>
<AIChatDialogue v-model:chats="messages" align="leftRight" :hints="hints" mode="bubble" :roleConfig="roleConfig" @hintClick="hints = []">
<template #hintBox="{ content, onHintClick }">
<button :style="hintStyle" type="button" @click="onHintClick">
{{ content }}
<IconArrowRight :style="{ marginLeft: '10px' }" />
</button>
</template>
</AIChatDialogue>
</template>自定义渲染会话框
各区块通过具名作用域插槽自定义,提供插槽即替换对应区块,每个插槽的参数都带有默认节点,可在其中二次组合:
| 插槽 | 说明 | 参数 |
|---|---|---|
#avatar | 自定义头像 | { message, role, defaultAvatar } |
#title | 自定义标题 | { message, role, defaultTitle } |
#content | 自定义内容区域 | { message, role, defaultContent, class } |
#action | 自定义消息操作栏 | { message, defaultActions, defaultActionsObj, class } |
#fullDialogue | 完全自定义渲染整条对话 | { message, role, defaultNodes, class } |
defaultActions 是默认操作栏的按钮数组,defaultActionsObj 是同样的按钮按名称索引的对象,与默认操作栏显示的按钮一致,按钮不显示时(如消息未完成)对应的键不存在。defaultNodes 含 avatar、title、content、action 四个默认节点。默认节点是 VNode,用 <component :is="..." /> 渲染。
interface DefaultActionNodeObj {
copyNode?: VNode;
likeNode?: VNode;
dislikeNode?: VNode;
resetNode?: VNode;
shareNode?: VNode;
editNode?: VNode;
moreNode?: VNode;
}
interface FullDialogueNodes {
avatar: VNode;
title: VNode;
content: VNode;
action: VNode;
}
Hello, I'm your AI assistant.
给一个 Semi Design 的 Button 组件的使用示例
以下是一个 Semi 代码的使用示例:
jsx
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
import { Avatar } from '@fibrous/semi-ui-vue';
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 messages = shallowRef<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",
},
]);
</script>
<template>
<AIChatDialogue v-model:chats="messages" align="leftRight" mode="bubble" :roleConfig="roleConfig">
<template #title="{ role }">
<div class="semi-ai-chat-dialogue-title">My-{{ role?.name }}</div>
</template>
<template #avatar="{ role }">
<Avatar alt="" shape="square" size="extra-small" :src="role?.avatar" />
</template>
<template #action="{ defaultActions, class: className }">
<div :class="className">
<component :is="defaultActions[0]" />
</div>
</template>
</AIChatDialogue>
</template>自定义渲染消息内容
通过 #contentItem 插槽按照消息类型返回内容渲染:每个内容项(包括 message 类型内层的输入文本、图片、文件)都会先经过该插槽,参数为 { item, type, message, index, parent },type 是内容项的类型,没有 type 的顶层内容项视为 message;内层项的 parent 是所属的 message 项。插槽返回空内容时由内置渲染接管,返回非空内容时,外面包一层 semi-ai-chat-dialogue-content-custom-renderer。内容为纯文本的消息不是内容项,用 #messageText 插槽替换其 Markdown 渲染,参数为 { text, message }。AIChatDialogue.Reasoning、AIChatDialogue.Annotation 可在插槽中复用,见下文 Static Properties。用法如下


代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, type AIChatDialogueAnnotation as Annotation, type AIChatDialogueContentItemSlotProps as ContentItemSlotProps, type AIChatDialogueMessage as Message } from '@fibrous/semi-ai-vue';
import { MarkdownRender, Toast } from '@fibrous/semi-ui-vue';
// The fields of the content items this demo renders; the slot types `item` as the union of every item type.
interface DemoItem {
name?: string;
arguments?: string;
text?: string;
status?: string;
summary?: { text?: string; type?: string }[];
annotations?: Annotation[];
}
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 messages = shallowRef<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 userTextStyle = {
backgroundColor: 'var(--semi-color-fill-1)',
color: 'var(--semi-color-text-0)',
borderRadius: '25px',
padding: '6px 16px',
};
const assistantStyle = {
color: 'var(--semi-color-text-0)',
padding: '6px 16px',
};
const functionCallStyle = {
backgroundColor: 'var(--semi-color-fill-1)',
padding: '6px 16px',
borderRadius: '25px',
};
const fields = (item: ContentItemSlotProps['item']) => item as DemoItem;
const onAnnotationClick = (e: MouseEvent | KeyboardEvent) => {
e.stopPropagation();
Toast.success('Ready to open the sidebar!');
};
</script>
<template>
<AIChatDialogue v-model:chats="messages" :roleConfig="roleConfig">
<template #contentItem="{ item, type, message }">
<div v-if="type === 'function_call' && fields(item).name === 'create_travel_guide'" :style="functionCallStyle">Function Tool Call: {{ fields(item).name }} {{ fields(item).arguments }}</div>
<template v-else-if="type === 'input_text'">
<div v-if="message.role === 'user'" class="userTextStyle" :style="userTextStyle">
{{ fields(item).text }}
</div>
<div v-else :style="assistantStyle">{{ fields(item).text }}</div>
</template>
<AIChatDialogue.Reasoning v-else-if="type === 'reasoning'" :status="fields(item).status" :summary="fields(item).summary">
<template #content="{ summary, markdownRenderProps }">
<AIChatDialogue.Annotation :annotation="fields(item).annotations ?? []" description="参考资料" :maxCount="3" @click="onAnnotationClick" />
<div :style="{ marginTop: '8px' }">
<MarkdownRender format="md" v-bind="markdownRenderProps" :raw="summary?.[0]?.text ?? ''" />
</div>
</template>
</AIChatDialogue.Reasoning>
</template>
<template #messageText="{ text, message }">
<div v-if="message.role === 'user'" class="userTextStyle" :style="userTextStyle">{{ text }}</div>
<div v-else :style="assistantStyle">{{ text }}</div>
</template>
</AIChatDialogue>
</template>消息数据转换
当前组件的对话消息以 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-vue';
// 将 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?
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, chatCompletionToMessage } from '@fibrous/semi-ai-vue';
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',
},
};
const messages = shallowRef(chatCompletionToMessage(CHAT_COMPLETION_DATA));
</script>
<template>
<AIChatDialogue v-model:chats="messages" align="leftRight" mode="bubble" :roleConfig="roleConfig" />
</template>比如,当用户使用 Chat Completion API 接口返回流式数据时,可以通过 streamingChatCompletionToMessage 函数将 Chat Completion Chunk Object List 转换为 Dialogue Message 消息块格式。
代码
<script setup lang="ts">
import { onBeforeUnmount, onMounted, shallowRef } from 'vue';
import { AIChatDialogue, streamingChatCompletionToMessage, type AIChatDialogueMessage as Message, type StreamingChatState } from '@fibrous/semi-ai-vue';
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 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',
},
];
const messages = shallowRef<Message[]>([]);
let timer: ReturnType<typeof setInterval> | undefined;
onMounted(() => {
const total = STREAMING_CHAT_COMPLETION_DATA.length;
let i = 1;
let state: StreamingChatState | undefined;
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;
messages.value = [partialMessages[0]];
i += 1;
}, 100);
});
onBeforeUnmount(() => clearInterval(timer));
</script>
<template>
<AIChatDialogue v-model:chats="messages" align="leftRight" mode="bubble" :roleConfig="roleConfig" />
</template>当用户使用 Response API 接口返回非流式数据时,可以通过 responseToMessage 函数将 Response Object 转换为 Dialogue Message 消息块格式。
Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统
代码
<script setup lang="ts">
import { shallowRef } from 'vue';
import { AIChatDialogue, responseToMessage } from '@fibrous/semi-ai-vue';
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 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 messages = shallowRef([responseToMessage(RESPONSE_DATA)]);
</script>
<template>
<AIChatDialogue v-model:chats="messages" align="leftRight" mode="bubble" :roleConfig="roleConfig" />
</template>当用户使用 Response API 接口返回流式数据时,可以通过 streamingResponseToMessage 函数将 Response Chunk Object List 转换为 Dialogue Message 消息块格式。
代码
<script setup lang="ts">
import { onBeforeUnmount, onMounted, shallowRef } from 'vue';
import { AIChatDialogue, streamingResponseToMessage, type AIChatDialogueMessage as Message, type ResponseChunk, type StreamingResponseState } from '@fibrous/semi-ai-vue';
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,
];
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: {},
},
},
];
const messages = shallowRef<Message[]>([]);
let timer: ReturnType<typeof setTimeout> | undefined;
onMounted(() => {
let currentLength = 0;
let currentState: StreamingResponseState | undefined;
const tick = () => {
timer = setTimeout(() => {
if (currentLength > 0) {
const currentIndices = FIXED_SHUFFLED_INDICES.slice(0, currentLength);
const currentChunks = currentIndices.map((index) => REASONING_CHUNKS[index]);
const result = streamingResponseToMessage(currentChunks, currentState);
if (result) {
const { message: responseMessage, nextState } = result;
if (responseMessage) {
messages.value = [responseMessage];
currentState = nextState;
}
}
}
currentLength += 1;
if (currentLength <= FIXED_SHUFFLED_INDICES.length) {
tick();
}
}, 200);
};
tick();
});
onBeforeUnmount(() => clearTimeout(timer));
</script>
<template>
<AIChatDialogue v-model:chats="messages" align="leftRight" mode="bubble" :roleConfig="roleConfig" />
</template>API 参考
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| align | 对话布局方式,可选 leftRight(左右分布)或 leftAlign(左对齐)。 | "leftRight" | "leftAlign" | semiDefault("AIChatDialogue", "align", "leftRight") |
| chats | 受控对话消息列表,配合 v-model:chats 使用。 | Message[] | semiGlobalDefault("AIChatDialogue", "chats") |
| disabledFileItemClick | 是否禁用文件卡片的点击。 | boolean | semiDefault("AIChatDialogue", "disabledFileItemClick", false) |
| escapeHtml | 是否转义用户消息中的 HTML 标签,避免被 Markdown 解析器当作 HTML 处理而丢失内容。 | boolean | semiDefault("AIChatDialogue", "escapeHtml", true) |
| hintCls | 提示区最外层的类名。 | string | semiGlobalDefault("AIChatDialogue", "hintCls") |
| hints | 提示信息。点击某条提示后,它成为新的用户消息,并触发 hintClick。 | string[] | semiGlobalDefault("AIChatDialogue", "hints") |
| hintStyle | 提示区最外层的样式。 | StyleValue | semiGlobalDefault("AIChatDialogue", "hintStyle") |
| selecting | 是否开启多选模式。 | boolean | semiDefault("AIChatDialogue", "selecting", false) |
| markdownRenderProps | 透传给每条消息所用 MarkdownRender 的属性,raw 由消息决定,不可传入;components 与内置组件合并,同名的(如 code)覆盖内置组件。 | MarkdownRenderProps | semiGlobalDefault("AIChatDialogue", "markdownRenderProps") |
| mode | 对话模式,可选 bubble、noBubble 或 userBubble。 | "bubble" | "noBubble" | "userBubble" | semiDefault("AIChatDialogue", "mode", "bubble") |
| roleConfig | 各角色(user、assistant、system 等)的名称、头像与头像颜色,必填,不读取全局默认值。 | RoleConfig | undefined |
| showReset | 是否展示重置操作。 | boolean | semiDefault("AIChatDialogue", "showReset", true) |
| showReference | 是否在文字或文件消息中展示可被引用的图标,仅对用户消息生效。 | boolean | semiDefault("AIChatDialogue", "showReference", false) |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| update:chats | 消息列表变化时触发,v-model:chats 据此更新,与 chatsChange 传出同一个数组。 | [chats: Message[]] |
| chatsChange | 消息列表变化时触发,载荷为新的消息数组。 | [chats: Message[]] |
| annotationClick | 点击参考来源时触发,载荷为被点击消息块的注释数组。 | [annotations: Annotation[]] |
| fileClick | 点击文件卡片时触发。 | [file?: InputFile | undefined] |
| hintClick | 点击提示词时触发。 | [hint: string] |
| imageClick | 点击图片时触发。 | [image?: InputImage | undefined] |
| messageBadFeedback | 对消息点踩时触发。 | [message?: Message | undefined] |
| messageCopy | 复制消息时触发:文本写入剪贴板成功后才触发,复制的文本不含推理内容;没有可复制的文本时既不写入也不触发。 | [message?: Message | undefined] |
| messageDelete | 删除消息时触发。 | [message?: Message | undefined] |
| messageEdit | 编辑消息时触发。 | [message?: Message | undefined] |
| messageGoodFeedback | 对消息点赞时触发。 | [message?: Message | undefined] |
| messageReset | 重置消息时触发。 | [message?: Message | undefined] |
| messageShare | 分享消息时触发。 | [message?: Message | undefined] |
| referenceClick | 点击引用按钮时触发。载荷没有 id,加入 AIChatInput 的 references 前由使用方补上;文件项没有 type。 | Type[item: Partial<Reference>] |
| select | 选中的消息变化时触发。 | [selectedIds: string[]] |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| avatar | 自定义头像,参数含默认头像 defaultAvatar、消息 message 与角色 role。 | AIChatDialogueAvatarSlotProps |
| title | 自定义标题,参数含默认标题 defaultTitle、消息 message 与角色 role。 | AIChatDialogueTitleSlotProps |
| content | 自定义内容区域,参数含默认内容 defaultContent、消息 message、角色 role 与 class。defaultContent 与默认渲染一致,包含引用列表与加载指示。 | AIChatDialogueContentSlotProps |
| action | 自定义消息操作栏,参数含默认按钮数组 defaultActions、按名称索引的 defaultActionsObj、消息 message 与 class;按钮不显示时(如消息未完成)对应的键不存在。 | AIChatDialogueActionSlotProps |
| fullDialogue | 完全自定义整条对话,参数含 defaultNodes(avatar、title、content、action)、消息 message、角色 role 与 class。 | AIChatDialogueFullDialogueSlotProps |
| contentItem | 按内容项自定义渲染,每个内容项都会先经过它,参数含 item、type、message、index 与 parent;返回空内容时由内置渲染接管。 | AIChatDialogueContentItemSlotProps |
| messageText | 替换内容为纯文本的消息的 Markdown 渲染,参数含文本 text 与消息 message。 | AIChatDialogueMessageTextSlotProps |
| messageEdit | 用户消息处于编辑状态时显示的编辑器,参数为消息转换得到的输入内容。 | MessageContent |
| hintBox | 自定义提示项,参数含提示文本 content、序号 index 与点击回调 onHintClick。 | AIChatDialogueHintBoxSlotProps |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| selectAll | 全选所有消息。 | Type() => void |
| deselectAll | 取消全选所有消息。 | Type() => void |
| scrollToBottom | 滚动到最底部,参数为 true 时带动画。 | Type(animation: boolean) => void |
| scrollToTop | 滚动到最顶部,参数为 true 时带动画。 | Type(animation: boolean) => void |
class、style 与原生属性经 attrs 落在根元素上;根元素带 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
方法在上方生成的 Exposes 表中,通过模板引用调用:
<script setup lang="ts">
import { useTemplateRef } from 'vue';
import { AIChatDialogue } from '@fibrous/semi-ai-vue';
const dialogue = useTemplateRef('dialogue');
const toBottom = () => dialogue.value?.scrollToBottom(true);
</script>
<template>
<AIChatDialogue ref="dialogue" v-model:chats="messages" :roleConfig="roleConfig" />
</template>
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 传入:
<!-- CustomCode.vue -->
<script setup lang="ts">
import { AIChatDialogue } from '@fibrous/semi-ai-vue';
defineOptions({ inheritAttrs: false });
const DefaultCode = AIChatDialogue.defaultComponents.code;
</script>
<template>
<!-- 可以在此处添加自定义逻辑 -->
<DefaultCode v-bind="$attrs"><slot /></DefaultCode>
</template>
<script setup lang="ts">
import { AIChatDialogue } from '@fibrous/semi-ai-vue';
import CustomCode from './CustomCode.vue';
const markdownRenderProps = { components: { code: CustomCode } };
</script>
<template>
<AIChatDialogue v-model:chats="messages" :markdownRenderProps="markdownRenderProps" :roleConfig="roleConfig" />
</template>
AIChatDialogue.Reasoning
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| status | 推理状态。为 completed 时标题显示已完成,且初次渲染时折叠;其他值显示思考中并展开。 | string | undefined |
| summary | 推理摘要,非空时按换行拼接其中的文本作为推理正文。 | { text?: string | undefined; type?: string | undefined; }[] | undefined |
| content | 推理内容,summary 为空时按换行拼接其中的文本作为推理正文。 | { text?: string | undefined; type?: string | undefined; }[] | undefined |
| markdownRenderProps | 透传给推理正文所用 MarkdownRender 的属性,raw 由推理正文决定。 | MarkdownRenderProps | undefined |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| 无 |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| content | 替换推理正文的 Markdown 渲染,参数为 status、summary、content 与 markdownRenderProps。 | ReasoningWidgetProps |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| 无 |
AIChatDialogue.Step
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| steps | 步骤列表。每个步骤含 summary、status 与 actions,status 为 completed 时显示完成图标,否则显示加载中;actions 只放数据(summary、description 等),步骤可点击或用 Enter、空格展开收起。 | DialogueStep[] | undefined |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| 无 |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| actionIcon | 显示在每个操作描述前的图标,参数为操作 item、序号 index、所属步骤 step 与步骤序号 stepIndex。 | DialogueStepWidgetActionIconSlotProps |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| 无 |
AIChatDialogue.Annotation
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| maxCount | 最多显示的来源图标数,超出部分显示为 +N。 | number | undefined |
| annotation | 参考来源列表,每项可含 title、logo、url、detail。 | AnnotationItemProps[] | Annotation[] | undefined |
| description | 来源图标后的描述文字,不传时显示来源数量。 | string | undefined |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| click | 点击或用 Enter、空格激活时触发,参数为原生事件与参考来源列表。 | [event: MouseEvent | KeyboardEvent, items: AnnotationItemProps[]] |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| 无 |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| 无 |
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 |