@blueking/chat-x 0.0.48-beta.1 → 0.0.49-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ag-ui/types/file.d.ts +23 -0
- package/dist/ag-ui/types/index.d.ts +1 -0
- package/dist/ag-ui/types/messages.d.ts +2 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-file-card.vue.d.ts +14 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/artifact-preview-host.vue.d.ts +8 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/preview-strategy.d.ts +9 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/html-preview.vue.d.ts +6 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/markdown-preview.vue.d.ts +6 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/txt-preview.vue.d.ts +6 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/renderers/url-iframe-preview.vue.d.ts +6 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/artifact-preview/use-artifact-preview-loader.d.ts +21 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue.d.ts +12 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/file-icon.d.ts +29 -0
- package/dist/components/chat-message/assistant-message/message-artifacts/message-artifacts.vue.d.ts +10 -0
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/use-artifact-preview.d.ts +59 -0
- package/dist/composables/use-message-group.d.ts +434 -0
- package/dist/icons/file.d.ts +24 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/index.css +1 -1
- package/dist/index.js +2083 -1600
- package/dist/index.js.map +1 -1
- package/dist/lang/lang.d.ts +6 -1
- package/dist/mcp/generated/docs/activity-message.md +122 -97
- package/dist/mcp/generated/docs/assistant-message.md +125 -62
- package/dist/mcp/generated/docs/chat-container.md +94 -16
- package/dist/mcp/generated/docs/file-artifact-panel.md +279 -0
- package/dist/mcp/generated/docs/info-message.md +23 -7
- package/dist/mcp/generated/docs/loading-message.md +36 -17
- package/dist/mcp/generated/docs/message-render.md +44 -33
- package/dist/mcp/generated/docs/reasoning-message.md +15 -11
- package/dist/mcp/generated/docs/tool-message.md +61 -45
- package/dist/mcp/generated/docs/use-artifact-preview.md +237 -0
- package/dist/mcp/generated/docs/use-message-group.md +24 -0
- package/dist/mcp/generated/docs/user-message.md +185 -121
- package/dist/mcp/generated/index.json +79 -9
- package/dist/mcp/index.js +0 -0
- package/package.json +20 -21
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<!-- AI SUMMARY -->
|
|
2
|
+
## 快速了解
|
|
3
|
+
|
|
4
|
+
汇总当前会话所有 AssistantMessage 的 artifacts,支持关键词搜索、列表选中与下载; 预览区委托 ArtifactPreviewHost:按类型走 text_from_download(html / markdown / md / txt / json) 或 preview_url_iframe(其余类型);download_url / preview_url 经 onArtifactClick 异步获取。 源码位置:src/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue。
|
|
5
|
+
|
|
6
|
+
### 关联组件
|
|
7
|
+
- **assistant-message** — 文件产物来源于 AssistantMessage.property.artifacts
|
|
8
|
+
- **chat-container** — 面板挂载在侧栏「文件产物」Tab(固定、不可关闭),并通过 onArtifactClick 异步取链
|
|
9
|
+
- **execution-summary** — 同为 ChatContainer 侧栏 Tab 面板,交互形态一致
|
|
10
|
+
- **message-loading** — ArtifactPreviewHost 取链 / 拉取正文过程使用 MessageLoading
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
<!-- FULL DOC -->
|
|
14
|
+
|
|
15
|
+
# FileArtifactPanel 文件产物预览
|
|
16
|
+
|
|
17
|
+
## 源码事实
|
|
18
|
+
|
|
19
|
+
- **源码位置**:`src/components/chat-message/assistant-message/message-artifacts/file-artifact-panel.vue`
|
|
20
|
+
- **能力域**:消息系统
|
|
21
|
+
- **能力说明**:汇总当前会话全部文件产物;左侧列表搜索与选中,右侧预览委托内部 `ArtifactPreviewHost`。
|
|
22
|
+
|
|
23
|
+
> **导出说明**:内部侧栏面板组件,**通常不直接使用**;由 `ChatContainer` 在「文件产物」Tab 内自动挂载。预览加载与渲染为同目录下 `artifact-preview/` 内部实现,不单独导出。
|
|
24
|
+
|
|
25
|
+
点击 AI 回复中的[文件卡片](/components/message/assistant-message)后,`ChatContainer` 侧栏会弹出固定的「文件产物」Tab,聚合展示当前会话**所有** `AssistantMessage` 的文件产物,并命中被点击的文件进行预览。
|
|
26
|
+
|
|
27
|
+
面板左侧为可搜索的文件列表,右侧为预览区。通常不需要直接使用,由 `ChatContainer` 在侧栏内自动渲染。
|
|
28
|
+
|
|
29
|
+
## 核心能力
|
|
30
|
+
|
|
31
|
+
- **会话级聚合**:拍平当前会话所有 `AssistantMessage.property.artifacts`,统一在一个列表内展示
|
|
32
|
+
- **唯一命中**:同一会话可能出现多个消息 + 同名文件,文件名不可作唯一键,统一用 `messageUid#消息内下标#outputId` 生成全局唯一 id
|
|
33
|
+
- **关键词搜索**:按文件名实时过滤列表
|
|
34
|
+
- **异步取链**:`AIFileInfo` 本身不含 `url` / `previewUrl`,通过 `ChatContainer` 的 `onArtifactClick` 按 `outputId` 缓存获取 `download_url` / `preview_url`
|
|
35
|
+
- **职责拆分**:
|
|
36
|
+
- **面板本身**:列表、搜索、预览头(文件名 / 图标 / 下载)
|
|
37
|
+
- **`ArtifactPreviewHost`**:按策略加载正文或预览 URL,分派到对应 renderer;展示 loading / empty / error(含重试)
|
|
38
|
+
- **未传 `onArtifactClick`**:下载按钮隐藏,预览区展示无数据
|
|
39
|
+
|
|
40
|
+
## 基础用法
|
|
41
|
+
|
|
42
|
+
面板**未从包入口导出**,业务侧请走下方「业务接入」;下列示例仅用于文档站 / 本地调试(与 `ExecutionSummary` 文档站写法一致:相对路径引入 + 自行挂 Provider)。
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<template>
|
|
46
|
+
<div style="height: 480px; border: 1px solid #dcdee5; border-radius: 8px; overflow: hidden;">
|
|
47
|
+
<FileArtifactPanel
|
|
48
|
+
:active-id="activeArtifactId"
|
|
49
|
+
:artifacts="sessionArtifacts"
|
|
50
|
+
@select="handleSelect"
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script setup lang="ts">
|
|
56
|
+
import { shallowRef } from 'vue'
|
|
57
|
+
import { buildArtifactId, useArtifactPreviewProvider } from '@blueking/chat-x'
|
|
58
|
+
import type { AIFileInfo, SessionArtifact } from '@blueking/chat-x'
|
|
59
|
+
// 内部组件:仅文档 / 调试;业务请用 ChatContainer 自动挂载
|
|
60
|
+
import FileArtifactPanel from './message-artifacts/file-artifact-panel.vue'
|
|
61
|
+
|
|
62
|
+
const files: AIFileInfo[] = [
|
|
63
|
+
{ name: '周报.html', outputId: 'a-html', size: 10240, type: 'html' },
|
|
64
|
+
{ name: '说明.md', outputId: 'a-md', size: 8192, type: 'md' },
|
|
65
|
+
{ name: '纪要.txt', outputId: 'a-txt', size: 4096, type: 'txt' },
|
|
66
|
+
{ name: '配置.json', outputId: 'a-json', size: 2048, type: 'json' },
|
|
67
|
+
{ name: '立项.pdf', outputId: 'a-pdf', size: 204800, type: 'pdf' },
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
const sessionArtifacts: SessionArtifact[] = files.map((file, index) => ({
|
|
71
|
+
...file,
|
|
72
|
+
artifactId: buildArtifactId('msg-1', index, file.outputId),
|
|
73
|
+
messageUid: 'msg-1',
|
|
74
|
+
}))
|
|
75
|
+
|
|
76
|
+
useArtifactPreviewProvider({
|
|
77
|
+
getOnArtifactClick: () => async file => {
|
|
78
|
+
// 文本类返回可 fetch 的 download_url;iframe 类返回 preview_url
|
|
79
|
+
const res = await api.getArtifactUrls(file.outputId)
|
|
80
|
+
return { download_url: res.download_url, preview_url: res.preview_url }
|
|
81
|
+
},
|
|
82
|
+
onOpen: () => {},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const activeArtifactId = shallowRef(sessionArtifacts[0].artifactId)
|
|
86
|
+
const handleSelect = (id: string) => {
|
|
87
|
+
activeArtifactId.value = id
|
|
88
|
+
}
|
|
89
|
+
</script>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**渲染效果**(点击左侧列表切换类型;文本类直渲染,PDF 走 iframe。Mock 取链约 600ms)
|
|
93
|
+
|
|
94
|
+
## 业务接入(ChatContainer)
|
|
95
|
+
|
|
96
|
+
日常用法是给容器传 `messages`(含 `property.artifacts`)与 `onArtifactClick`,点击文件卡片即可打开侧栏面板:
|
|
97
|
+
|
|
98
|
+
```vue
|
|
99
|
+
<template>
|
|
100
|
+
<ChatContainer
|
|
101
|
+
v-model="input"
|
|
102
|
+
:messages="messages"
|
|
103
|
+
:on-artifact-click="onArtifactClick"
|
|
104
|
+
@send-message="handleSend"
|
|
105
|
+
/>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<script setup lang="ts">
|
|
109
|
+
import { ref, shallowRef } from 'vue'
|
|
110
|
+
import {
|
|
111
|
+
ChatContainer,
|
|
112
|
+
MessageRole,
|
|
113
|
+
MessageStatus,
|
|
114
|
+
type AIFileInfo,
|
|
115
|
+
type Message,
|
|
116
|
+
} from '@blueking/chat-x'
|
|
117
|
+
|
|
118
|
+
const input = ref('')
|
|
119
|
+
const messages = shallowRef<Message[]>([
|
|
120
|
+
{
|
|
121
|
+
id: 'u1',
|
|
122
|
+
messageId: 'u1',
|
|
123
|
+
role: MessageRole.User,
|
|
124
|
+
status: MessageStatus.Complete,
|
|
125
|
+
content: '整理本周评审材料',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'a1',
|
|
129
|
+
messageId: 'a1',
|
|
130
|
+
uid: 'assistant-uid-1',
|
|
131
|
+
role: MessageRole.Assistant,
|
|
132
|
+
status: MessageStatus.Complete,
|
|
133
|
+
content: '已生成评审材料,点击卡片可在侧栏预览:',
|
|
134
|
+
property: {
|
|
135
|
+
artifacts: [
|
|
136
|
+
{ name: '周报.html', outputId: 'a-html', size: 10240, type: 'html' },
|
|
137
|
+
{ name: '说明.md', outputId: 'a-md', size: 8192, type: 'md' },
|
|
138
|
+
{ name: '配置.json', outputId: 'a-json', size: 2048, type: 'json' },
|
|
139
|
+
{ name: '立项.pdf', outputId: 'a-pdf', size: 204800, type: 'pdf' },
|
|
140
|
+
] satisfies AIFileInfo[],
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
])
|
|
144
|
+
|
|
145
|
+
const onArtifactClick = async (file: AIFileInfo) => {
|
|
146
|
+
const res = await api.getArtifactUrls(file.outputId)
|
|
147
|
+
return {
|
|
148
|
+
download_url: res.download_url,
|
|
149
|
+
preview_url: res.preview_url,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const handleSend = () => {
|
|
154
|
+
/* ... */
|
|
155
|
+
}
|
|
156
|
+
</script>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 触发链路
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
ArtifactFileCard(点击文件卡片)
|
|
163
|
+
└─ useArtifactPreviewConsumer().openPreview({ file, index, messageUid })
|
|
164
|
+
└─ useArtifactPreviewProvider(ChatContainer 内)
|
|
165
|
+
├─ 记录命中文件 activeArtifactId
|
|
166
|
+
└─ onOpen → addCustomTab('file-artifact') 展开并选中侧栏 Tab
|
|
167
|
+
└─ FileArtifactPanel
|
|
168
|
+
├─ 列表 @select → setActiveArtifactId
|
|
169
|
+
├─ 下载 → resolveArtifactUrls + triggerArtifactDownload
|
|
170
|
+
└─ ArtifactPreviewHost
|
|
171
|
+
├─ useArtifactPreviewLoader(策略 + fetch / 取链,防竞态)
|
|
172
|
+
└─ HtmlPreview | MarkdownPreview | TxtPreview | UrlIframePreview
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- 文件卡片通过 `useArtifactPreviewConsumer` 注入预览上下文,无 Provider 时卡片不可点击(兜底 `undefined`)
|
|
176
|
+
- `ChatContainer` 通过 `useArtifactPreviewProvider` 提供上下文,并把「打开侧栏 Tab」这一副作用以 `onOpen` 注入,保持 composable 职责单一
|
|
177
|
+
- 侧栏「文件产物」Tab 固定不可关闭,`order: -1` 排在「执行情况」之前;会话切换或无文件产物时自动移除
|
|
178
|
+
|
|
179
|
+
## 唯一 id 规则
|
|
180
|
+
|
|
181
|
+
同一会话可能存在多个 `AssistantMessage`,且不同消息里可能有同名文件,因此**文件名不可作为唯一键**。统一由 `buildArtifactId` 生成:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { buildArtifactId } from '@blueking/chat-x';
|
|
185
|
+
|
|
186
|
+
// messageUid#消息内下标#outputId
|
|
187
|
+
buildArtifactId('msg-a', 2, 'output-9'); // => 'msg-a#2#output-9'
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Provider 侧聚合与文件卡片侧透传必须使用同一规则,保证命中一致。
|
|
191
|
+
|
|
192
|
+
## 预览机制
|
|
193
|
+
|
|
194
|
+
预览由内部 `getArtifactPreviewStrategy(type)` 决定 **加载方式** 与 **渲染器**;面板不直接写死类型分支。
|
|
195
|
+
|
|
196
|
+
| 文件类型 | load | 取链字段 | renderer |
|
|
197
|
+
| -------- | ---- | -------- | -------- |
|
|
198
|
+
| `html` | `text_from_download` | `download_url` → `fetch` 正文 | `HtmlPreview`(`<iframe srcdoc>`) |
|
|
199
|
+
| `markdown` / `md` | `text_from_download` | 同上 | `MarkdownPreview`(`MarkdownContent`) |
|
|
200
|
+
| `txt` / `json` | `text_from_download` | 同上 | `TxtPreview`(`<pre>`) |
|
|
201
|
+
| 其余(如 `pdf` / `jpg`) | `preview_url_iframe` | `preview_url` | `UrlIframePreview`(`<iframe src>`,一般为后台转好的 PDF) |
|
|
202
|
+
|
|
203
|
+
> `md`(`AIFileType.Md`)为后台扩展名别名,与 `markdown`(`AIFileType.Markdown`)等价,共用 Markdown 直渲染。
|
|
204
|
+
|
|
205
|
+
### 加载态
|
|
206
|
+
|
|
207
|
+
| status | 表现 |
|
|
208
|
+
| ------ | ---- |
|
|
209
|
+
| `loading` | 预览区 [MessageLoading](/components/helper/message-loading) |
|
|
210
|
+
| `ready` | 对应 renderer 渲染 |
|
|
211
|
+
| `empty` | 「暂无可预览的文件」(无文件 / 未传 `onArtifactClick` / 缺所需 URL) |
|
|
212
|
+
| `error` | 「预览加载失败」+ 重试按钮 |
|
|
213
|
+
|
|
214
|
+
切换文件时 `useArtifactPreviewLoader` 用 `loadSeq` + `AbortController` 中断上一次 `fetch`,避免竞态覆盖。下载图标仍由面板用 bkui `Loading` spin 单独表达。
|
|
215
|
+
|
|
216
|
+
## 内部结构(不导出)
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
message-artifacts/
|
|
220
|
+
├── file-artifact-panel.vue # 列表 + 下载头 + 挂载 Host
|
|
221
|
+
└── artifact-preview/
|
|
222
|
+
├── artifact-preview-host.vue # 状态机 UI + 分派 renderer
|
|
223
|
+
├── preview-strategy.ts # getArtifactPreviewStrategy
|
|
224
|
+
├── use-artifact-preview-loader.ts
|
|
225
|
+
└── renderers/
|
|
226
|
+
├── html-preview.vue
|
|
227
|
+
├── markdown-preview.vue
|
|
228
|
+
├── txt-preview.vue
|
|
229
|
+
└── url-iframe-preview.vue
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## API
|
|
233
|
+
|
|
234
|
+
### Props
|
|
235
|
+
|
|
236
|
+
| 属性名 | 类型 | 必填 | 说明 |
|
|
237
|
+
| --------- | ------------------- | ---- | -------------------------------------- |
|
|
238
|
+
| activeId | `string` | ✓ | 当前命中的文件 id(`messageUid#index#outputId`) |
|
|
239
|
+
| artifacts | `SessionArtifact[]` | ✓ | 当前会话全部文件产物 |
|
|
240
|
+
|
|
241
|
+
### Events
|
|
242
|
+
|
|
243
|
+
| 事件名 | 参数 | 说明 |
|
|
244
|
+
| ------ | ----------------- | -------------------------- |
|
|
245
|
+
| select | `(id: string)` | 列表内切换选中文件,参数为文件 `artifactId` |
|
|
246
|
+
|
|
247
|
+
### Slots / Expose
|
|
248
|
+
|
|
249
|
+
无。
|
|
250
|
+
|
|
251
|
+
## 类型定义
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import type { AIFileInfo, SessionArtifact } from '@blueking/chat-x';
|
|
255
|
+
|
|
256
|
+
// 会话级文件产物:在 AIFileInfo 基础上补充命中所需字段
|
|
257
|
+
type SessionArtifact = AIFileInfo & {
|
|
258
|
+
artifactId: string; // 全局唯一 id:messageUid#index#outputId
|
|
259
|
+
messageUid: string; // 所属 AssistantMessage 的 uid
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
type AIFileInfo = {
|
|
263
|
+
name: string;
|
|
264
|
+
outputId: string;
|
|
265
|
+
size: number;
|
|
266
|
+
type: AIFileType;
|
|
267
|
+
};
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## 关联 Composable
|
|
271
|
+
|
|
272
|
+
预览**命中与取链**由 [useArtifactPreview](/composables/use-artifact-preview) 提供(Provider / Consumer + `ARTIFACT_PREVIEW_TOKEN`)。**正文加载与渲染**由内部 `useArtifactPreviewLoader` + `ArtifactPreviewHost` 完成,不在该 composable 内。
|
|
273
|
+
|
|
274
|
+
## 关联组件
|
|
275
|
+
|
|
276
|
+
- [AssistantMessage](/components/message/assistant-message) — 文件产物来源(`property.artifacts`)
|
|
277
|
+
- [ChatContainer](/components/setup/chat-container) — 侧栏「文件产物」Tab 挂载场景,提供 `onArtifactClick`
|
|
278
|
+
- [MessageLoading](/components/helper/message-loading) — Host 预览区异步加载态
|
|
279
|
+
- [ExecutionSummary](/components/agent/execution-summary) — 同为侧栏 Tab 面板
|
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
<!-- FULL DOC -->
|
|
12
12
|
|
|
13
13
|
# InfoMessage 信息消息
|
|
14
|
+
|
|
14
15
|
## 源码事实
|
|
15
16
|
|
|
16
17
|
- **源码位置**:`src/components/chat-message/info-message/info-message.vue`
|
|
17
18
|
- **能力域**:消息系统
|
|
18
19
|
- **能力说明**:渲染居中的系统信息提示。
|
|
19
20
|
|
|
20
|
-
>
|
|
21
|
+
> **导出说明**:`InfoMessage` **未**从包入口导出(入口同名是 TS interface)。消费方经 `MessageRender` / `MessageContainer` 使用。下文 `InfoMessageComp` 为文档站内部示例。
|
|
21
22
|
|
|
22
23
|
系统信息分隔组件,在聊天消息列表中以**居中虚线分隔条**的形式展示非对话类信息(会话重置、时间节点、状态变更等)。
|
|
23
24
|
|
|
@@ -35,13 +36,19 @@
|
|
|
35
36
|
|
|
36
37
|
```vue
|
|
37
38
|
<template>
|
|
38
|
-
<
|
|
39
|
+
<MessageRender :message="message" />
|
|
39
40
|
</template>
|
|
40
41
|
|
|
41
42
|
<script setup lang="ts">
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
const
|
|
43
|
+
import { MessageRender, MessageRole, MessageStatus } from '@blueking/chat-x';
|
|
44
|
+
|
|
45
|
+
const message = {
|
|
46
|
+
id: '1',
|
|
47
|
+
messageId: '1',
|
|
48
|
+
role: MessageRole.Info,
|
|
49
|
+
content: '以下是新的对话',
|
|
50
|
+
status: MessageStatus.Complete,
|
|
51
|
+
};
|
|
45
52
|
</script>
|
|
46
53
|
```
|
|
47
54
|
|
|
@@ -53,11 +60,20 @@
|
|
|
53
60
|
|
|
54
61
|
```vue
|
|
55
62
|
<template>
|
|
56
|
-
<
|
|
63
|
+
<MessageRender :message="message" />
|
|
57
64
|
</template>
|
|
58
65
|
|
|
59
66
|
<script setup lang="ts">
|
|
60
|
-
import {
|
|
67
|
+
import { MessageRender, MessageRole, MessageStatus } from '@blueking/chat-x';
|
|
68
|
+
|
|
69
|
+
// 运行时 content 兼容 string[](TS 类型声明多为 string)
|
|
70
|
+
const message = {
|
|
71
|
+
id: '1',
|
|
72
|
+
messageId: '1',
|
|
73
|
+
role: MessageRole.Info,
|
|
74
|
+
content: ['会话已重置', '以下是新的对话'],
|
|
75
|
+
status: MessageStatus.Complete,
|
|
76
|
+
};
|
|
61
77
|
</script>
|
|
62
78
|
```
|
|
63
79
|
|
|
@@ -12,46 +12,60 @@
|
|
|
12
12
|
<!-- FULL DOC -->
|
|
13
13
|
|
|
14
14
|
# LoadingMessage 加载中消息
|
|
15
|
+
|
|
15
16
|
## 源码事实
|
|
16
17
|
|
|
17
18
|
- **源码位置**:`src/components/chat-message/loading-message/loading-message.vue`
|
|
18
19
|
- **能力域**:消息系统
|
|
19
20
|
- **能力说明**:消息列表中的加载占位,默认使用 AiLoading,也支持默认插槽覆盖。
|
|
20
21
|
|
|
21
|
-
>
|
|
22
|
+
> **导出说明**:`LoadingMessage` **未**从包入口导出。消费方经 `MessageRender`(`role: 'loading'`)或由 `MessageContainer` 自动注入。下文 `LoadingMessageComp` 为文档站内部示例。
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
加载等待状态组件:`AiLoading`(18px)+ 默认文案「请求中...」。可通过默认插槽自定义文案。
|
|
24
25
|
|
|
25
|
-
>
|
|
26
|
+
> **提示**:通常**不需要手动使用**,`MessageContainer` / `useMessageGroup` 会在满足条件时自动注入。
|
|
26
27
|
|
|
27
28
|
## 渲染效果
|
|
28
29
|
|
|
29
30
|
## 基础用法
|
|
30
31
|
|
|
31
|
-
组件无 Props
|
|
32
|
+
组件无 Props。文档站内部示例:
|
|
33
|
+
|
|
34
|
+
```vue
|
|
35
|
+
<template>
|
|
36
|
+
<LoadingMessageComp />
|
|
37
|
+
</template>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
消费方经 `MessageRender`:
|
|
32
41
|
|
|
33
42
|
```vue
|
|
34
43
|
<template>
|
|
35
|
-
<
|
|
44
|
+
<MessageRender
|
|
45
|
+
:message="{
|
|
46
|
+
id: 'loading',
|
|
47
|
+
messageId: '',
|
|
48
|
+
role: MessageRole.Loading,
|
|
49
|
+
content: '',
|
|
50
|
+
status: MessageStatus.Pending,
|
|
51
|
+
}"
|
|
52
|
+
/>
|
|
36
53
|
</template>
|
|
37
54
|
|
|
38
55
|
<script setup lang="ts">
|
|
39
|
-
import {
|
|
56
|
+
import { MessageRender, MessageRole, MessageStatus } from '@blueking/chat-x';
|
|
40
57
|
</script>
|
|
41
58
|
```
|
|
42
59
|
|
|
43
60
|
## 自定义加载文案
|
|
44
61
|
|
|
45
|
-
|
|
62
|
+
通过默认插槽覆盖「请求中...」:
|
|
46
63
|
|
|
47
64
|
```vue
|
|
48
65
|
<template>
|
|
49
|
-
|
|
66
|
+
<!-- 文档站内部示例 -->
|
|
67
|
+
<LoadingMessageComp>正在思考中,请稍候...</LoadingMessageComp>
|
|
50
68
|
</template>
|
|
51
|
-
|
|
52
|
-
<script setup lang="ts">
|
|
53
|
-
import { LoadingMessage } from '@blueking/chat-x';
|
|
54
|
-
</script>
|
|
55
69
|
```
|
|
56
70
|
|
|
57
71
|
## 动画说明
|
|
@@ -67,15 +81,18 @@
|
|
|
67
81
|
|
|
68
82
|
## 在 MessageContainer 中的自动注入
|
|
69
83
|
|
|
70
|
-
`
|
|
84
|
+
`useMessageGroup` 构建分组时,若**最后一条为用户消息**且 **`renderMode !== RenderMode.Share`**,自动在末尾追加 Loading 组:
|
|
71
85
|
|
|
72
86
|
```typescript
|
|
73
|
-
//
|
|
74
|
-
|
|
87
|
+
// use-message-group.ts(简化)
|
|
88
|
+
const shouldAppendLoading =
|
|
89
|
+
messages.at(-1)?.role === MessageRole.User && renderMode !== RenderMode.Share;
|
|
90
|
+
|
|
91
|
+
if (shouldAppendLoading) {
|
|
75
92
|
list.push({
|
|
76
93
|
messages: [
|
|
77
94
|
{
|
|
78
|
-
role: MessageRole.Loading,
|
|
95
|
+
role: MessageRole.Loading,
|
|
79
96
|
content: '',
|
|
80
97
|
status: MessageStatus.Pending,
|
|
81
98
|
id: 'loading',
|
|
@@ -87,7 +104,8 @@ if (messages.at(-1)?.role === MessageRole.User) {
|
|
|
87
104
|
}
|
|
88
105
|
```
|
|
89
106
|
|
|
90
|
-
|
|
107
|
+
- 下一条 AI 消息到来后,末尾不再是 user,Loading 组自动消失
|
|
108
|
+
- **分享预览**(`renderMode === RenderMode.Share`)**不会**注入 Loading,避免分享页出现「请求中」占位
|
|
91
109
|
|
|
92
110
|
**触发示例**:
|
|
93
111
|
|
|
@@ -127,6 +145,7 @@ if (messages.at(-1)?.role === MessageRole.User) {
|
|
|
127
145
|
- **默认插槽**:可通过默认插槽自定义加载文案,未传入时显示内置的 "请求中..."
|
|
128
146
|
- **i18n 支持**:默认文案 "请求中..." 通过内置 `t()` 函数处理,英文环境自动显示 "Requesting..."
|
|
129
147
|
- **选择模式**:`MessageContainer` 开启 `enableSelection` 时,Loading 消息组不显示复选框
|
|
148
|
+
- **分享模式**:`renderMode === RenderMode.Share` 时不自动注入 Loading
|
|
130
149
|
- **自动生命周期**:Loading 组随消息列表变化自动插入/移除,无需手动控制
|
|
131
150
|
|
|
132
151
|
## API
|
|
@@ -13,15 +13,16 @@
|
|
|
13
13
|
<!-- FULL DOC -->
|
|
14
14
|
|
|
15
15
|
# MessageRender 消息渲染器
|
|
16
|
+
|
|
16
17
|
## 源码事实
|
|
17
18
|
|
|
18
19
|
- **源码位置**:`src/components/chat-message/message-render/message-render.vue`
|
|
19
20
|
- **能力域**:消息系统
|
|
20
|
-
- **能力说明**:按 message.role 分发到用户、助手、工具、推理、活动、中断等消息组件。
|
|
21
|
+
- **能力说明**:按 `message.role` 分发到用户、助手、工具、推理、活动、中断等消息组件。
|
|
21
22
|
|
|
22
|
-
>
|
|
23
|
+
> **导出说明**:`MessageRender` **已**从 `@blueking/chat-x` 包入口导出,消费方可直接 import。下文 `MessageRenderComp` 为文档站相对路径 demo,与包入口行为一致。
|
|
23
24
|
|
|
24
|
-
统一的消息渲染入口,通过 `message.role`
|
|
25
|
+
统一的消息渲染入口,通过 `message.role` 派发到对应子组件;由单个 `computed` 完成,无额外状态。
|
|
25
26
|
|
|
26
27
|
## 渲染架构
|
|
27
28
|
|
|
@@ -151,7 +152,7 @@ MessageRender
|
|
|
151
152
|
|
|
152
153
|
### 流式输出(streaming)
|
|
153
154
|
|
|
154
|
-
`status: 'streaming'`
|
|
155
|
+
`status: 'streaming'` 时,默认回退的 `ContentRender` / `MarkdownContent` 会按流式规则补全未闭合语法;内容由外部逐步追加:
|
|
155
156
|
|
|
156
157
|
```vue
|
|
157
158
|
<script setup lang="ts">
|
|
@@ -202,65 +203,74 @@ MessageRender
|
|
|
202
203
|
|
|
203
204
|
## 自定义内容渲染(default slot)
|
|
204
205
|
|
|
205
|
-
`default` slot **仅对 `role: 'assistant'` 生效**,用于替换默认的 `ContentRender
|
|
206
|
+
`default` slot **仅对 `role: 'assistant'` 生效**,用于替换默认的 `ContentRender`。
|
|
207
|
+
|
|
208
|
+
未提供 slot 时,内部回退为:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
h(ContentRender, { content: message.content || '', status: message.status }, /* codeHeader */)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
自定义 slot 时,运行时参数来自 `AssistantMessage` 的 `v-bind`,**仅保证 `{ content }`**。需要 `status` 时请从外层 `message.status` 读取(不要依赖 slot 内的 `status`)。
|
|
206
215
|
|
|
207
216
|
```vue
|
|
208
217
|
<template>
|
|
209
|
-
<MessageRender
|
|
210
|
-
|
|
211
|
-
:on-action="handleAction"
|
|
212
|
-
>
|
|
213
|
-
<template #default="{ content, status }">
|
|
214
|
-
<!-- 完全接管内容区域渲染 -->
|
|
218
|
+
<MessageRender :message="message">
|
|
219
|
+
<template #default="{ content }">
|
|
215
220
|
<MyMarkdownRenderer
|
|
216
221
|
:content="content"
|
|
217
|
-
:streaming="status === 'streaming'"
|
|
222
|
+
:streaming="message.status === 'streaming'"
|
|
218
223
|
/>
|
|
219
224
|
</template>
|
|
220
225
|
</MessageRender>
|
|
221
226
|
</template>
|
|
222
227
|
```
|
|
223
228
|
|
|
224
|
-
|
|
229
|
+
| 参数 | 类型 | 说明 |
|
|
230
|
+
| --------- | -------- | ---------------------------- |
|
|
231
|
+
| `content` | `string` | 消息内容(运行时保证) |
|
|
225
232
|
|
|
226
|
-
|
|
227
|
-
| --------- | --------------- | ------------ |
|
|
228
|
-
| `content` | `string` | 消息内容 |
|
|
229
|
-
| `status` | `MessageStatus` | 当前消息状态 |
|
|
233
|
+
## 与 MessageContainer / ChatContainer 配合
|
|
230
234
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
在 `MessageContainer` 的 `default` slot 中使用,可替换默认的 `MessageRender` 渲染逻辑:
|
|
235
|
+
自定义 `ChatContainer` 的 `#message` 时,插槽参数只有 `message` / `messageToolsStatus` / `onInterruptResume`。用户消息工具相关回调需由外层自行绑定透传,否则删除/编辑/复制/引用会失效(AI 消息工具栏在 `MessageContainer` 内渲染,不受 `#message` 影响):
|
|
234
236
|
|
|
235
237
|
```vue
|
|
236
238
|
<template>
|
|
237
|
-
<
|
|
239
|
+
<ChatContainer
|
|
238
240
|
:messages="messages"
|
|
239
241
|
:message-status="messageStatus"
|
|
240
242
|
:on-agent-action="handleAgentAction"
|
|
241
243
|
:on-user-action="handleUserAction"
|
|
242
|
-
|
|
244
|
+
:common-tippy-options="commonTippyOptions"
|
|
243
245
|
>
|
|
244
|
-
<template #
|
|
245
|
-
<!-- 自定义 MessageRender 的行为 -->
|
|
246
|
+
<template #message="{ message, messageToolsStatus, onInterruptResume }">
|
|
246
247
|
<MessageRender
|
|
247
248
|
:message="message"
|
|
248
249
|
:message-tools-status="messageToolsStatus"
|
|
249
250
|
:on-action="handleUserAction"
|
|
250
|
-
:on-input-confirm="
|
|
251
|
+
:on-input-confirm="(content, docSchema) => handleUserInputConfirm(message, content, docSchema)"
|
|
252
|
+
:on-shortcut-confirm="formModel => handleUserShortcutConfirm(message, formModel)"
|
|
253
|
+
:tippy-options="commonTippyOptions"
|
|
254
|
+
:on-interrupt-resume="onInterruptResume"
|
|
251
255
|
>
|
|
252
256
|
<template
|
|
253
257
|
v-if="message.role === 'assistant'"
|
|
254
|
-
#default="{ content
|
|
258
|
+
#default="{ content }"
|
|
255
259
|
>
|
|
256
260
|
<MyCustomContent
|
|
257
261
|
:content="content"
|
|
258
|
-
:status="status"
|
|
262
|
+
:status="message.status"
|
|
263
|
+
/>
|
|
264
|
+
</template>
|
|
265
|
+
<template #codeHeader="{ language, token }">
|
|
266
|
+
<MyCodeActions
|
|
267
|
+
:language="language"
|
|
268
|
+
:token="token"
|
|
259
269
|
/>
|
|
260
270
|
</template>
|
|
261
271
|
</MessageRender>
|
|
262
272
|
</template>
|
|
263
|
-
</
|
|
273
|
+
</ChatContainer>
|
|
264
274
|
</template>
|
|
265
275
|
```
|
|
266
276
|
|
|
@@ -280,11 +290,11 @@ slot 参数类型与 `AssistantMessage` 的 slot 保持一致(`Partial<Assista
|
|
|
280
290
|
|
|
281
291
|
### Slots
|
|
282
292
|
|
|
283
|
-
| 插槽名 | 参数
|
|
284
|
-
| ---------------- |
|
|
285
|
-
| answeredQuestion | `{ item, index, status }`
|
|
286
|
-
| codeHeader | `{ language: string; token: Token[] }`
|
|
287
|
-
| default | `{ content: string
|
|
293
|
+
| 插槽名 | 参数 | 说明 |
|
|
294
|
+
| ---------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
295
|
+
| answeredQuestion | `{ item, index, status }` | 自定义 UserQuestion 已回答回显,透传给 InterruptMessageRender → UserQuestionAnsweredCard 的 `#answer` |
|
|
296
|
+
| codeHeader | `{ language: string; token: Token[] }` | 代码块头部自定义操作区域,透传给 ContentRender → MarkdownContent → CodeContent;**仅对 assistant 生效** |
|
|
297
|
+
| default | `{ content: string }` | 替换 AssistantMessage 内容区;**仅对 assistant 生效**;运行时仅保证 `content`(`status` 请读外层 message) |
|
|
288
298
|
|
|
289
299
|
## 消息类型映射
|
|
290
300
|
|
|
@@ -314,6 +324,7 @@ enum MessageRole {
|
|
|
314
324
|
Tool = 'tool',
|
|
315
325
|
Activity = 'activity',
|
|
316
326
|
Loading = 'loading',
|
|
327
|
+
Interrupt = 'interrupt',
|
|
317
328
|
}
|
|
318
329
|
|
|
319
330
|
// 消息状态
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
<!-- FULL DOC -->
|
|
13
13
|
|
|
14
14
|
# ReasoningMessage 推理消息
|
|
15
|
+
|
|
15
16
|
## 源码事实
|
|
16
17
|
|
|
17
18
|
- **源码位置**:`src/components/chat-message/reasoning-message/reasoning-message.vue`
|
|
18
19
|
- **能力域**:消息系统
|
|
19
20
|
- **能力说明**:渲染推理过程,覆盖加载、错误与 Markdown 内容展示。
|
|
20
21
|
|
|
21
|
-
>
|
|
22
|
+
> **导出说明**:`ReasoningMessage` **未**从包入口导出(入口同名是 TS interface)。消费方经 `MessageRender` / `MessageContainer` 使用。下文 `ReasoningMessageComp` 为文档站内部示例。
|
|
22
23
|
|
|
23
24
|
AI 思维链(Chain-of-Thought)推理过程展示组件。由**可点击标题栏**和**内容区域**组成,内容区支持 Markdown 渲染。`duration` 传入后自动折叠一次,用户可随时点击标题展开/收起。
|
|
24
25
|
|
|
@@ -44,17 +45,19 @@ AI 思维链(Chain-of-Thought)推理过程展示组件。由**可点击标
|
|
|
44
45
|
|
|
45
46
|
```vue
|
|
46
47
|
<template>
|
|
47
|
-
<
|
|
48
|
-
:content="content"
|
|
49
|
-
:status="status"
|
|
50
|
-
/>
|
|
48
|
+
<MessageRender :message="message" />
|
|
51
49
|
</template>
|
|
52
50
|
|
|
53
51
|
<script setup lang="ts">
|
|
54
|
-
import {
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
52
|
+
import { MessageRender, MessageRole, MessageStatus } from '@blueking/chat-x';
|
|
53
|
+
|
|
54
|
+
const message = {
|
|
55
|
+
id: '1',
|
|
56
|
+
messageId: '1',
|
|
57
|
+
role: MessageRole.Reasoning,
|
|
58
|
+
status: MessageStatus.Complete,
|
|
59
|
+
content: ['让我分析一下这个问题...', '首先需要考虑以下几个方面...'],
|
|
60
|
+
};
|
|
58
61
|
</script>
|
|
59
62
|
```
|
|
60
63
|
|
|
@@ -113,11 +116,12 @@ const { stop } = watch(
|
|
|
113
116
|
通过 `v-model:collapsed` 从外部读取或设置折叠状态:
|
|
114
117
|
|
|
115
118
|
```vue
|
|
119
|
+
<!-- 文档站内部示例:组件本体支持 v-model:collapsed;消费方一般经 MessageRender 渲染 -->
|
|
116
120
|
<template>
|
|
117
121
|
<button @click="collapsed = !collapsed">
|
|
118
122
|
{{ collapsed ? '展开推理' : '收起推理' }}
|
|
119
123
|
</button>
|
|
120
|
-
<
|
|
124
|
+
<ReasoningMessageComp
|
|
121
125
|
v-model:collapsed="collapsed"
|
|
122
126
|
:content="content"
|
|
123
127
|
:status="status"
|
|
@@ -127,7 +131,7 @@ const { stop } = watch(
|
|
|
127
131
|
|
|
128
132
|
<script setup lang="ts">
|
|
129
133
|
import { ref } from 'vue';
|
|
130
|
-
import {
|
|
134
|
+
import { MessageStatus } from '@blueking/chat-x';
|
|
131
135
|
|
|
132
136
|
const collapsed = ref(false);
|
|
133
137
|
const status = MessageStatus.Complete;
|