@blueking/chat-x 0.0.45-beta.4 → 0.0.45-beta.5
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/components/chat-input/chat-input.vue.d.ts +5 -2
- package/dist/components/chat-message/interrupt-message/user-question/use-user-question.d.ts +1 -1
- package/dist/index.js +15 -27
- package/dist/index.js.map +1 -1
- package/dist/mcp/generated/docs/chat-container.md +27 -7
- package/dist/mcp/generated/docs/chat-input.md +33 -1
- package/dist/mcp/generated/docs/user-question-card.md +19 -2
- package/dist/mcp/generated/index.json +1 -1
- package/package.json +1 -1
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
- **消息分组**:内置 `useMessageGroup` 自动处理消息分组、Tool 合并、Loading 注入
|
|
31
31
|
- **输入区状态推导**:传给 `MessageContainer` 与 `ChatInput` 的 `messageStatus` 为内部计算值 `inputStatus`:当分组中存在 id 为 `LOADING_MESSAGE_ID`(`'__loading__'`,由 `useMessageGroup` 注入的占位 Loading 消息)时,对内使用 `MessageStatus.Fetching`;否则使用外部传入的 `messageStatus`。用于在「已发用户消息、尚未流式」阶段与流式中一致地展示停止能力,并避免输入区重复发送
|
|
32
32
|
- **待审批发送阻塞**:当消息中存在 `AIDevToolApproval` 且状态为 `pending` / `draft` 的中断项时,`useMessageGroup` 会返回待审批提示,容器在输入区上方展示提示并通过 `ChatInput.sendDisabledTip` 禁止继续发送
|
|
33
|
-
- **用户问题中断**:当消息中存在待回答 `UserQuestion` 中断时,容器会在输入区上方挂载 `UserQuestionCard
|
|
33
|
+
- **用户问题中断**:当消息中存在待回答 `UserQuestion` 中断时,容器会在输入区上方挂载 `UserQuestionCard`;结构化作答走 `onInterruptResume`,用户在输入框直接发送时走 `onSendMessage` 并在第三参数附带 skip 用的 `payload`(`status: 'cancelled'`)与 `interrupt`,且不会自动清空输入框
|
|
34
34
|
- **执行摘要**:侧边栏展示工具调用 / FlowAgent 执行记录,支持关键词搜索和对话定位
|
|
35
35
|
- **侧栏全屏**:Tab 栏右侧提供全屏/退出全屏按钮,基于 `useFullScreen` 将侧栏区域(`.ai-full-screen-wrapper`)以浏览器原生全屏展示;全屏时 Tippy 的 `appendTo` 自动切换为全屏容器,避免 tooltip 被遮挡
|
|
36
36
|
- **自定义 Tab**:通过 `useCustomTabProvider` 支持动态添加自定义 Tab(如节点详情)
|
|
@@ -368,7 +368,10 @@ ai-chat-container(:data-ai-size="size")
|
|
|
368
368
|
|
|
369
369
|
## 用户问题中断
|
|
370
370
|
|
|
371
|
-
当会话中最近一条待处理 interrupt 包含 `InterruptReason.UserQuestion` 时,`ChatContainer` 会在 `ChatInput` 上方显示 [UserQuestionCard](/components/agent/user-question-card)
|
|
371
|
+
当会话中最近一条待处理 interrupt 包含 `InterruptReason.UserQuestion` 时,`ChatContainer` 会在 `ChatInput` 上方显示 [UserQuestionCard](/components/agent/user-question-card)。
|
|
372
|
+
|
|
373
|
+
- **结构化作答**:用户在卡片内完成选择或点击「跳过」后,通过 `onInterruptResume(payload, interrupt)` 回传 `UserQuestionResume`。
|
|
374
|
+
- **输入框发送**:用户也可在输入框直接点击发送;容器会调用 `onSendMessage(content, docSchema, options)`,其中 `options.interrupt` 为当前激活的 UserQuestion,`options.payload` 为 `buildSkipResumePayload` 生成的 skip resume(`status: 'cancelled'`,`answers: []`)。此时**不会自动清空**输入框,由业务侧在 `onSendMessage` 内决定如何处理 `content` 与中断恢复。
|
|
372
375
|
|
|
373
376
|
```vue
|
|
374
377
|
<template>
|
|
@@ -382,18 +385,35 @@ ai-chat-container(:data-ai-size="size")
|
|
|
382
385
|
</template>
|
|
383
386
|
|
|
384
387
|
<script setup lang="ts">
|
|
385
|
-
import {
|
|
388
|
+
import {
|
|
389
|
+
type OnInterruptResume,
|
|
390
|
+
type UserMessage,
|
|
391
|
+
type TagSchema,
|
|
392
|
+
type Interrupt,
|
|
393
|
+
type InterruptResume,
|
|
394
|
+
} from '@blueking/chat-x';
|
|
386
395
|
|
|
387
396
|
const handleInterruptResume: OnInterruptResume = async (payload, interrupt) => {
|
|
388
|
-
//
|
|
389
|
-
// 自由文本输入也会转换为 label: 'others' 的单条回答
|
|
397
|
+
// UserQuestionCard 完成 / 跳过时 payload 为 UserQuestionResume
|
|
390
398
|
await resumeAgent({ interruptId: interrupt.id, resume: payload });
|
|
391
399
|
};
|
|
400
|
+
|
|
401
|
+
const handleSendMessage = async (
|
|
402
|
+
content: UserMessage['content'],
|
|
403
|
+
docSchema: TagSchema,
|
|
404
|
+
options?: { interrupt?: Interrupt; payload?: InterruptResume },
|
|
405
|
+
) => {
|
|
406
|
+
if (options?.interrupt && options?.payload) {
|
|
407
|
+
// 存在 UserQuestion 时发送:附带 skip resume,content 仍为输入框文本
|
|
408
|
+
await resumeAgent({ interruptId: options.interrupt.id, resume: options.payload });
|
|
409
|
+
// 业务侧自行决定是否将 content 作为新用户消息继续发送
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
await sendMessage(content, docSchema);
|
|
413
|
+
};
|
|
392
414
|
</script>
|
|
393
415
|
```
|
|
394
416
|
|
|
395
|
-
如果没有传入 `onInterruptResume`,输入框自由文本不会被中断逻辑截获,仍然走普通 `onSendMessage`,避免输入被静默清空。
|
|
396
|
-
|
|
397
417
|
## 自定义消息组渲染
|
|
398
418
|
|
|
399
419
|
通过 `#group` 插槽可替换单个消息组的默认内容,透传至内部 `MessageContainer`。外层消息组容器(`id`、hover、选中背景)仍由 `MessageContainer` 管理。
|
|
@@ -93,6 +93,31 @@ chat-input-container
|
|
|
93
93
|
|
|
94
94
|
> **实现细节**:组件内部用 `messageState` 计算属性决定实际按钮状态:当 `messageStatus` 为 `pending`、`streaming` 或 `fetching` 时直接使用该状态(确保停止按钮始终可用);否则当输入为空或仅含空白字符时强制为 `disabled`,其余情况使用 `messageStatus` 的值。`fetching` 时按 Enter **不会**触发发送(避免请求中与 Loading 占位阶段重复提交)。
|
|
95
95
|
|
|
96
|
+
### onSendMessage 第三参数 options(UserQuestion 上下文)
|
|
97
|
+
|
|
98
|
+
`ChatInput` 自身调用 `onSendMessage` 时只传前两个参数。当组件被 [ChatContainer](/components/setup/chat-container) 包裹且存在待回答 `UserQuestion` 中断时,容器会在用户点击发送时注入第三个参数:
|
|
99
|
+
|
|
100
|
+
| 字段 | 类型 | 说明 |
|
|
101
|
+
| ----------- | ------------------ | -------------------------------------------------------------------- |
|
|
102
|
+
| `interrupt` | `Interrupt` | 当前激活的 `UserQuestionInterrupt` |
|
|
103
|
+
| `payload` | `InterruptResume` | skip resume(`status: 'cancelled'`,`payload.answers` 为空数组) |
|
|
104
|
+
|
|
105
|
+
此场景下容器**不会**自动清空 `modelValue`,业务侧需在 `onSendMessage` 内自行处理消息发送与 `resumeAgent` 的先后顺序。结构化作答仍通过 `UserQuestionCard` → `onInterruptResume` 完成。
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const handleSendMessage = async (
|
|
109
|
+
content: UserMessage['content'],
|
|
110
|
+
docSchema: TagSchema,
|
|
111
|
+
options?: { interrupt?: Interrupt; payload?: InterruptResume },
|
|
112
|
+
) => {
|
|
113
|
+
if (options?.interrupt && options?.payload) {
|
|
114
|
+
await resumeAgent({ interruptId: options.interrupt.id, resume: options.payload });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
await sendMessage(content, docSchema);
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
96
121
|
### sendDisabledTip(业务阻塞发送)
|
|
97
122
|
|
|
98
123
|
当业务侧需要临时阻止发送但仍允许用户输入时,可传入 `sendDisabledTip`。组件会置灰发送按钮,按钮 tooltip 展示该文案,并拦截点击发送、按 Enter 发送和 `triggerSendMessage()`:
|
|
@@ -504,7 +529,7 @@ chat-input-container
|
|
|
504
529
|
| sendDisabledTip | `string` | - | - | 业务阻塞发送时的 tooltip 提示;传入后发送按钮置灰,点击、Enter 与 `triggerSendMessage()` 均不会发送 |
|
|
505
530
|
| supportUpload | `boolean` | `true` | - | 是否显示文件上传按钮 |
|
|
506
531
|
| tippyOptions | `AITippyProps` | — | - | 透传给 FileUploadBtn 和 InputAttachment 的 tooltip 配置 |
|
|
507
|
-
| onSendMessage | `(content: UserMessage['content'], docSchema: TagSchema) => Promise<void>` | - | - | 发送消息回调,无文件时 content
|
|
532
|
+
| onSendMessage | `(content: UserMessage['content'], docSchema: TagSchema, options?: { interrupt?: Interrupt; payload?: InterruptResume }) => Promise<void>` | - | - | 发送消息回调,无文件时 content 为字符串,有文件时为数组;经 [ChatContainer](/components/setup/chat-container) 使用时,存在待回答 UserQuestion 会传入第三参数 `options` |
|
|
508
533
|
| onStopSending | `() => Promise<void>` | - | - | 停止发送回调,点击停止按钮时触发 |
|
|
509
534
|
| onUpload | `(file: File) => Promise<{ download_url?: string }>` | - | - | 文件上传回调(每次单文件) |
|
|
510
535
|
|
|
@@ -600,6 +625,13 @@ type SendContent =
|
|
|
600
625
|
// 有文件时:数组
|
|
601
626
|
{ type: 'binary'; url?: string; mimeType: string; filename: string } | { type: 'text'; text: string }
|
|
602
627
|
>;
|
|
628
|
+
|
|
629
|
+
// onSendMessage 完整签名(第三参数由 ChatContainer 在 UserQuestion 场景注入)
|
|
630
|
+
type OnSendMessage = (
|
|
631
|
+
content: SendContent,
|
|
632
|
+
docSchema: TagSchema,
|
|
633
|
+
options?: { interrupt?: Interrupt; payload?: InterruptResume },
|
|
634
|
+
) => Promise<void>;
|
|
603
635
|
```
|
|
604
636
|
|
|
605
637
|
## 完整集成示例
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
- **自定义作答形态**:通过 `#question` slot 可替换默认选择题,渲染任意表单;作答有效时调用 `setAnswer` 回传 `UserQuestionAnswerItem`,无效时传 `undefined`。
|
|
30
30
|
- **完成校验**:所有题目均已作答(`setAnswer` 收到有效答案)后才允许点击「完成」。
|
|
31
31
|
- **跳过**:点击「跳过」返回 `status: 'cancelled'` 与空 `answers`。
|
|
32
|
-
-
|
|
32
|
+
- **输入框发送**:存在待回答 UserQuestion 时,用户也可在 `ChatInput` 直接发送;`ChatContainer` 会调用 `onSendMessage` 并在第三参数附带与「跳过」等价的 skip `payload` 及 `interrupt`,输入框内容不会自动清空。
|
|
33
33
|
|
|
34
34
|
## 数据协议
|
|
35
35
|
|
|
@@ -141,7 +141,24 @@ const payload = {
|
|
|
141
141
|
/>
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
- 卡片内「完成 / 跳过」→ `onInterruptResume(payload, interrupt)`
|
|
145
|
+
- 输入框直接发送 → `onSendMessage(content, docSchema, { interrupt, payload })`,其中 `payload` 由 `buildSkipResumePayload(interrupt)` 生成(`status: 'cancelled'`)
|
|
146
|
+
|
|
147
|
+
## 工具函数 buildSkipResumePayload
|
|
148
|
+
|
|
149
|
+
从 `@blueking/chat-x` 导出,用于构造 UserQuestion 的 skip resume(与卡片「跳过」及输入框发送时容器注入的 `options.payload` 一致):
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { buildSkipResumePayload, InterruptReason } from '@blueking/chat-x';
|
|
153
|
+
|
|
154
|
+
const payload = buildSkipResumePayload(interrupt);
|
|
155
|
+
// {
|
|
156
|
+
// interruptId: interrupt.id,
|
|
157
|
+
// reason: InterruptReason.UserQuestion,
|
|
158
|
+
// status: 'cancelled',
|
|
159
|
+
// payload: { answers: [] },
|
|
160
|
+
// }
|
|
161
|
+
```
|
|
145
162
|
|
|
146
163
|
## 自定义题目渲染(#question slot)
|
|
147
164
|
|