Skip to content

Commit

Permalink
Feat: support for uploading files in chat (#188)
Browse files Browse the repository at this point in the history
* ✨ Feat(chat): add support for file upload directly in chat (inline files)

* ✨ Feat(chat): toggle inline files support with template flag

* 🐛 Bug(chat): inline files fixes
  • Loading branch information
MasterKenth authored Nov 5, 2024
1 parent d5bd800 commit ce4a01b
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 48 deletions.
9 changes: 9 additions & 0 deletions fai-rag-app/fai-backend/fai_backend/assistant/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ def AssistantForm(
step=500,
),

c.Select(
name='allow_inline_files',
label=_('Allow users to attach files in chat'),
required=True,
options=[('True', 'Yes'), ('False', 'No')],
value=str(data.allow_inline_files) if data else "False",
size='sm'
),

c.Select(
name='files_collection_id',
label=_('Collection ID'),
Expand Down
1 change: 1 addition & 0 deletions fai-rag-app/fai-backend/fai_backend/assistant/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class AssistantTemplate(BaseModel):
id: str
meta: AssistantTemplateMeta
max_tokens: int = -1
allow_inline_files: bool = False
files_collection_id: Optional[str] = None
streams: list[AssistantStreamConfig | AssistantStreamPipelineDef]

Expand Down
1 change: 1 addition & 0 deletions fai-rag-app/fai-backend/fai_backend/assistant/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ class TemplatePayload(BaseModel):
sample_questions: list[str | None] | None = None
files_collection_id: str | None = None
response_format: str | None = None
allow_inline_files: bool = False
delete_label: str = "Delete" # TODO: fix hack for allowing something to show up in list to click on.
7 changes: 5 additions & 2 deletions fai-rag-app/fai-backend/fai_backend/assistant/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ def basic_stream_adapter():
0].settings else None,
instructions=template.streams[0].messages[0].content or '',
files_collection_id=template.files_collection_id,
max_tokens=template.max_tokens
max_tokens=template.max_tokens,
allow_inline_files=template.allow_inline_files
)

def rag_stream_adapter():
Expand All @@ -107,7 +108,8 @@ def rag_stream_adapter():
1].settings else None,
instructions=template.streams[1].messages[0].content or '',
files_collection_id=template.files_collection_id,
max_tokens=template.max_tokens
max_tokens=template.max_tokens,
allow_inline_files=template.allow_inline_files
)

return basic_stream_adapter() if len(template.streams) == 1 else rag_stream_adapter()
Expand Down Expand Up @@ -179,6 +181,7 @@ def rag_stream():
payload.sample_questions) > 0 else []
),
max_tokens=payload.max_tokens,
allow_inline_files=payload.allow_inline_files,
files_collection_id=payload.files_collection_id,
streams=basic_stream() if payload.files_collection_id is '' else rag_stream()
)
16 changes: 16 additions & 0 deletions fai-rag-app/fai-backend/fai_backend/documents/routes.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from tempfile import NamedTemporaryFile

from fastapi import APIRouter, Depends, Form, UploadFile

