Add new FileSourceType for 'id' and use that for local uploads, prefixed with 'pipecat:'
This commit is contained in:
@@ -250,6 +250,14 @@ class FileUrl(FileSource):
|
|||||||
|
|
||||||
type: FileSourceType = "url"
|
type: FileSourceType = "url"
|
||||||
url: str
|
url: str
|
||||||
|
public: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
class FileId(FileSource):
|
||||||
|
"""File source as a file ID."""
|
||||||
|
|
||||||
|
type: FileSourceType = "id"
|
||||||
|
id: str
|
||||||
|
|
||||||
|
|
||||||
class File(BaseModel):
|
class File(BaseModel):
|
||||||
@@ -257,7 +265,7 @@ class File(BaseModel):
|
|||||||
|
|
||||||
format: str # Mime format of the file, e.g., 'application/pdf'
|
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 | FileId
|
||||||
|
|
||||||
|
|
||||||
class SendFileOptions(BaseModel):
|
class SendFileOptions(BaseModel):
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
from typing import Any, Dict, Mapping, Optional
|
from typing import Any, Dict, Mapping, Optional
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from pydantic import BaseModel, ValidationError
|
from pydantic import BaseModel, ValidationError
|
||||||
|
|
||||||
@@ -574,19 +576,35 @@ class RTVIProcessor(FrameProcessor):
|
|||||||
type = file.source.type
|
type = file.source.type
|
||||||
opts = data.options if data.options is not None else RTVI.SendFileOptions()
|
opts = data.options if data.options is not None else RTVI.SendFileOptions()
|
||||||
|
|
||||||
if type == "bytes":
|
match type:
|
||||||
source = file.source.bytes
|
case "bytes":
|
||||||
elif type == "url":
|
source = file.source.bytes
|
||||||
if file.source.url.startswith("/files/"):
|
case "url":
|
||||||
|
if not file.source.public:
|
||||||
|
# read bytes from URL and encode to base64
|
||||||
|
type = "bytes"
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(file.source.url) as response:
|
||||||
|
content = io.BytesIO(await response.content.read())
|
||||||
|
base64_string = base64.b64encode(content.getvalue()).decode("utf-8")
|
||||||
|
source = f"data:{file.format};base64,{base64_string}"
|
||||||
|
else:
|
||||||
|
source = file.source.url
|
||||||
|
case "id":
|
||||||
|
if not file.source.id.startswith("pipecat:"):
|
||||||
|
logger.warning(f"Unsupported file ID: {file.source.id}")
|
||||||
|
self.send_error_response(data.id, f"Unsupported file ID: {file.source.id}")
|
||||||
|
return
|
||||||
if not self._folder:
|
if not self._folder:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Send-file with /files/ URL requires uploads_folder on RTVIProcessor "
|
"Send-file with a pipecat id requires uploads_folder on RTVIProcessor "
|
||||||
"(e.g. uploads_folder=runner_uploads_folder())."
|
"(e.g. uploads_folder=runner_uploads_folder())."
|
||||||
)
|
)
|
||||||
|
self.send_error_response(data.id, "Uploads folder not set")
|
||||||
return
|
return
|
||||||
# read bytes from file system, encode to base64, then delete the file
|
# read bytes from file system, encode to base64, then delete the file
|
||||||
type = "bytes"
|
type = "bytes"
|
||||||
file_path = os.path.join(self._folder, file.source.url.removeprefix("/files/"))
|
file_path = os.path.join(self._folder, file.source.id.removeprefix("pipecat:"))
|
||||||
with open(file_path, "rb") as f:
|
with open(file_path, "rb") as f:
|
||||||
raw_bytes = f.read()
|
raw_bytes = f.read()
|
||||||
encoded_image = base64.b64encode(raw_bytes).decode("utf-8")
|
encoded_image = base64.b64encode(raw_bytes).decode("utf-8")
|
||||||
@@ -595,11 +613,10 @@ class RTVIProcessor(FrameProcessor):
|
|||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.warning(f"Failed to remove uploaded file {file_path}: {e}")
|
logger.warning(f"Failed to remove uploaded file {file_path}: {e}")
|
||||||
else:
|
source = file.source.id
|
||||||
source = file.source.url
|
case _:
|
||||||
else:
|
logger.warning(f"Unsupported file source type: {type}")
|
||||||
logger.warning(f"Unsupported file source type: {type}")
|
return
|
||||||
return
|
|
||||||
|
|
||||||
if type == "bytes" and file.format.startswith("image/"):
|
if type == "bytes" and file.format.startswith("image/"):
|
||||||
# Only access width/height if the original source is RTVIFileBytes (not RTVIFileUrl)
|
# Only access width/height if the original source is RTVIFileBytes (not RTVIFileUrl)
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ def _setup_webrtc_routes(app: FastAPI, args: argparse.Namespace):
|
|||||||
# Return the file as a URL that can be used in the client, matching the format of RTVIFile
|
# Return the file as a URL that can be used in the client, matching the format of RTVIFile
|
||||||
return {
|
return {
|
||||||
"name": original_name,
|
"name": original_name,
|
||||||
"source": {"type": "url", "url": f"/files/{safe_name}"},
|
"source": {"type": "id", "id": f"pipecat:{safe_name}"},
|
||||||
"format": media_type,
|
"format": media_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user