fix: files

This commit is contained in:
Timothy J. Baek 2024-06-18 14:33:44 -07:00
parent 83986620ee
commit b4bdea6d85
5 changed files with 116 additions and 3 deletions

View File

@ -42,7 +42,7 @@ class FileModel(BaseModel):
#################### ####################
class FileResponse(BaseModel): class FileModelResponse(BaseModel):
id: str id: str
user_id: str user_id: str
filename: str filename: str

View File

@ -12,12 +12,20 @@ from fastapi import (
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import List, Union, Optional from typing import List, Union, Optional
from pathlib import Path
from fastapi import APIRouter from fastapi import APIRouter
from fastapi.responses import StreamingResponse, JSONResponse, FileResponse
from pydantic import BaseModel from pydantic import BaseModel
import json import json
from apps.webui.models.files import Files, FileForm, FileModel, FileResponse from apps.webui.models.files import (
Files,
FileForm,
FileModel,
FileModelResponse,
)
from utils.utils import get_verified_user, get_admin_user from utils.utils import get_verified_user, get_admin_user
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
@ -121,6 +129,35 @@ async def get_file_by_id(id: str, user=Depends(get_verified_user)):
) )
############################
# Get File Content By Id
############################
@router.get("/{id}/content", response_model=Optional[FileModel])
async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
file = Files.get_file_by_id(id)
if file:
file_path = Path(file.meta["path"])
# Check if the file already exists in the cache
if file_path.is_file():
print(f"file_path: {file_path}")
return FileResponse(file_path)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
############################ ############################
# Delete File By Id # Delete File By Id
############################ ############################

View File

@ -92,6 +92,34 @@ export const getFileById = async (token: string, id: string) => {
return res; return res;
}; };
export const getFileContentById = async (token: string, id: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/files/${id}/content`, {
method: 'GET',
headers: {
Accept: 'application/json',
authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return await res.blob();
})
.catch((err) => {
error = err.detail;
console.log(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const deleteFileById = async (token: string, id: string) => { export const deleteFileById = async (token: string, id: string) => {
let error = null; let error = null;

View File

@ -22,7 +22,12 @@
} from '$lib/apis/rag'; } from '$lib/apis/rag';
import { uploadFile } from '$lib/apis/files'; import { uploadFile } from '$lib/apis/files';
import { SUPPORTED_FILE_TYPE, SUPPORTED_FILE_EXTENSIONS, WEBUI_BASE_URL } from '$lib/constants'; import {
SUPPORTED_FILE_TYPE,
SUPPORTED_FILE_EXTENSIONS,
WEBUI_BASE_URL,
WEBUI_API_BASE_URL
} from '$lib/constants';
import Prompts from './MessageInput/PromptCommands.svelte'; import Prompts from './MessageInput/PromptCommands.svelte';
import Suggestions from './MessageInput/Suggestions.svelte'; import Suggestions from './MessageInput/Suggestions.svelte';
@ -116,6 +121,7 @@
type: 'file', type: 'file',
file: uploadedFile, file: uploadedFile,
id: uploadedFile.id, id: uploadedFile.id,
url: `${WEBUI_API_BASE_URL}/files/${uploadedFile.id}`,
name: file.name, name: file.name,
collection_name: '', collection_name: '',
status: 'uploaded', status: 'uploaded',

View File

@ -8,6 +8,7 @@
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import { user as _user } from '$lib/stores'; import { user as _user } from '$lib/stores';
import { getFileContentById } from '$lib/apis/files';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@ -97,6 +98,47 @@
<div class={$settings?.chatBubble ?? true ? 'self-end' : ''}> <div class={$settings?.chatBubble ?? true ? 'self-end' : ''}>
{#if file.type === 'image'} {#if file.type === 'image'}
<img src={file.url} alt="input" class=" max-h-96 rounded-lg" draggable="false" /> <img src={file.url} alt="input" class=" max-h-96 rounded-lg" draggable="false" />
{:else if file.type === 'file'}
<button
class="h-16 w-72 flex items-center space-x-3 px-2.5 dark:bg-gray-850 rounded-xl border border-gray-200 dark:border-none text-left"
type="button"
on:click={async () => {
if (file?.url) {
getFileContentById(localStorage.token, file.id).then((blob) => {
if (blob) {
const url = URL.createObjectURL(blob);
window.open(url, '_blank').focus();
}
});
}
}}
>
<div class="p-2.5 bg-red-400 text-white rounded-lg">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-6 h-6"
>
<path
fill-rule="evenodd"
d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z"
clip-rule="evenodd"
/>
<path
d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z"
/>
</svg>
</div>
<div class="flex flex-col justify-center -space-y-0.5">
<div class=" dark:text-gray-100 text-sm font-medium line-clamp-1">
{file.name}
</div>
<div class=" text-gray-500 text-sm">{$i18n.t('File')}</div>
</div>
</button>
{:else if file.type === 'doc'} {:else if file.type === 'doc'}
<button <button
class="h-16 w-72 flex items-center space-x-3 px-2.5 dark:bg-gray-850 rounded-xl border border-gray-200 dark:border-none text-left" class="h-16 w-72 flex items-center space-x-3 px-2.5 dark:bg-gray-850 rounded-xl border border-gray-200 dark:border-none text-left"