Feat: File uploads for future conversations on SDK API (#13378)

### What problem does this PR solve?

This PR aims to:

1. Enable file uploads for the public API, similarly to what
/document/upload_info accomplishes for the frontend;
2. Enable files sent to the /chat/:chat_id/completions endpoint to be
used within the conversation.
We classify the first item as a new future, while classifying the second
one as a bug fix.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)

*The work related to this PR was co-authored by*

[Bruno Ferreira](https://github.com/brunopferreira): Custom Solutions
Manager @ [Orbcom](https://orbcom.pt/)
[Pedro Ferreira](https://github.com/sirj0k3r): Lead Software Developer @
[Orbcom](https://orbcom.pt/)
[Pedro Cardoso](https://github.com/pedromiguel4560): Associate Software
Developer @ [Orbcom](https://orbcom.pt/)

*This PR replaces #13248*

---------

Co-authored-by: Pedro Cardoso <pedrocardoso@orbcom.pt>
Co-authored-by: Pedro Ferreira <pedroferreira@orbcom.pt>
This commit is contained in:
orbcom-pedroferreira
2026-03-04 14:26:58 +00:00
committed by GitHub
parent 020068dd16
commit 61209ff3bf
3 changed files with 124 additions and 0 deletions

View File

@ -148,6 +148,62 @@ async def upload(tenant_id):
return server_error_response(e)
@manager.route("/file/upload_info", methods=["POST"]) # noqa: F821
@token_required
async def upload_info(tenant_id):
"""
Upload runtime file metadata for SDK chat completions.
---
tags:
- File
security:
- ApiKeyAuth: []
parameters:
- in: formData
name: file
type: file
required: false
description: File(s) to upload as runtime attachments.
- in: query
name: url
type: string
required: false
description: Optional URL to fetch and convert into a runtime attachment.
responses:
200:
description: Runtime attachment descriptor(s) for the `files` field in completions requests.
"""
files = await request.files
file_objs = files.getlist("file") if files and files.get("file") else []
url = request.args.get("url")
if file_objs and url:
return get_json_result(
data=False,
message="Provide either multipart file(s) or ?url=..., not both.",
code=RetCode.BAD_REQUEST,
)
if not file_objs and not url:
return get_json_result(
data=False,
message="Missing input: provide multipart file(s) or url",
code=RetCode.BAD_REQUEST,
)
try:
if url and not file_objs:
return get_json_result(data=FileService.upload_info(tenant_id, None, url))
if len(file_objs) == 1:
return get_json_result(data=FileService.upload_info(tenant_id, file_objs[0], None))
results = [FileService.upload_info(tenant_id, f) for f in file_objs]
return get_json_result(data=results)
except Exception as e:
return server_error_response(e)
@manager.route('/file/create', methods=['POST']) # noqa: F821
@token_required
async def create(tenant_id):