Update File upload RTVI messages and frames to use mime-type as the format
This commit is contained in:
@@ -244,14 +244,6 @@ class ImageRawFrame:
|
|||||||
format: Optional[str]
|
format: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
ImageFileFormat = Literal["png", "jpg", "jpeg", "webp", "gif", "heic", "hief"]
|
|
||||||
DocFileFormat = Literal[
|
|
||||||
"pdf", "csv", "txt", "md", "doc", "docx", "xls", "xlsx", "json", "html", "css", "javascript"
|
|
||||||
]
|
|
||||||
MediaFileFormat = Literal["mp3", "wav", "ogg", "aac", "mp4", "webm", "ogg", "avi"]
|
|
||||||
|
|
||||||
FileFormat = Literal[ImageFileFormat, DocFileFormat, MediaFileFormat]
|
|
||||||
|
|
||||||
FileSourceType = Literal["bytes", "url"] # TODO: Add support for "id"
|
FileSourceType = Literal["bytes", "url"] # TODO: Add support for "id"
|
||||||
|
|
||||||
|
|
||||||
@@ -263,13 +255,13 @@ class FileRawFrame:
|
|||||||
file: Raw file bytes.
|
file: Raw file bytes.
|
||||||
type: Type of the file ('bytes' or 'url'),
|
type: Type of the file ('bytes' or 'url'),
|
||||||
name: Optional name of the file.
|
name: Optional name of the file.
|
||||||
format: File format (e.g., 'pdf', 'docx').
|
format: File format (expected in Mime Format).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
file: bytes | str
|
file: bytes | str
|
||||||
type: FileSourceType
|
type: FileSourceType
|
||||||
name: Optional[str]
|
name: Optional[str]
|
||||||
format: Optional[FileFormat]
|
format: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from openai.types.chat import (
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from pipecat.adapters.schemas.tools_schema import AdapterType, ToolsSchema
|
from pipecat.adapters.schemas.tools_schema import AdapterType, ToolsSchema
|
||||||
from pipecat.frames.frames import AudioRawFrame, ImageFileFormat
|
from pipecat.frames.frames import AudioRawFrame
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
|
||||||
@@ -412,19 +412,18 @@ class LLMContext:
|
|||||||
role: The role of this message (defaults to "user").
|
role: The role of this message (defaults to "user").
|
||||||
"""
|
"""
|
||||||
bytes = file
|
bytes = file
|
||||||
is_image = format in get_args(ImageFileFormat)
|
is_image = format.startswith("image/")
|
||||||
if not is_image and format != "pdf":
|
if not is_image and format != "application/pdf":
|
||||||
# TODO how to send error back?
|
# TODO how to send error back?
|
||||||
logger.warning(f"Unsupported file format for LLM context: {format}")
|
logger.warning(f"Unsupported file format for LLM context: {format}")
|
||||||
return
|
return
|
||||||
mime_type = f"image/{format}" if is_image else "application/pdf"
|
|
||||||
if type == "url":
|
if type == "url":
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(file) as response:
|
async with session.get(file) as response:
|
||||||
content = io.BytesIO(await response.content.read())
|
content = io.BytesIO(await response.content.read())
|
||||||
base64_string = base64.b64encode(content.getvalue()).decode("utf-8")
|
base64_string = base64.b64encode(content.getvalue()).decode("utf-8")
|
||||||
bytes = f"data:{mime_type};base64,{base64_string}"
|
bytes = f"data:{format};base64,{base64_string}"
|
||||||
if format in get_args(ImageFileFormat):
|
if is_image:
|
||||||
message = LLMContext.create_image_url_message(role=role, url=bytes, text=text)
|
message = LLMContext.create_image_url_message(role=role, url=bytes, text=text)
|
||||||
self.add_message(message)
|
self.add_message(message)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
from pipecat.frames.frames import (
|
from pipecat.frames.frames import (
|
||||||
AggregationType,
|
AggregationType,
|
||||||
FileFormat,
|
|
||||||
FileSourceType,
|
FileSourceType,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -256,7 +255,7 @@ class FileUrl(FileSource):
|
|||||||
class File(BaseModel):
|
class File(BaseModel):
|
||||||
"""File data structure for RTVI file sending."""
|
"""File data structure for RTVI file sending."""
|
||||||
|
|
||||||
format: FileFormat
|
format: str # Mime format of the file, e.g., 'application/pdf'
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
source: FileBytes | FileUrl
|
source: FileBytes | FileUrl
|
||||||
customOpts: Optional[dict] = None # ex. 'detail' in openAI or 'citations' in Bedrock
|
customOpts: Optional[dict] = None # ex. 'detail' in openAI or 'citations' in Bedrock
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
from typing import Any, Dict, Mapping, Optional, get_args
|
from typing import Any, Dict, Mapping, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel, ValidationError
|
from pydantic import BaseModel, ValidationError
|
||||||
@@ -20,10 +20,8 @@ from pipecat.frames.frames import (
|
|||||||
EndFrame,
|
EndFrame,
|
||||||
EndTaskFrame,
|
EndTaskFrame,
|
||||||
ErrorFrame,
|
ErrorFrame,
|
||||||
FileFormat,
|
|
||||||
Frame,
|
Frame,
|
||||||
FunctionCallResultFrame,
|
FunctionCallResultFrame,
|
||||||
ImageFileFormat,
|
|
||||||
InputAudioRawFrame,
|
InputAudioRawFrame,
|
||||||
InputTransportMessageFrame,
|
InputTransportMessageFrame,
|
||||||
LLMConfigureOutputFrame,
|
LLMConfigureOutputFrame,
|
||||||
@@ -566,9 +564,6 @@ class RTVIProcessor(FrameProcessor):
|
|||||||
async def _handle_send_file(self, data: RTVI.SendFileData):
|
async def _handle_send_file(self, data: RTVI.SendFileData):
|
||||||
"""Handle a send-file message from the client."""
|
"""Handle a send-file message from the client."""
|
||||||
file = data.file
|
file = data.file
|
||||||
if file.format not in get_args(FileFormat):
|
|
||||||
logger.warning(f"Unsupported file format: {file.format}")
|
|
||||||
return
|
|
||||||
|
|
||||||
source = None
|
source = None
|
||||||
if file.source.type == "bytes":
|
if file.source.type == "bytes":
|
||||||
@@ -579,7 +574,7 @@ class RTVIProcessor(FrameProcessor):
|
|||||||
logger.warning(f"Unsupported file source type: {file.source.type}")
|
logger.warning(f"Unsupported file source type: {file.source.type}")
|
||||||
return
|
return
|
||||||
|
|
||||||
if file.source.type == "bytes" and file.format in get_args(ImageFileFormat):
|
if file.source.type == "bytes" and file.format.startswith("image/"):
|
||||||
size = [file.source.width or 0, file.source.height or 0]
|
size = [file.source.width or 0, file.source.height or 0]
|
||||||
file_frame = UserImageRawFrame(
|
file_frame = UserImageRawFrame(
|
||||||
text=data.content,
|
text=data.content,
|
||||||
|
|||||||
Reference in New Issue
Block a user