AI 组件

AI对话

用户展示 AI 聊天中的对话信息

使用场景

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)。

模式
会话布局方式
System

Hello, I'm your AI assistant.

User

给一个 Semi Design 的 Button 组件的使用示例

Assistant

以下是一个 Semi 代码的使用示例:

jsx
import React from 'react';
import { Button } from '@fibrous/semi-ui-react';

const MyComponent = () => {
  return (
    <Button>Click me</Button>
 );
};
export default MyComponent;
代码
<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 头部插入更早的消息(如滚到顶部时加载历史)不会滚动列表,已显示的消息保持在原位置。

Assistant

点击下方按钮发送消息,回复会以流式方式逐步生成。

代码
<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 种官方样式(成功 / 请求中 / 失败)。

Assistant

请求成功

Assistant
请稍候...
Assistant

请求错误

代码
<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 组件用于步骤等信息的分步展示。

Assistant

普通文本

User

帮我生成类似的图片

图片

以下是文件展示

semi.pdf
semi.json
semi.docx
Assistant

Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统。

get_weather {'location':'Paris, France'}
2 篇资料

恭喜你,你已经掌握了 semi design 的所有知识!

创建一份全面的北京旅游攻略,包含景点、住宿、交通、美食和实用旅行建议
搜索北京旅游景点介绍及门票信息
正在搜索: 北京旅游景点介绍及门票信息
读取指定文件的指定行内容
正在创建文档: 北京旅游攻略
创建包含北京旅游攻略的文件
正在创建代码文件: beijing_travel_guide.html
总结北京旅游攻略的创建成果并呈现给用户
代码
<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 组件构建对话

User
测试文本,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字,这里是一段很长的文字
飞书文档.docx
Music.mp4
Image.jpeg
code.json

当前消息为引用 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>

选择

会话布局方式
是否开启选择
选择方式
System

Hello, I'm your AI assistant.

User

给一个 Semi Design 的 Button 组件的使用示例

Assistant

以下是一个 Semi 代码的使用示例:

jsx
import React from 'react';
import { Button } from '@fibrous/semi-ui-react';

const MyComponent = () => {
  return (
    <Button>Click me</Button>
 );
};
export default MyComponent;
代码
<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 事件。

Assistant

Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统,你可以向我提问任何关于 Semi 的问题。

Semi 组件库有哪些常用组件?
能否展示一个使用 Semi 组件库构建的页面示例?
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;
}
Assistant

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;
}
My-System

Hello, I'm your AI assistant.

My-User

给一个 Semi Design 的 Button 组件的使用示例

My-Assistant

以下是一个 Semi 代码的使用示例:

jsx
import React from 'react';
import { Button } from '@fibrous/semi-ui-react';

const MyComponent = () => {
  return (
    <Button>Click me</Button>
 );
};
export default MyComponent;
代码
<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。用法如下

User
你好
Assistant
你好呀,请问有什么可以帮助你的吗~
User
帮我生成类似的图片
图片
图片
Assistant
Function Tool Call: create_travel_guide { "city": "北京" }
代码
<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 中展示)

Assistant

Hello! How can I assist you today?

get_current_weather { "location": "Boston, MA" }
代码
<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 消息块格式。

Assistant
3 篇资料

Semi Design 是由抖音前端团队和MED产品设计团队设计、开发并维护的设计系统

get_semi_page {"pageName":"AIChatDialogue"}
代码
<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是否禁用文件卡片的点击。booleansemiDefault("AIChatDialogue", "disabledFileItemClick", false)
escapeHtml是否转义用户消息中的 HTML 标签,避免被 Markdown 解析器当作 HTML 处理而丢失内容。booleansemiDefault("AIChatDialogue", "escapeHtml", true)
hintCls提示区最外层的类名。stringsemiGlobalDefault("AIChatDialogue", "hintCls")
hints提示信息。点击某条提示后,它成为新的用户消息,并触发 hintClick。string[]semiGlobalDefault("AIChatDialogue", "hints")
hintStyle提示区最外层的样式。StyleValuesemiGlobalDefault("AIChatDialogue", "hintStyle")
selecting是否开启多选模式。booleansemiDefault("AIChatDialogue", "selecting", false)
markdownRenderProps透传给每条消息所用 MarkdownRender 的属性,raw 由消息决定,不可传入;components 与内置组件合并,同名的(如 code)覆盖内置组件。MarkdownRenderPropssemiGlobalDefault("AIChatDialogue", "markdownRenderProps")
mode对话模式,可选 bubble、noBubble 或 userBubble。"bubble" | "noBubble" | "userBubble"semiDefault("AIChatDialogue", "mode", "bubble")
roleConfig各角色(user、assistant、system 等)的名称、头像与头像颜色,必填,不读取全局默认值。RoleConfigundefined
showReset是否展示重置操作。booleansemiDefault("AIChatDialogue", "showReset", true)
showReference是否在文字或文件消息中展示可被引用的图标,仅对用户消息生效。booleansemiDefault("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、 yellowAvatarColorgrey

Message

属性说明类型默认值
role角色string-
name名称string-
id唯一标识string-
content消息内容string| ContentItem[]-
createdAt创建时间number-
model模型名称string-
status消息状态,可选值为 queued | in_progress | incomplete | completed | failed | cancelledstringcompleted
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.ReasoningComponent
Step步骤展示组件,API 见下方 AIChatDialogue.StepComponent
Annotation参考来源组件,API 见下方 AIChatDialogue.AnnotationComponent

使用示例:

当需要在自定义代码块渲染时保留默认功能,可以把默认组件包进自己的组件,再通过 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 时标题显示已完成,且初次渲染时折叠;其他值显示思考中并展开。stringundefined
summary推理摘要,非空时按换行拼接其中的文本作为推理正文。{ text?: string | undefined; type?: string | undefined; }[]undefined
content推理内容,summary 为空时按换行拼接其中的文本作为推理正文。{ text?: string | undefined; type?: string | undefined; }[]undefined
markdownRenderProps透传给推理正文所用 MarkdownRender 的属性,raw 由推理正文决定。MarkdownRenderPropsundefined

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。numberundefined
annotation参考来源列表,每项可含 title、logo、url、detail。AnnotationItemProps[] | Annotation[]undefined
description来源图标后的描述文字,不传时显示来源数量。stringundefined

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 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$color-aiChatDialogue_wrapper-selected-bgvar(--semi-color-fill-0)外层选中背景色
$color-aiChatDialogue_loading_circle_first-bgrgba(83, 56, 255, 1)第一个加载圆点背景色
$color-aiChatDialogue_loading_circle_second-bgrgba(176, 48, 240, 1)第二个加载圆点背景色
$color-aiChatDialogue_loading_circle_third-bgrgba(231, 45, 255, 1)第三个加载圆点背景色
$color-aiChatDialogue_loading_textvar(--semi-color-text-3)加载中文本颜色
$color-aiChatDialogue_bubble-bgvar(--semi-color-fill-0)对话气泡背景色
$color-aiChatDialogue_file-bgvar(--semi-color-fill-0)文件组件背景色
$color-aiChatDialogue_file_title-textvar(--semi-color-text-0)文件标题文本颜色
$color-aiChatDialogue_file_metadata-textvar(--semi-color-text-2)文件元数据文本颜色
$color-aiChatDialogue_tool_call-bgvar(--semi-color-fill-0)工具调用组件背景色
$color-aiChatDialogue_tool_call-textvar(--semi-color-text-2)工具调用组件文本颜色
$color-aiChatDialogue_action_dropdown-textvar(--semi-color-danger)操作区下拉菜单文本颜色
$color-aiChatDialogue_reasoning-bordervar(--semi-color-border)推理组件边框色
$color-aiChatDialogue_step-bordervar(--semi-color-border)步骤组件边框色
$color-aiChatDialogue_step_action-bgvar(--semi-color-fill-0)步骤组件 action 背景色
$color-aiChatDialogue_step_completed-textvar(--semi-color-success)步骤组件完成文本颜色
$color-aiChatDialogue_step_suffix-textvar(--semi-color-text-1)步骤组件后缀文本颜色
$color-aiChatDialogue_step_action-textvar(--semi-color-text-2)步骤组件 action 文本颜色
$color-aiChatDialogue_annotation-bordervar(--semi-color-border)参考来源组件边框色
$color-aiChatDialogue_annotation-bg-hovervar(--semi-color-fill-0)参考来源组件悬停背景色
$color-aiChatDialogue_annotation-textvar(--semi-color-text-2)参考来源组件文本颜色
$color-aiChatDialogue_reference-bgvar(--semi-color-fill-0)引用组件背景色
$color-aiChatDialogue_reference-textvar(--semi-color-text-2)引用组件文本颜色
$color-aiChatDialogue_icon_reference-textvar(--semi-color-text-0)用户消息引用图标颜色
$color-aiChatDialogue_code-bordervar(--semi-color-border)代码块边框色
$color-aiChatDialogue_code_topSlot-bgvar(--semi-color-fill-0)代码块顶部槽背景色
$color-aiChatDialogue_code_topSlot-textvar(--semi-color-text-0)代码块顶部槽文本颜色
$color-aiChatDialogue_hint-bgvar(--semi-color-bg-0)提示项背景色
$color-aiChatDialogue_hint-bordervar(--semi-color-border)提示项边框色
$color-aiChatDialogue_hint-bg-hovervar(--semi-color-fill-0)提示项悬停背景色
$color-aiChatDialogue_hint-textvar(--semi-color-text-0)提示项文本颜色
$color-aiChatDialogue_code_icon-bgvar(--semi-teal-4)code icon 背景色
$color-aiChatDialogue_word_icon-bgvar(--semi-blue-4)word icon 背景色
$color-aiChatDialogue_excel_icon-bgvar(--semi-green-4)excel icon 背景色
$color-aiChatDialogue_pdf_icon-bgvar(--semi-red-4)pdf icon 背景色
$color-aiChatDialogue_video_icon-bgvar(--semi-purple-4)video icon 背景色
$color-aiChatDialogue_default_icon-bgvar(--semi-grey-4)default icon 背景色
$color-aiChatDialogue_icon-textvar(--semi-color-white)icon 文本颜色
$color-aiChatDialogue_failedvar(--semi-color-danger)失败提示颜色
$color-aiChatDialogue_code_content-bgvar(--semi-color-bg-0)code content 背景色
$width-aiChatDialogue_wrapper100%外层宽度
$width-aiChatDialogue_inner100%气泡右侧展示内层宽度
$width-aiChatDialogue_backBottom_button42px返回按钮宽度
$height-aiChatDialogue_backBottom_button42px返回按钮高度
$width-aiChatDialogue_avatar24px头像宽度
$height-aiChatDialogue_avatar24px头像高度
$width-aiChatDialogue_content_editing100%内容宽度 - 编辑状态
$width-aiChatDialogue_content_inner100%内容内层宽度
$width-aiChatDialogue_content_max100%内容最大宽度
$width-aiChatDialogue_bubble_max100%气泡最大宽度
$width-aiChatDialogue_loading_circle3px加载圆点宽度
$height-aiChatDialogue_loading_circle3px加载圆点高度
$width-aiChatDialogue_img256px图片宽度
$height-aiChatDialogue_img256px图片高度
$width-aiChatDialogue_img_list128px图片列表项宽度
$height-aiChatDialogue_img_list128px图片列表项高度
$width-aiChatDialogue_file240px文件组件宽度(含内边距)
$width-aiChatDialogue_file_icon_wrapper36px文件图标外层宽度
$height-aiChatDialogue_file_icon_wrapper36px文件图标外层高度
$width-aiChatDialogue_action_btn24px操作按钮宽度
$height-aiChatDialogue_action_btn24px操作按钮高度
$width-aiChatDialogue_reasoning100%推理组件宽度
$width-aiChatDialogue_reasoning_wrapper100%推理组件外层宽度
$width-aiChatDialogue_reasoning-border1px推理组件 border 宽度
$width-aiChatDialogue_reasoning_prefix20px推理前缀图标宽度
$height-aiChatDialogue_reasoning_prefix20px推理前缀图标高度
$width-aiChatDialogue_step-border1px步骤组件 border 宽度
$width-aiChatDialogue_step_prefix20px步骤前缀图标宽度
$height-aiChatDialogue_step_prefix20px步骤前缀图标高度
$width-aiChatDialogue_step_suffix20px步骤后缀图标宽度
$height-aiChatDialogue_step_suffix20px步骤后缀图标高度
$width-aiChatDialogue_step_line1px步骤左侧线宽度
$width-aiChatDialogue_annotation100%参考来源宽度
$width-aiChatDialogue_annotation-border1px参考来源组件 border 宽度
$width-aiChatDialogue_annotation_logo16px参考来源logo宽度
$height-aiChatDialogue_annotation_logo16px参考来源logo高度
$width-aiChatDialogue_reference_icon16px引用图标宽度
$height-aiChatDialogue_reference_icon16px引用图标高度
$width-aiChatDialogue_reference_max200px引用组件最大宽度
$width-aiChatDialogue_reference_name_max130px引用名称最大宽度
$width-aiChatDialogue_code-border1px代码块组件 border 宽度
$width-aiChatDialogue_hint-border1px提示项组件 border 宽度
$width-aiChatDialogue_file_title_max180px文件标题最大宽度
$width-aiChatDialogue_file_title_max-hover140px用户消息悬停时文件标题最大宽度(显示引用图标时)
$width-aiChatDialogue_action_copy24px复制按钮宽度
$spacing-aiChatDialogue_wrapper-paddingY8px最外层垂直内边距
$spacing-aiChatDialogue_wrapper-paddingX16px最外层水平内边距
$spacing-aiChatDialogue_wrapper-marginTop2px最外层顶部外边距
$spacing-aiChatDialogue_wrapper-columnGap16px最外层列间距
$spacing-aiChatDialogue_container-columnGap12pxcontainer 容器列间距
$spacing-aiChatDialogue_content_failed-marginRight8px内容失败提示右外边距
$spacing-aiChatDialogue_content_bubble-marginTop8px内容气泡顶部外边距
$spacing-aiChatDialogue_content_noBubble-marginTop8px内容无气泡顶部外边距
$spacing-aiChatDialogue_bubble-paddingY12px气泡垂直内边距
$spacing-aiChatDialogue_bubble-paddingX16px气泡水平内边距
$spacing-aiChatDialogue_content_loading-marginTop8px内容加载顶部外边距
$spacing-aiChatDialogue_loading_item-marginY1px加载项垂直外边距
$spacing-aiChatDialogue_loading_item-marginX1px加载项水平外边距
$spacing-aiChatDialogue_loading_text-marginLeft8px加载文本左外边距
$spacing-aiChatDialogue_img-marginTop8px图片顶部外边距
$spacing-aiChatDialogue_img_list-marginRight4px图片列表右外边距
$spacing-aiChatDialogue_file-paddingY8px文件组件垂直内边距
$spacing-aiChatDialogue_file-paddingX8px文件组件水平内边距
$spacing-aiChatDialogue_file-columnGap8px文件组件列间距
$spacing-aiChatDialogue_file-marginTop8px文件组件顶部外边距
$spacing-aiChatDialogue_file-marginRight8px文件组件右外边距
$spacing-aiChatDialogue_tool_call-paddingY6px工具调用垂直内边距
$spacing-aiChatDialogue_tool_call-paddingX8px工具调用水平内边距
$spacing-aiChatDialogue_tool_call-columnGap4px工具调用列间距
$spacing-aiChatDialogue_tool_call-marginTop8px工具调用顶部外边距
$spacing-aiChatDialogue_custom_renderer-marginTop8px自定义渲染器顶部外边距
$spacing-aiChatDialogue_action-marginTop8px操作顶部外边距
$spacing-aiChatDialogue_reasoning_wrapper-marginTop8px推理组件外层顶部外边距
$spacing-aiChatDialogue_reasoning_header-padding16px推理头部内边距
$spacing-aiChatDialogue_reasoning_content-paddingY12px推理内容垂直内边距
$spacing-aiChatDialogue_reasoning_content-paddingX16px推理内容水平内边距
$spacing-aiChatDialogue_step_wrapper-marginTop4px步骤外层顶部外边距
$spacing-aiChatDialogue_step-paddingY6px步骤垂直内边距
$spacing-aiChatDialogue_step-paddingX12px步骤水平内边距
$spacing-aiChatDialogue_step-marginTop12px步骤顶部外边距
$spacing-aiChatDialogue_step_prefix-marginRight6px步骤前缀右外边距
$spacing-aiChatDialogue_step_suffix-marginLeft8px步骤后缀左外边距
$spacing-aiChatDialogue_step_panel-gap12px步骤面板间距
$spacing-aiChatDialogue_step_line-marginLeft20px步骤连接线左外边距
$spacing-aiChatDialogue_step_action-marginTop12px步骤 action 顶部外边距
$spacing-aiChatDialogue_step_action_summary-marginBottom4px步骤 action 摘要底部外边距
$spacing-aiChatDialogue_step_action_desc-paddingY6px步骤 action 描述垂直内边距
$spacing-aiChatDialogue_step_action_desc-paddingX8px步骤 action 描述水平内边距
$spacing-aiChatDialogue_step_action_desc-columnGap4px步骤 action 描述列间距
$spacing-aiChatDialogue_annotation_wrapper-marginTop8px参考来源外层顶部外边距
$spacing-aiChatDialogue_annotation_wrapper-paddingY8px参考来源外层垂直内边距
$spacing-aiChatDialogue_annotation_wrapper-paddingX12px参考来源外层水平内边距
$spacing-aiChatDialogue_annotation_content-columnGap8px参考来源内容列间距
$spacing-aiChatDialogue_references-marginTop8px引用顶部外边距
$spacing-aiChatDialogue_references-columnGap4px引用列间距
$spacing-aiChatDialogue_references-rowGap4px引用行间距
$spacing-aiChatDialogue_reference-paddingY8px引用垂直内边距
$spacing-aiChatDialogue_reference-paddingX12px引用水平内边距
$spacing-aiChatDialogue_reference-columnGap8px引用列间距
$spacing-aiChatDialogue_reference_icon-marginRight4px引用图标右外边距
$spacing-aiChatDialogue_reference_img-marginRight4px引用图片右外边距
$spacing-aiChatDialogue_icon_reference-paddingY0px用户消息引用图标垂直内边距
$spacing-aiChatDialogue_icon_reference-paddingX8px用户消息引用图标水平内边距
$spacing-aiChatDialogue_code_topSlot-paddingY8px代码块顶部槽垂直内边距
$spacing-aiChatDialogue_code_topSlot-paddingX16px代码块顶部槽水平内边距
$spacing-aiChatDialogue_hints-rowGap8px提示行间距
$spacing-aiChatDialogue_hints-marginTop4px提示顶部外边距
$spacing-aiChatDialogue_hints-marginBottom12px提示底部外边距
$spacing-aiChatDialogue_hints-marginLeft52px提示左外边距
$spacing-aiChatDialogue_hint_item-paddingY8px提示项垂直内边距
$spacing-aiChatDialogue_hint_item-paddingX16px提示项水平内边距
$spacing-aiChatDialogue_hints_selecting-marginLeft68px提示项选择状态下左外边距
$radius-aiChatDialogue_backBottom_button50%返回按钮圆角
$radius-aiChatDialogue_wrapper_selected16px外层选中圆角
$radius-aiChatDialogue_loading_circlevar(--semi-border-radius-circle)加载圆点圆角
$radius-aiChatDialogue_bubblevar(--semi-border-radius-large)对话气泡圆角
$radius-aiChatDialogue_imgvar(--semi-border-radius-medium)图片圆角
$radius-aiChatDialogue_filevar(--semi-border-radius-medium)文件组件圆角
$radius-aiChatDialogue_icon_wrappervar(--semi-border-radius-extra-small)图标外层圆角
$radius-aiChatDialogue_tool_callvar(--semi-border-radius-medium)工具调用组件圆角
$radius-aiChatDialogue_reasoning_wrappervar(--semi-border-radius-large)推理组件外层圆角
$radius-aiChatDialogue_stepvar(--semi-border-radius-medium)步骤组件圆角
$radius-aiChatDialogue_action_descvar(--semi-border-radius-medium)操作描述圆角
$radius-aiChatDialogue_annotation_wrappervar(--semi-border-radius-medium)参考来源外层圆角
$radius-aiChatDialogue_referencevar(--semi-border-radius-medium)引用组件圆角
$radius-aiChatDialogue_reference_icon2px引用图标圆角
$radius-aiChatDialogue_codevar(--semi-border-radius-large)代码块圆角
$radius-aiChatDialogue_hint_itemvar(--semi-border-radius-medium)提示项圆角
$font-aiChatDialogue_title-fontSize$font-size-header-6标题字体大小
$font-aiChatDialogue_title-fontWeight$font-weight-regular标题字体粗细
$font-aiChatDialogue_title-lineHeight20px标题行高
$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-lineHeight22px推理头部标题行高
$font-aiChatDialogue_dropdown_icon-fontSize$font-size-header-6下拉菜单图标字体大小
$font-aiChatDialogue_step-fontSize$font-size-regular步骤字体大小
$font-aiChatDialogue_step-lineHeight20px步骤行高
$font-aiChatDialogue_step_action-fontSize$font-size-regular步骤 action 字体大小
$font-aiChatDialogue_step_action-lineHeight20px步骤 action 行高
$font-aiChatDialogue_annotation_content-fontSize$font-size-regular参考来源内容字体大小
$font-aiChatDialogue_references-fontSize12px引用字体大小
$font-aiChatDialogue_references-lineHeight$font-size-header-6引用行高
$font-aiChatDialogue_code_topSlot-fontSize$font-size-regular代码块顶部槽字体大小
$font-aiChatDialogue_code-lineHeight32px代码块行高
$font-aiChatDialogue_hint_content-fontSize$font-size-regular提示内容字体大小
$z-aiChatDialogue_backBottom1返回按钮z-index