Skip to content

FileUrl schema in FastAPI endpoint #3103

@nicolas-chaulet

Description

@nicolas-chaulet

Initial Checks

Description

I have a FastAPI application that expects pydantic ai ModelMessages as input. It appears that a recent release made the identifier field on FileUrl mandatory for FastAPI. See code sample below.

Example Code

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from pydantic_ai import ModelMessage

app = FastAPI(
    title="Image Message API",
    description="FastAPI service for processing image-based model messages",
    version="1.0.0",
)


class AgentRun(BaseModel):
    messages: list[ModelMessage] = Field(
        ..., min_length=1, description="List of messages in the conversation so far"
    )


@app.post("/process-messages")
async def process_messages(run: AgentRun):
    try:
        # Here you would integrate with your agent to process the messages
        # For demonstration, we just return the received messages
        return {"status": "success", "messages": run.messages}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


if __name__ == "__main__":
    from fastapi.testclient import TestClient
    import json

    client = TestClient(app)

    response = client.post(
        "/process-messages",
        json={
            "messages": [
                {
                    "kind": "request",
                    "parts": [
                        {
                            "part_kind": "user-prompt",
                            "content": [
                                "Describe the content of this image",
                                {
                                    "kind": "image-url",
                                    "url": "https://example.com/image.png",
                                    # "identifier": "example-image",
                                },
                            ],
                        }
                    ],
                }
            ]
        },
    )
    print(json.dumps(response.json(), indent=2))


"""
{
  "detail": [
    {
      "type": "string_type",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "str"
      ],
      "msg": "Input should be a valid string",
      "input": [
        "Describe the content of this image",
        {
          "kind": "image-url",
          "url": "https://example.com/image.png"
        }
      ]
    },
    {
      "type": "string_type",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "str"
      ],
      "msg": "Input should be a valid string",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "ImageUrl",
        "identifier"
      ],
      "msg": "Field required",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "AudioUrl",
        "identifier"
      ],
      "msg": "Field required",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "literal_error",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "AudioUrl",
        "kind"
      ],
      "msg": "Input should be 'audio-url'",
      "input": "image-url",
      "ctx": {
        "expected": "'audio-url'"
      }
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "DocumentUrl",
        "identifier"
      ],
      "msg": "Field required",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "literal_error",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "DocumentUrl",
        "kind"
      ],
      "msg": "Input should be 'document-url'",
      "input": "image-url",
      "ctx": {
        "expected": "'document-url'"
      }
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "VideoUrl",
        "identifier"
      ],
      "msg": "Field required",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "literal_error",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "VideoUrl",
        "kind"
      ],
      "msg": "Input should be 'video-url'",
      "input": "image-url",
      "ctx": {
        "expected": "'video-url'"
      }
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "BinaryContent",
        "data"
      ],
      "msg": "Field required",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "BinaryContent",
        "media_type"
      ],
      "msg": "Field required",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "missing",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "BinaryContent",
        "identifier"
      ],
      "msg": "Field required",
      "input": {
        "kind": "image-url",
        "url": "https://example.com/image.png"
      }
    },
    {
      "type": "literal_error",
      "loc": [
        "body",
        "messages",
        0,
        "request",
        "parts",
        0,
        "user-prompt",
        "content",
        "json-or-python[json=list[union[str,...,...,...,...,...]],python=chain[is-instance[Sequence],function-wrap[sequence_validator()]]]",
        1,
        "BinaryContent",
        "kind"
      ],
      "msg": "Input should be 'binary'",
      "input": "image-url",
      "ctx": {
        "expected": "'binary'"
      }
    }
  ]
}
"""

Python, Pydantic AI & LLM client version

FastAPI: 0.118.0
Pydantic: 1.0.15

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions