Skip to content

feat: add support for uploading other file types and extend file upload settings #2940

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def save_context(self, details, workflow_manage):
self.context['document'] = details.get('document_list')
self.context['image'] = details.get('image_list')
self.context['audio'] = details.get('audio_list')
self.context['other'] = details.get('other_list')
self.status = details.get('status')
self.err_message = details.get('err_message')
for key, value in workflow_variable.items():
Expand All @@ -59,7 +60,8 @@ def execute(self, question, **kwargs) -> NodeResult:
'question': question,
'image': self.workflow_manage.image_list,
'document': self.workflow_manage.document_list,
'audio': self.workflow_manage.audio_list
'audio': self.workflow_manage.audio_list,
'other': self.workflow_manage.other_list,
}
return NodeResult(node_variable, workflow_variable)

Expand All @@ -83,5 +85,6 @@ def get_details(self, index: int, **kwargs):
'image_list': self.context.get('image'),
'document_list': self.context.get('document'),
'audio_list': self.context.get('audio'),
'other_list': self.context.get('other'),
'global_fields': global_fields
}
4 changes: 4 additions & 0 deletions apps/application/flow/workflow_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ def __init__(self, flow: Flow, params, work_flow_post_handler: WorkFlowPostHandl
base_to_response: BaseToResponse = SystemToResponse(), form_data=None, image_list=None,
document_list=None,
audio_list=None,
other_list=None,
start_node_id=None,
start_node_data=None, chat_record=None, child_node=None):
if form_data is None:
Expand All @@ -248,12 +249,15 @@ def __init__(self, flow: Flow, params, work_flow_post_handler: WorkFlowPostHandl
document_list = []
if audio_list is None:
audio_list = []
if other_list is None:
other_list = []
self.start_node_id = start_node_id
self.start_node = None
self.form_data = form_data
self.image_list = image_list
self.document_list = document_list
self.audio_list = audio_list
self.other_list = other_list
self.params = params
self.flow = flow
self.context = {}
Expand Down
5 changes: 4 additions & 1 deletion apps/application/serializers/chat_message_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ def chat(self, instance: Dict, with_valid=True):
'image_list': instance.get('image_list', []),
'document_list': instance.get('document_list', []),
'audio_list': instance.get('audio_list', []),
'other_list': instance.get('other_list', []),
}
).chat(base_to_response=OpenaiToResponse())

Expand Down Expand Up @@ -274,6 +275,7 @@ class ChatMessageSerializer(serializers.Serializer):
image_list = serializers.ListField(required=False, error_messages=ErrMessage.list(_("picture")))
document_list = serializers.ListField(required=False, error_messages=ErrMessage.list(_("document")))
audio_list = serializers.ListField(required=False, error_messages=ErrMessage.list(_("Audio")))
other_list = serializers.ListField(required=False, error_messages=ErrMessage.list(_("Other")))
child_node = serializers.DictField(required=False, allow_null=True,
error_messages=ErrMessage.dict(_("Child Nodes")))

Expand Down Expand Up @@ -372,6 +374,7 @@ def chat_work_flow(self, chat_info: ChatInfo, base_to_response):
image_list = self.data.get('image_list')
document_list = self.data.get('document_list')
audio_list = self.data.get('audio_list')
other_list = self.data.get('other_list')
user_id = chat_info.application.user_id
chat_record_id = self.data.get('chat_record_id')
chat_record = None
Expand All @@ -388,7 +391,7 @@ def chat_work_flow(self, chat_info: ChatInfo, base_to_response):
'client_id': client_id,
'client_type': client_type,
'user_id': user_id}, WorkFlowPostHandler(chat_info, client_id, client_type),
base_to_response, form_data, image_list, document_list, audio_list,
base_to_response, form_data, image_list, document_list, audio_list, other_list,
self.data.get('runtime_node_id'),
self.data.get('node_data'), chat_record, self.data.get('child_node'))
r = work_flow_manage.run()
Expand Down
2 changes: 2 additions & 0 deletions apps/application/views/chat_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ def post(self, request: Request, chat_id: str):
'document_list') if 'document_list' in request.data else [],
'audio_list': request.data.get(
'audio_list') if 'audio_list' in request.data else [],
'other_list': request.data.get(
'other_list') if 'other_list' in request.data else [],
'client_type': request.auth.client_type,
'node_id': request.data.get('node_id', None),
'runtime_node_id': request.data.get('runtime_node_id', None),
Expand Down
71 changes: 66 additions & 5 deletions ui/src/components/ai-chat/component/chat-input-operate/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
uploadDocumentList.length ||
uploadImageList.length ||
uploadAudioList.length ||
uploadVideoList.length
uploadVideoList.length ||
uploadOtherList.length
"
>
<el-row :gutter="10">
Expand Down Expand Up @@ -50,6 +51,42 @@
</div>
</el-card>
</el-col>
<el-col
v-for="(item, index) in uploadOtherList"
:key="index"
:xs="24"
:sm="props.type === 'debug-ai-chat' ? 24 : 12"
:md="props.type === 'debug-ai-chat' ? 24 : 12"
:lg="props.type === 'debug-ai-chat' ? 24 : 12"
:xl="props.type === 'debug-ai-chat' ? 24 : 12"
class="mb-8"
>
<el-card
shadow="never"
style="--el-card-padding: 8px; max-width: 100%"
class="file cursor"
>
<div
class="flex align-center"
@mouseenter.stop="mouseenter(item)"
@mouseleave.stop="mouseleave()"
>
<div
@click="deleteFile(index, 'document')"
class="delete-icon color-secondary"
v-if="showDelete === item.url"
>
<el-icon>
<CircleCloseFilled />
</el-icon>
</div>
<img :src="getImgUrl(item && item?.name)" alt="" width="24" />
<div class="ml-4 ellipsis-1" :title="item && item?.name">
{{ item && item?.name }}
</div>
</div>
</el-card>
</el-col>

<el-col
:xs="24"
Expand Down Expand Up @@ -310,9 +347,10 @@ const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp']
const documentExtensions = ['pdf', 'docx', 'txt', 'xls', 'xlsx', 'md', 'html', 'csv']
const videoExtensions = ['mp4', 'avi', 'mov', 'mkv', 'flv']
const audioExtensions = ['mp3', 'wav', 'ogg', 'aac', 'm4a']
let otherExtensions = ['ppt', 'doc']

const getAcceptList = () => {
const { image, document, audio, video } = props.applicationDetails.file_upload_setting
const { image, document, audio, video, other } = props.applicationDetails.file_upload_setting
let accepts: any = []
if (image) {
accepts = [...imageExtensions]
Expand All @@ -326,6 +364,11 @@ const getAcceptList = () => {
if (video) {
accepts = [...accepts, ...videoExtensions]
}
if (other) {
// 其他文件类型
otherExtensions = props.applicationDetails.file_upload_setting.otherExtensions
accepts = [...accepts, ...otherExtensions]
}

if (accepts.length === 0) {
return `.${t('chat.uploadFile.tipMessage')}`
Expand All @@ -339,7 +382,8 @@ const checkMaxFilesLimit = () => {
uploadImageList.value.length +
uploadDocumentList.value.length +
uploadAudioList.value.length +
uploadVideoList.value.length
uploadVideoList.value.length +
uploadOtherList.value.length
)
}

Expand All @@ -350,7 +394,8 @@ const uploadFile = async (file: any, fileList: any) => {
uploadImageList.value.length +
uploadDocumentList.value.length +
uploadAudioList.value.length +
uploadVideoList.value.length
uploadVideoList.value.length +
uploadOtherList.value.length
if (file_limit_once >= maxFiles) {
MsgWarning(t('chat.uploadFile.limitMessage1') + maxFiles + t('chat.uploadFile.limitMessage2'))
fileList.splice(0, fileList.length)
Expand All @@ -376,6 +421,8 @@ const uploadFile = async (file: any, fileList: any) => {
uploadVideoList.value.push(file)
} else if (audioExtensions.includes(extension)) {
uploadAudioList.value.push(file)
} else if (otherExtensions.includes(extension)) {
uploadOtherList.value.push(file)
}

if (!chatId_context.value) {
Expand Down Expand Up @@ -434,6 +481,15 @@ const uploadFile = async (file: any, fileList: any) => {
file.file_id = f[0].file_id
}
})
uploadOtherList.value.forEach((file: any) => {
const f = response.data.filter(
(f: any) => f.name.replaceAll(' ', '') === file.name.replaceAll(' ', '')
)
if (f.length > 0) {
file.url = f[0].url
file.file_id = f[0].file_id
}
})
if (!inputValue.value && uploadImageList.value.length > 0) {
inputValue.value = t('chat.uploadFile.imageMessage')
}
Expand Down Expand Up @@ -499,6 +555,7 @@ const uploadImageList = ref<Array<any>>([])
const uploadDocumentList = ref<Array<any>>([])
const uploadVideoList = ref<Array<any>>([])
const uploadAudioList = ref<Array<any>>([])
const uploadOtherList = ref<Array<any>>([])

const showDelete = ref('')

Expand Down Expand Up @@ -709,13 +766,15 @@ function autoSendMessage() {
image_list: uploadImageList.value,
document_list: uploadDocumentList.value,
audio_list: uploadAudioList.value,
video_list: uploadVideoList.value
video_list: uploadVideoList.value,
other_list: uploadOtherList.value,
})
inputValue.value = ''
uploadImageList.value = []
uploadDocumentList.value = []
uploadAudioList.value = []
uploadVideoList.value = []
uploadOtherList.value = []
if (quickInputRef.value) {
quickInputRef.value.textareaStyle.height = '45px'
}
Expand Down Expand Up @@ -771,6 +830,8 @@ function deleteFile(index: number, val: string) {
uploadVideoList.value.splice(index, 1)
} else if (val === 'audio') {
uploadAudioList.value.splice(index, 1)
} else if (val === 'other') {
uploadOtherList.value.splice(index, 1)
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is quite comprehensive and well-structured, but there are a few points that can be improved:

  1. Duplicated Code: The logic for handling file types (image, document, etc.) is duplicated several times. This can be optimized into a single function.

  2. Use of ref: Ensure all variables are properly initialized using reactive. Adding this will make the component reactive to changes in its state.

  3. Comments: Add comments where necessary to explain complex sections of the code.

  4. Type Safety: Consider adding type annotations to improve readability and maintainability.

Here's an updated version with these improvements:

import { reactive } from 'vue';
import { ElIcon, CircleCloseFilled } from '@element-plus/icons-vue';
// Import translation methods like t here

interface FileItem {
  name: string;
  url?: string;
}

const data = reactive({
  uploadImageList: [] as Array<FileItem>,
  uploadDocumentList: [] as Array<FileItem>,
  uploadVideoList: [] as Array<FileItem>,
  uploadOtherList: [] as Array<FileItem>,
  showDelete: '',
});

function getImgUrl(name: string): string {
  // Logic to generate image URL based on file name
  return '';
}

function checkMaxFilesLimit(): boolean {
  return (
    uploadImageList.length +
    uploadDocumentList.length +
    uploadAudioList.length +
    uploadOtherList.length
  ) >= props.applicationDetails.file_upload_setting.max_files!;
}

function handleFileType(file: any, fileType: string) {
  if (fileType === 'image') {
    uploadImageList.value.push({ name: file.name, url: getFileUrl(file) });
  } else if (fileType === 'document') {
    uploadDocumentList.value.push({ name: file.name, url: getFileUrl(file) });
  } else if (fileType === 'video') {
    uploadVideoList.value.push({ name: file.name, url: getFileUrl(file) });
  } else if (file === 'audio') {
    uploadAudioList.value.push({ name: file.name, url: getFileUrl(file) });
  } else if (uploadOtherList.some(item => item.name.replace(/\u00A0/, '') === file.name.replace(/\u00A0/, ''))) {
    let match = uploadOtherList.find(f => f.name.replaceAll('\u00A0', '') === file.name.replaceAll('\u00A0', '')) || null;
    if (match) {
      match.url = response.data.filter(f => f.name.replace(/\u00A0/, '') === file.name.replace(/\u00A0/, ''))[0]?.url;
      match.file_id = response.data.filter(f => f.name.replace(/\u00A0/, '') === file.name.replace(/\u00A0/, ''))[0]?.file_id;
    }
    uploadOtherList.value.push(match ? match : { name: file.name, url: '', file_id: '' });
  }
}

async function autoSendMessage() {
  await chatInterface.autoSendFile(data);
  inputValue.value = '';
  data.uploadImageList = [];
  data.uploadDocumentList = [];
  data.uploadAudioList = [];
  data.uploadOtherList = [];
}

function deleteFromUpload(index: number, val: string) {
  if (val === 'image') {
    data.uploadImageList.splice(index, 1);
  } else if (val === 'document') {
    data.uploadDocumentList.splice(index, 1);
  } else if (val === 'video') {
    data.uploadVideoList.splice(index, 1);
  } else if (val === 'audio') {
    data.uploadAudioList.splice(index, 1);
  } else if (val === 'other') {
    data.uploadOtherList.splice(index, 1);
  }
}

Key Changes:

  1. Reactive Initialization: Initialized data using reactive.
  2. Optimized FileType Handling: Created a separate function handleFileType to reduce duplication.
  3. Removed Duplicates: Unified similar logic within handleFileType method.
  4. Added Comments: Added comments to explain major sections of the code.
  5. Type Annotations: Ensured type safety for better clarity and maintenance.

Expand Down
6 changes: 4 additions & 2 deletions ui/src/locales/lang/en-US/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export default {
document: 'Documents',
image: 'Image',
audio: 'Audio',
video: 'Video'
video: 'Video',
other: 'Other file',
addExtensions: 'Add file extensions',
},
status: {
label: 'Status',
Expand All @@ -55,7 +57,7 @@ export default {
param: {
outputParam: 'Output Parameters',
inputParam: 'Input Parameters',
initParam: 'Startup Parameters',
initParam: 'Startup Parameters'
},

inputPlaceholder: 'Please input',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code looks mostly correct, but here are some minor suggestions for improvement:

  1. Remove Duplicated export Statement: The last line should be closed with a semicolon (;) to ensure proper JavaScript formatting.

    export {};
  2. Improve Comments and Readability: Adding comments can help clarify certain parts of the code, especially if they involve complex logic or feature additions.

  3. Ensure Consistent Formatting: Maintain consistent spacing around operators and after commas to improve readability.

  4. Handle File Extentions: If there's going to be more handling of file types, consider storing the list of allowed extensions in an array and checking against it instead of using separate constants.

Here is the revised version of the code with these improvements:

import React from 'react';
import PropTypes from 'prop-types';

const App = () => (
  <div>
    {/* Add necessary components */}
  </div>
);

App.propTypes = {
  // Define props here as needed
};

// Localized Strings
export default {
  common: {
    docs: 'Docs',
    doc: 'Document',
    image: 'Image',
    audio: 'Audio',
    video: 'Video',
    other: 'Other file',
    addExtensions: 'Add file extensions'
  },
  status: {
    label: 'Status'
    // Add other statuses as needed
  },
  param: {
    outputParam: 'Output Parameters',
    inputParam: 'Input Parameters',
    initParam: 'Startup Parameters'
  }
};

These changes make the code cleaner and better formatted while maintaining its functionality.

Expand Down
4 changes: 3 additions & 1 deletion ui/src/locales/lang/zh-CN/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export default {
document: '文档',
image: '图片',
audio: '音频',
video: '视频'
video: '视频',
other: '其他文件',
addExtensions: '添加文件扩展名',
},
status: {
label: '状态',
Expand Down
4 changes: 3 additions & 1 deletion ui/src/locales/lang/zh-Hant/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ export default {
document: '文檔',
image: '圖片',
audio: '音頻',
video: '視頻'
video: '視頻',
other: '其他文件',
addExtensions: '添加文件擴展名'
},
status: {
label: '狀態',
Expand Down
Loading