Skip to content

File Upload & Custom Responses

File upload types and response helpers for files, HTML, and plain text.

UploadFile

UploadFile dataclass

An uploaded file from a multipart form request.

Attributes:

Name Type Description
filename str

Original filename from the upload.

content_type str

MIME type of the uploaded file.

content bytes

Raw bytes of the file content.

size int

File size in bytes.

Source code in src/agenticapi/interface/upload.py
@dataclass(slots=True)
class UploadFile:
    """An uploaded file from a multipart form request.

    Attributes:
        filename: Original filename from the upload.
        content_type: MIME type of the uploaded file.
        content: Raw bytes of the file content.
        size: File size in bytes.
    """

    filename: str
    content_type: str
    content: bytes
    size: int

FileResult

FileResult dataclass

Convenience wrapper for returning files from agent endpoints.

Handlers return this instead of constructing Starlette responses directly. The framework converts it to the appropriate response type based on the content field:

  • bytes → inline Response with the given media type
  • strFileResponse (interpreted as a file path)
  • async/sync iterable → StreamingResponse
Example

@app.agent_endpoint(name="export") async def export_csv(intent, context): csv_data = "name,value\nalice,42\nbob,17" return FileResult( content=csv_data.encode(), media_type="text/csv", filename="export.csv", )

Attributes:

Name Type Description
content bytes | str | Any

File data — bytes, a file path string, or an iterable for streaming.

media_type str

MIME type of the response (e.g., "application/pdf", "image/png").

filename str | None

Suggested download filename. Sets the Content-Disposition header.

headers dict[str, str] | None

Additional response headers.

Source code in src/agenticapi/interface/response.py
@dataclass(slots=True)
class FileResult:
    """Convenience wrapper for returning files from agent endpoints.

    Handlers return this instead of constructing Starlette responses
    directly. The framework converts it to the appropriate response type
    based on the ``content`` field:

    - ``bytes`` → inline ``Response`` with the given media type
    - ``str`` → ``FileResponse`` (interpreted as a file path)
    - async/sync iterable → ``StreamingResponse``

    Example:
        @app.agent_endpoint(name="export")
        async def export_csv(intent, context):
            csv_data = "name,value\\nalice,42\\nbob,17"
            return FileResult(
                content=csv_data.encode(),
                media_type="text/csv",
                filename="export.csv",
            )

    Attributes:
        content: File data — bytes, a file path string, or an iterable for streaming.
        media_type: MIME type of the response (e.g., "application/pdf", "image/png").
        filename: Suggested download filename. Sets the Content-Disposition header.
        headers: Additional response headers.
    """

    content: bytes | str | Any
    media_type: str = "application/octet-stream"
    filename: str | None = None
    headers: dict[str, str] | None = None

    def to_response(self) -> Response:
        """Convert to the appropriate Starlette response type.

        Returns:
            A Starlette Response, FileResponse, or StreamingResponse.
        """
        extra_headers: dict[str, str] = dict(self.headers) if self.headers else {}
        if self.filename:
            # Sanitize filename: remove path separators and escape quotes
            safe_name = self.filename.replace("/", "_").replace("\\", "_").replace('"', '\\"')
            extra_headers["Content-Disposition"] = f'attachment; filename="{safe_name}"'

        if isinstance(self.content, bytes):
            return Response(
                content=self.content,
                media_type=self.media_type,
                headers=extra_headers or None,
            )

        if isinstance(self.content, str):
            # Resolve the path and ensure it's absolute (prevent path traversal)
            import pathlib

            resolved = pathlib.Path(self.content).resolve()
            return FileResponse(
                path=str(resolved),
                media_type=self.media_type,
                filename=self.filename,
                headers=extra_headers or None,
            )

        if isinstance(self.content, (collections.abc.AsyncIterator, collections.abc.Iterator)):
            return StreamingResponse(
                content=self.content,
                media_type=self.media_type,
                headers=extra_headers or None,
            )

        # Fallback: treat as bytes-like
        return Response(
            content=bytes(self.content),
            media_type=self.media_type,
            headers=extra_headers or None,
        )

to_response

to_response() -> Response

Convert to the appropriate Starlette response type.

Returns:

Type Description
Response

A Starlette Response, FileResponse, or StreamingResponse.