from fai_backend.collection.dependencies import get_collection_service
from fai_backend.collection.service import CollectionService
from fai_backend.config import settings
from fai_backend.dependencies import get_page_template_for_logged_in_users, get_project_user
from fai_backend.files.dependecies import get_file_upload_service
from fai_backend.files.file_parser import ParserFactory
from fai_backend.files.service import FileUploadService
from fai_backend.framework import components as c
from fai_backend.framework import events as e
Expand Down Expand Up @@ -162,3 +165,16 @@ def parse_documents(
file_service.dump_list_to_json(stringify_parsed_files, dest_directory_path, dest_file_name)

return []


@router.post('/document/parse')
def parse_document(
file: UploadFile
):
with NamedTemporaryFile(delete=False) as temp:
temp.write(file.file.read())
temp.flush()
parser = ParserFactory.get_parser(temp.name)
parsed = parser.parse(temp.name)
joined = "\n\n".join([p.text for p in parsed])
return joined
3 changes: 2 additions & 1 deletion fai-rag-app/fai-backend/fai_backend/files/file_parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from abc import abstractmethod, ABC

import magic
from unstructured.documents.elements import Element
from unstructured.partition.docx import partition_docx
from unstructured.partition.md import partition_md
from unstructured.partition.pdf import partition_pdf
Expand All @@ -10,7 +11,7 @@

class AbstractDocumentParser(ABC):
@abstractmethod
def parse(self, filename: str):
def parse(self, filename: str) -> list[Element]:
pass


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ class Assistant(BaseModel):
description: str
sampleQuestions: list[str]
maxTokens: int
allowInlineFiles: bool


class SSEChat(UIComponent):
Expand Down
1 change: 1 addition & 0 deletions fai-rag-app/fai-backend/fai_backend/new_chat/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class ClientChatState(BaseModel):
rename_label: str = "Rename" # TODO: fix hack for allowing something to show up in list to click on.
history: list[LLMClientChatMessage]
max_tokens: int
allow_inline_files: bool


class ChatHistoryEditPayload(BaseModel):
Expand Down
1 change: 1 addition & 0 deletions fai-rag-app/fai-backend/fai_backend/new_chat/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def chat_index_view(authenticated_user: ProjectUser | None = Depends(get_project
project=p.id,
description=a.meta.description,
maxTokens=a.max_tokens,
allowInlineFiles=a.allow_inline_files,
sampleQuestions=a.meta.sample_questions) for p in projects for a in p.assistants if
not a.id.startswith('_')]

Expand Down
1 change: 1 addition & 0 deletions fai-rag-app/fai-backend/fai_backend/new_chat/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ def convert_message(m: AssistantStreamMessage) -> LLMClientChatMessage:
timestamp=chat_history_model.history[0].timestamp,
title=default_title,
max_tokens=chat_history_model.assistant.max_tokens,
allow_inline_files=chat_history_model.assistant.allow_inline_files,
history=[convert_message(message) for message in chat_history_model.history])
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script lang="ts">
import SVG from '$lib/components/SVG.svelte'
export let title: string
export let onRemove: () => void
export let state: 'valid' | 'invalid' | 'pending'
const maxTitleLength = 15
$: shortTitle = title.length > maxTitleLength ? title.slice(0, maxTitleLength - 3) + '...' : title
</script>

<div class="p-1">
<div
class="flex items-center gap-2 p-2 bg-gray-800 text-white rounded relative"
class:bg-red-600={state === 'invalid'}
class:opacity-50={state === 'pending'}
>
<div
class="bg-orange-500 rounded w-8 h-8 flex items-center justify-center"
class:bg-red-500={state === 'invalid'}
class:bg-gray-500={state === 'pending'}
>
{#if state === 'invalid'}
<SVG
width="24"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWNpcmNsZS14Ij48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjxwYXRoIGQ9Im0xNSA5LTYgNiIvPjxwYXRoIGQ9Im05IDkgNiA2Ii8+PC9zdmc+"
/>
{:else if state === 'pending'}
<SVG
width="24"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWVsbGlwc2lzIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxIi8+PGNpcmNsZSBjeD0iMTkiIGN5PSIxMiIgcj0iMSIvPjxjaXJjbGUgY3g9IjUiIGN5PSIxMiIgcj0iMSIvPjwvc3ZnPg=="
/>
{:else}
<SVG
width="24"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWZpbGUtdGV4dCI+PHBhdGggZD0iTTE1IDJINmEyIDIgMCAwIDAtMiAydjE2YTIgMiAwIDAgMCAyIDJoMTJhMiAyIDAgMCAwIDItMlY3WiIvPjxwYXRoIGQ9Ik0xNCAydjRhMiAyIDAgMCAwIDIgMmg0Ii8+PHBhdGggZD0iTTEwIDlIOCIvPjxwYXRoIGQ9Ik0xNiAxM0g4Ii8+PHBhdGggZD0iTTE2IDE3SDgiLz48L3N2Zz4="
/>
{/if}
</div>
<span class="break-keep">{shortTitle}</span>
<button
on:click={onRemove}
class="absolute -right-1 -top-1 w-5 h-5 bg-black text-xs flex items-center justify-center rounded-full"
>
X
</button>
</div>
</div>
Loading

0 comments on commit ce4a01b

Please sign in to comment.