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):

View File

@ -158,6 +158,11 @@ async def async_completion(tenant_id, chat_id, question, name="New session", ses
"role": "user",
"id": str(uuid4())
}
# Propagate runtime attachments so downstream chat flow can resolve file content.
if isinstance(kwargs.get("files"), list) and kwargs["files"]:
question["files"] = kwargs["files"]
conv.message.append(question)
for m in conv.message:
if m["role"] == "system":

View File

@ -6124,6 +6124,69 @@ Failure:
---
### Upload document
**POST** `/api/v1/file/upload_info`
Uploads a file and creates the respective document
#### Request
- Method: POST
- URL: `/api/v1/file/upload_info`
- Headers:
- `'Content-Type: multipart/form-data`
- `'Authorization: Bearer <YOUR_API_KEY>'`
- Form:
- `'file=@{FILE_PATH}'`
##### Request example
```bash
curl --request POST \
--url http://{address}/api/v1/file/upload_info \
--header 'Content-Type: multipart/form-data' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--form 'file=@./test1.pdf'
```
##### Request parameters
- `'file'`: (*Form parameter*), `file`, *Required*
The file to upload.
#### Response
Success:
```json
{
"code": 0,
"data": {
"created_at": 1772451421.7924063,
"created by": "be951084066611f18f5f00155d2f98f4",
"extension": "pdf",
"id": "2143a03d162c11f1b80f00155d334d02",
"mime_type": "application/pdf",
"name": "test1.pdf",
"preview_url": null,
"size": 49705
},
"message": "success"
}
```
Failure:
```json
{
"code": 400,
"message": "Provide either multipart file(s) or ?url=...!"
}
```
---
### Create file or folder
**POST** `/api/v1/file/create`