Source code in src/agenticapi/interface/response.py
def to_response(self) -> Response:
    """Convert to the appropriate Starlette response type.

    Returns:
        A Starlette Response, FileResponse, or StreamingResponse.
    """
    extra_headers: dict[str, str] = dict(self.headers) if self.headers else {}
    if self.filename:
        # Sanitize filename: remove path separators and escape quotes
        safe_name = self.filename.replace("/", "_").replace("\\", "_").replace('"', '\\"')
        extra_headers["Content-Disposition"] = f'attachment; filename="{safe_name}"'

    if isinstance(self.content, bytes):
        return Response(
            content=self.content,
            media_type=self.media_type,
            headers=extra_headers or None,
        )

    if isinstance(self.content, str):
        # Resolve the path and ensure it's absolute (prevent path traversal)
        import pathlib

        resolved = pathlib.Path(self.content).resolve()
        return FileResponse(
            path=str(resolved),
            media_type=self.media_type,
            filename=self.filename,
            headers=extra_headers or None,
        )

    if isinstance(self.content, (collections.abc.AsyncIterator, collections.abc.Iterator)):
        return StreamingResponse(
            content=self.content,
            media_type=self.media_type,
            headers=extra_headers or None,
        )

    # Fallback: treat as bytes-like
    return Response(
        content=bytes(self.content),
        media_type=self.media_type,
        headers=extra_headers or None,
    )

HTMLResult

HTMLResult dataclass

Convenience wrapper for returning HTML from agent endpoints.

Equivalent to FastAPI's HTMLResponse. The framework converts it to a Starlette HTMLResponse automatically.

Example

@app.agent_endpoint(name="dashboard") async def dashboard(intent, context): return HTMLResult(content="

Dashboard

Welcome!

")

Attributes:

Name Type Description
content str | bytes

HTML string or bytes.

status_code int

HTTP status code (default 200).

headers dict[str, str] | None

Additional response headers.

Source code in src/agenticapi/interface/response.py
@dataclass(slots=True)
class HTMLResult:
    """Convenience wrapper for returning HTML from agent endpoints.

    Equivalent to FastAPI's ``HTMLResponse``. The framework converts it
    to a Starlette ``HTMLResponse`` automatically.

    Example:
        @app.agent_endpoint(name="dashboard")
        async def dashboard(intent, context):
            return HTMLResult(content="<h1>Dashboard</h1><p>Welcome!</p>")

    Attributes:
        content: HTML string or bytes.
        status_code: HTTP status code (default 200).
        headers: Additional response headers.
    """

    content: str | bytes
    status_code: int = 200
    headers: dict[str, str] | None = None

    def to_response(self) -> Response:
        """Convert to a Starlette HTMLResponse.

        Returns:
            An HTMLResponse with ``text/html`` content type.
        """
        from starlette.responses import HTMLResponse

        return HTMLResponse(
            content=self.content,
            status_code=self.status_code,
            headers=self.headers,
        )

to_response

to_response() -> Response

Convert to a Starlette HTMLResponse.

Returns:

Type Description
Response

An HTMLResponse with text/html content type.

Source code in src/agenticapi/interface/response.py
def to_response(self) -> Response:
    """Convert to a Starlette HTMLResponse.

    Returns:
        An HTMLResponse with ``text/html`` content type.
    """
    from starlette.responses import HTMLResponse

    return HTMLResponse(
        content=self.content,
        status_code=self.status_code,
        headers=self.headers,
    )

PlainTextResult

PlainTextResult dataclass

Convenience wrapper for returning plain text from agent endpoints.

Equivalent to FastAPI's PlainTextResponse.

Example

@app.agent_endpoint(name="status") async def status(intent, context): return PlainTextResult(content="OK")

Attributes:

Name Type Description
content str | bytes

Text string or bytes.

status_code int

HTTP status code (default 200).

headers dict[str, str] | None

Additional response headers.

Source code in src/agenticapi/interface/response.py
@dataclass(slots=True)
class PlainTextResult:
    """Convenience wrapper for returning plain text from agent endpoints.

    Equivalent to FastAPI's ``PlainTextResponse``.

    Example:
        @app.agent_endpoint(name="status")
        async def status(intent, context):
            return PlainTextResult(content="OK")

    Attributes:
        content: Text string or bytes.
        status_code: HTTP status code (default 200).
        headers: Additional response headers.
    """

    content: str | bytes
    status_code: int = 200
    headers: dict[str, str] | None = None

    def to_response(self) -> Response:
        """Convert to a Starlette PlainTextResponse.

        Returns:
            A PlainTextResponse with ``text/plain`` content type.
        """
        from starlette.responses import PlainTextResponse

        return PlainTextResponse(
            content=self.content,
            status_code=self.status_code,
            headers=self.headers,
        )

to_response

to_response() -> Response

Convert to a Starlette PlainTextResponse.

Returns:

Type Description
Response

A PlainTextResponse with text/plain content type.

Source code in src/agenticapi/interface/response.py
def to_response(self) -> Response:
    """Convert to a Starlette PlainTextResponse.

    Returns:
        A PlainTextResponse with ``text/plain`` content type.
    """
    from starlette.responses import PlainTextResponse

    return PlainTextResponse(
        content=self.content,
        status_code=self.status_code,
        headers=self.headers,
    )