feat: 支持智能体文档附件与轻量读取
- 建立文档上传、异步读取、对象存储、补偿与聊天绑定闭环 - 按智能体 20K 上下文预算选择文档片段并保留稳定引用 - 统一聊天文件卡片、类型图标、草稿恢复与可靠下载
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
ChatDocumentAttachment,
|
||||
ChatImageAttachment,
|
||||
ChatTimelineMessageItem,
|
||||
ChatTimelineToolApprovalPayload,
|
||||
} from '@easyflow/common-ui';
|
||||
@@ -19,7 +21,10 @@ import { copyTextToClipboard } from '@easyflow/utils';
|
||||
import { ElButton, ElMessage } from 'element-plus';
|
||||
|
||||
import AiChatPanel from '#/components/ai-chat/AiChatPanel.vue';
|
||||
import { loadAgentChatImage } from '#/components/ai-chat/mediaApi';
|
||||
import {
|
||||
loadAgentChatDocument,
|
||||
loadAgentChatImage,
|
||||
} from '#/components/ai-chat/mediaApi';
|
||||
import { useAgentComposerDraft } from '#/components/ai-chat/useAgentComposerDraft';
|
||||
|
||||
import { approveAgentRun, rejectAgentRun } from '../api';
|
||||
@@ -104,6 +109,10 @@ async function handleSend(prompt: string) {
|
||||
ElMessage.warning('图片上传完成后再发送');
|
||||
return;
|
||||
}
|
||||
if (composer.documents.processing.value) {
|
||||
ElMessage.warning('文档读取完成后再发送');
|
||||
return;
|
||||
}
|
||||
const failedImage = composer.images.items.value.find(
|
||||
(item) => item.status === 'error',
|
||||
);
|
||||
@@ -111,9 +120,20 @@ async function handleSend(prompt: string) {
|
||||
ElMessage.error(failedImage.error || '请处理上传失败的图片');
|
||||
return;
|
||||
}
|
||||
const failedDocument = composer.documents.items.value.find(
|
||||
(item) => item.status === 'error',
|
||||
);
|
||||
if (failedDocument) {
|
||||
ElMessage.error(failedDocument.error || '请处理读取失败的文档');
|
||||
return;
|
||||
}
|
||||
await composer.flush();
|
||||
await sendDraft({
|
||||
...getDraftContext(),
|
||||
documentUploadIds: composer.documents.uploadIds.value,
|
||||
documents: composer.documents.readyItems.value.map((item) => ({
|
||||
...item,
|
||||
})),
|
||||
prompt,
|
||||
imageUploadIds: composer.images.uploadIds.value,
|
||||
images: composer.images.readyItems.value.map((item) => ({ ...item })),
|
||||
@@ -188,7 +208,7 @@ async function handleClearSession() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAddFiles(files: File[]) {
|
||||
async function handleAddImageFiles(files: File[]) {
|
||||
if (!props.agent.id) {
|
||||
ElMessage.warning('请先保存智能体');
|
||||
return;
|
||||
@@ -205,7 +225,24 @@ async function handleAddFiles(files: File[]) {
|
||||
composer.scheduleSave();
|
||||
}
|
||||
|
||||
async function handleRetryImage(item: any) {
|
||||
async function handleAddDocumentFiles(files: File[]) {
|
||||
if (!props.agent.id) {
|
||||
ElMessage.warning('请先保存智能体');
|
||||
return;
|
||||
}
|
||||
await composer.ensureSession();
|
||||
const rejected = await composer.documents.addFiles(files, {
|
||||
agentId: String(props.agent.id),
|
||||
mode: 'DRAFT',
|
||||
sessionId: composer.sessionId.value,
|
||||
});
|
||||
if (rejected > 0) {
|
||||
ElMessage.warning('每轮最多添加 3 份文档');
|
||||
}
|
||||
composer.scheduleSave();
|
||||
}
|
||||
|
||||
async function handleRetryImage(item: ChatImageAttachment) {
|
||||
await composer.images.retry(item, {
|
||||
agentId: String(props.agent.id),
|
||||
mode: 'DRAFT',
|
||||
@@ -214,7 +251,7 @@ async function handleRetryImage(item: any) {
|
||||
composer.scheduleSave();
|
||||
}
|
||||
|
||||
async function handleRemoveImage(item: any) {
|
||||
async function handleRemoveImage(item: ChatImageAttachment) {
|
||||
try {
|
||||
await composer.images.remove(item);
|
||||
composer.scheduleSave();
|
||||
@@ -223,6 +260,24 @@ async function handleRemoveImage(item: any) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRetryDocument(item: ChatDocumentAttachment) {
|
||||
await composer.documents.retry(item, {
|
||||
agentId: String(props.agent.id),
|
||||
mode: 'DRAFT',
|
||||
sessionId: composer.sessionId.value,
|
||||
});
|
||||
composer.scheduleSave();
|
||||
}
|
||||
|
||||
async function handleRemoveDocument(item: ChatDocumentAttachment) {
|
||||
try {
|
||||
await composer.documents.remove(item);
|
||||
composer.scheduleSave();
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '文档删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
function handleStop() {
|
||||
if (!loading.value) {
|
||||
return;
|
||||
@@ -275,6 +330,8 @@ async function handleReject(payload: ChatTimelineToolApprovalPayload) {
|
||||
closable
|
||||
:messages="[]"
|
||||
:loading="loading"
|
||||
:documents="composer.documents.items.value"
|
||||
:document-loader="loadAgentChatDocument"
|
||||
:images="composer.images.items.value"
|
||||
:image-enabled="imageEnabled"
|
||||
:image-loader="loadAgentChatImage"
|
||||
@@ -282,8 +339,11 @@ async function handleReject(payload: ChatTimelineToolApprovalPayload) {
|
||||
:approval-loading="approvalLoading"
|
||||
@send="handleSend"
|
||||
@update:model-value="handleDraftTextInput"
|
||||
@add-files="handleAddFiles"
|
||||
@add-files="handleAddImageFiles"
|
||||
@add-document-files="handleAddDocumentFiles"
|
||||
@remove-document="handleRemoveDocument"
|
||||
@remove-image="handleRemoveImage"
|
||||
@retry-document="handleRetryDocument"
|
||||
@retry-image="handleRetryImage"
|
||||
@stop="handleStop"
|
||||
@approve="handleApprove"
|
||||
@@ -314,6 +374,7 @@ async function handleReject(payload: ChatTimelineToolApprovalPayload) {
|
||||
<ChatTimeline
|
||||
v-else
|
||||
:items="timelineItems"
|
||||
:document-loader="loadAgentChatDocument"
|
||||
:image-loader="loadAgentChatImage"
|
||||
empty-text="输入问题试运行当前智能体"
|
||||
:approval-loading="approvalLoading"
|
||||
|
||||
Reference in New Issue
Block a user