mirror of
https://github.com/open-webui/open-webui.git
synced 2026-11-10 08:47:14 +00:00
feat: add a File Processing model capability to skip extraction and pass raw files through
This commit is contained in:
parent
31d08d592c
commit
7e5e62fd34
8 changed files with 81 additions and 8 deletions
|
|
@ -1483,7 +1483,7 @@ async def get_sources_from_items(
|
|||
or ('file', item.get('id')) in folder_items
|
||||
):
|
||||
query_result = {
|
||||
'documents': [[file_object.data.get('content', '')]],
|
||||
'documents': [[(file_object.data or {}).get('content', '')]],
|
||||
'metadatas': [
|
||||
[
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2542,7 +2542,16 @@ async def query_chat_files(
|
|||
if not accessible:
|
||||
return JSONCodec.dumps({'error': 'No accessible files found'})
|
||||
|
||||
file_items = [{**item} for item, _ in accessible]
|
||||
file_items = [{**item} for item, _ in accessible if item.get('processed') is not False]
|
||||
if not file_items:
|
||||
return json.dumps(
|
||||
{
|
||||
'error': (
|
||||
'The attached files were uploaded without content extraction '
|
||||
'(raw attachments); their content cannot be queried as text.'
|
||||
)
|
||||
}
|
||||
)
|
||||
rag_config = await Config.get_many(
|
||||
'rag.top_k',
|
||||
'rag.top_k_reranker',
|
||||
|
|
@ -2791,6 +2800,19 @@ async def view_file(
|
|||
if file.data:
|
||||
content = file.data.get('content', '')
|
||||
|
||||
if not content and 'content' not in (file.data or {}):
|
||||
return json.dumps(
|
||||
{
|
||||
'id': file.id,
|
||||
'filename': file.filename,
|
||||
'processed': False,
|
||||
'note': (
|
||||
'This file was uploaded without content extraction (raw attachment); '
|
||||
'no text content is available.'
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
total_chars = len(content)
|
||||
|
||||
# Line-based addressing (overrides char-based offset/max_chars)
|
||||
|
|
@ -2931,6 +2953,19 @@ async def view_knowledge_file(
|
|||
if file.data:
|
||||
content = file.data.get('content', '')
|
||||
|
||||
if not content and 'content' not in (file.data or {}):
|
||||
return json.dumps(
|
||||
{
|
||||
'id': file.id,
|
||||
'filename': file.filename,
|
||||
'processed': False,
|
||||
'note': (
|
||||
'This file was uploaded without content extraction (raw attachment); '
|
||||
'no text content is available.'
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
total_chars = len(content)
|
||||
|
||||
# Line-based addressing (overrides char-based offset/max_chars)
|
||||
|
|
|
|||
|
|
@ -342,6 +342,8 @@ def get_citation_source_from_tool_result(
|
|||
|
||||
elif tool_name in ('view_knowledge_file', 'view_file'):
|
||||
file_data = tool_result
|
||||
if 'content' not in file_data:
|
||||
return []
|
||||
filename = file_data.get('filename', 'Unknown File')
|
||||
file_id = file_data.get('id', '')
|
||||
knowledge_name = file_data.get('knowledge_name', '')
|
||||
|
|
@ -1856,7 +1858,12 @@ async def chat_completion_files_handler(
|
|||
__event_emitter__ = extra_params['__event_emitter__']
|
||||
sources = []
|
||||
|
||||
if files := body.get('metadata', {}).get('files', None):
|
||||
files = [
|
||||
item
|
||||
for item in (body.get('metadata', {}).get('files', None) or [])
|
||||
if item.get('processed') is not False
|
||||
]
|
||||
if files:
|
||||
# Check if all files are in full context mode
|
||||
all_full_context = all(item.get('context') == 'full' for item in files)
|
||||
|
||||
|
|
|
|||
|
|
@ -719,6 +719,7 @@
|
|||
type ModelCapability =
|
||||
| 'vision'
|
||||
| 'file_upload'
|
||||
| 'file_processing'
|
||||
| 'web_search'
|
||||
| 'image_generation'
|
||||
| 'code_interpreter'
|
||||
|
|
@ -746,6 +747,13 @@
|
|||
modelCapabilitiesById
|
||||
);
|
||||
|
||||
let fileProcessingCapableModels = [];
|
||||
$: fileProcessingCapableModels = getCapableModelIds(
|
||||
selectedModelIds,
|
||||
'file_processing',
|
||||
modelCapabilitiesById
|
||||
);
|
||||
|
||||
let webSearchCapableModels = [];
|
||||
$: webSearchCapableModels = getCapableModelIds(
|
||||
selectedModelIds,
|
||||
|
|
@ -871,6 +879,12 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
const skipProcessing =
|
||||
process && selectedModelIds.length > 0 && fileProcessingCapableModels.length === 0;
|
||||
if (skipProcessing) {
|
||||
process = false;
|
||||
}
|
||||
|
||||
const tempItemId = uuidv4();
|
||||
const fileItem = {
|
||||
type: 'file',
|
||||
|
|
@ -886,6 +900,7 @@
|
|||
// Stamp the user's default upload mode so the sent payload carries it;
|
||||
// the per-file toggle in FileItemModal can still override it afterwards.
|
||||
...($settings?.defaultUploadContext === 'full' ? { context: 'full' } : {}),
|
||||
...(skipProcessing ? { processed: false } : {}),
|
||||
...itemData
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -198,7 +198,8 @@
|
|||
};
|
||||
|
||||
const loadContent = async () => {
|
||||
selectedTab = '';
|
||||
const hasPreview = isAudio || isPDF || isExcel || isCode || isMarkdown || isDocx || isPptx;
|
||||
selectedTab = item?.processed === false && hasPreview ? 'preview' : '';
|
||||
expandedContent = false;
|
||||
docxData = null;
|
||||
if (item?.type === 'collection') {
|
||||
|
|
@ -344,7 +345,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
{#if edit}
|
||||
{#if edit && item?.processed !== false}
|
||||
<div class=" self-end">
|
||||
<Tooltip
|
||||
content={enableFullContent
|
||||
|
|
@ -438,7 +439,13 @@
|
|||
</PanzoomContainer>
|
||||
</div>
|
||||
{:else if selectedTab === ''}
|
||||
{#if item?.file?.data}
|
||||
{#if item?.processed === false}
|
||||
<div class="py-2 text-sm text-gray-500">
|
||||
{$i18n.t(
|
||||
'This file was uploaded without content extraction (raw attachment); no text content is available.'
|
||||
)}
|
||||
</div>
|
||||
{:else if item?.file?.data}
|
||||
{@const rawContent = (item?.file?.data?.content ?? '').trim() || 'No content'}
|
||||
{@const isTruncated =
|
||||
($settings?.renderMarkdownInPreviews ?? true) &&
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@
|
|||
label: $i18n.t('File Upload'),
|
||||
description: $i18n.t('Model accepts file inputs')
|
||||
},
|
||||
file_processing: {
|
||||
label: $i18n.t('File Processing'),
|
||||
description: $i18n.t(
|
||||
'Extract file content on upload. When disabled, uploads skip extraction entirely and files are passed along as raw attachments for the model or pipe to handle'
|
||||
)
|
||||
},
|
||||
file_context: {
|
||||
label: $i18n.t('File Context'),
|
||||
description: $i18n.t('Inject file content into conversation context')
|
||||
|
|
@ -74,9 +80,8 @@
|
|||
capabilities = capabilities;
|
||||
};
|
||||
|
||||
// Hide file_context when file_upload is disabled
|
||||
$: visibleCapabilities = (Object.keys(capabilityLabels) as Capability[]).filter((cap) => {
|
||||
if (cap === 'file_context' && !capabilities.file_upload) {
|
||||
if ((cap === 'file_processing' || cap === 'file_context') && !capabilities.file_upload) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ export const SUPPORTED_FILE_EXTENSIONS = [
|
|||
|
||||
export const DEFAULT_CAPABILITIES = {
|
||||
file_context: true,
|
||||
file_processing: true,
|
||||
vision: true,
|
||||
file_upload: true,
|
||||
web_search: true,
|
||||
|
|
|
|||
|
|
@ -1152,6 +1152,7 @@
|
|||
"External Web Loader URL": "",
|
||||
"External Web Search API Key": "",
|
||||
"External Web Search URL": "",
|
||||
"Extract file content on upload. When disabled, uploads skip extraction entirely and files are passed along as raw attachments for the model or pipe to handle": "",
|
||||
"Extract action items from this note.": "",
|
||||
"Extract images from PDFs so OCR can process image-only pages.": "",
|
||||
"Fade Effect for Streaming Text": "",
|
||||
|
|
@ -1224,6 +1225,7 @@
|
|||
"File content": "",
|
||||
"File content updated successfully.": "",
|
||||
"File Context": "",
|
||||
"File Processing": "",
|
||||
"File deleted successfully.": "",
|
||||
"File Extensions": "",
|
||||
"File Mode": "",
|
||||
|
|
@ -2646,6 +2648,7 @@
|
|||
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
|
||||
"This chat won't appear in history and your messages will not be saved.": "",
|
||||
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "",
|
||||
"This file was uploaded without content extraction (raw attachment); no text content is available.": "",
|
||||
"This folder is empty": "",
|
||||
"This is a default user permission and will remain enabled.": "",
|
||||
"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue