← eval_submission_nothink

fastapi_12942

failed NO_PATCH NO_PATCH UNSUBMITTED empty_patch(budget) · 24 tool calls · 313 s · fastapi/fastapi

Task input

🐛 Fix tagged discriminated union not recognized as body field

## Problem

When using a union type models with a tagged discriminator as an endpoint parameter, like the following:

```py
class Pie(BaseModel):
    time_to_cook: int
    num_ingredients: int


class ApplePie(Pie):
    fruit: Literal["apple"] = "apple"


class PumpkinPie(Pie):
    filling: Literal["pumpkin"] = "pumpkin"


def get_discriminator_value(v: Any) -> str:
    if isinstance(v, dict):
        return v.get("fruit", v.get("filling"))
    return getattr(v, "fruit", getattr(v, "filling", None))


Dessert = Annotated[
    Annotated[ApplePie, Tag("apple")] | Annotated[PumpkinPie, Tag("pumpkin")],
    Discriminator(get_discriminator_value),
]


app = FastAPI()
@app.post("/dessert")
async def dinner(dessert: Dessert) -> dict[str, Any]:
    return dessert.model_dump()
```

FastAPI fails to identify it correctly as **body** payload. It incorrectly considers it's a query parameter, which then causes a validation error. The workaround currently is to explicitly tell FastAPI it's a body parameter using `dessert: Annotated[Dessert, Body()]`

## Fix

The solution is to improve the function handling type annotations by handling the case where we can have `Annotated`
 types inside an `Annotated` type.

---

Discussed in https://github.com/fastapi/fastapi/discussions/12941

Tool calls (24)

#ToolArgumentsResult
1run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom typing import Any, Literal, Annotated\nfrom pydantic import BaseModel\nfrom fastapi import FastAPI, Body\nfrom fastapi.params import Tag, Discriminator\nfrom fastapi.testclient import TestClient\n\nclass Pie(BaseModel):\n    time_to_cook: int\n    num_ingredients: int\n\nclass ApplePie(Pie):\n    fruit: Literal[\"apple\"] = \"apple\"\n\nclass PumpkinPie(Pie):\n    filling: Literal[\"pumpkin\"] = \"pumpkin\"\n\ndef get_discriminator_value(v: Any) -> str:\n    if isinstance(v, dict):\n        return v.get(\"fruit\", v.get(\"filling\"))\n    return getattr(v, \"fruit\", getattr(v, \"filling\", None))\n\nDessert = Annotated[\n    Annotated[ApplePie, Tag(\"apple\")] | Annotated[PumpkinPie, Tag(\"pumpkin\")],\n    Discriminator(get_discriminator_value),\n]\n\napp = FastAPI()\n@app.post(\"/dessert\")\nasync def dinner(dessert: Dessert) -> dict[str, Any]:\n    return dessert.model_dump()\n\nclient = TestClient(app)\n\ndef test_repro():\n    # This should be recognized as a body parameter\n    response = client.post(\"/dessert\", json={\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"})\n    assert response.status_code == 200\n    assert response.json() == {\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"}\n\nif __name__ == \"__main__\":\n    try:\n        test_repro()\n        print(\"Success\")\n    except Exception as e:\n        print(f\"Failure: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval_submission_nothink/fastapi_12942/a/tmp/repro.py\", line 4, in <module>\n    from fastapi.params import Tag, Discriminator\nImportError: cannot import name 'Tag' from 'fastapi.params' (/workspace/fastapi/params.py)\n", "exit_code": 1}}
2run_command
{
 "command": "grep -rn \"Tag\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/openapi/models.py:429:class Tag(BaseModelWithConfig):\nfastapi/openapi/models.py:445: tags: Optional[List[Tag]] = None\n", "
full
{"status": "ok", "stdout": "fastapi/openapi/models.py:429:class Tag(BaseModelWithConfig):\nfastapi/openapi/models.py:445:    tags: Optional[List[Tag]] = None\n", "exit_code": 0}
3run_command
{
 "command": "grep -rn \"Discriminator\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/openapi/models.py:106:class Discriminator(BaseModel):\nfastapi/openapi/models.py:205: discriminator: Optional[Discriminator]
full
{"status": "ok", "stdout": "fastapi/openapi/models.py:106:class Discriminator(BaseModel):\nfastapi/openapi/models.py:205:    discriminator: Optional[Discriminator] = None\n", "exit_code": 0}
4run_command
{
 "command": "grep -rn \"Annotated\" fastapi | grep \"get_type_hints\" | head -20"
}
{"status": "ok", "stdout": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "exit_code": 0}
5run_command
{
 "command": "grep -rn \"Annotated\" fastapi/dependencies/utils.py | head -20"
}
{"status": "ok", "stdout": "75:from typing_extensions import Annotated, get_args, get_origin\n370: # Extract Annotated info\n371: if get_origin(use_annotation)
full
{"status": "ok", "stdout": "75:from typing_extensions import Annotated, get_args, get_origin\n370:    # Extract Annotated info\n371:    if get_origin(use_annotation) is Annotated:\n390:        # Set default for Annotated FieldInfo\n400:                f\" `Annotated` for {param_name!r}. Set the default value with `=` instead.\"\n407:        # Get Annotated Depends\n413:            \"Cannot specify `Depends` in `Annotated` and default value\"\n417:            \"Cannot specify a FastAPI annotation in `Annotated` and `Depends` as a\"\n424:            \"Cannot specify FastAPI annotations in `Annotated` and default value\"\n453:    # Handle default assignations, neither field_info nor depends was not found in Annotated nor default value\n", "exit_code": 0}
6read_file
{
 "filepath": "fastapi/dependencies/utils.py",
 "start_line": 360,
 "end_line": 460
}
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": " value: Any,\n is_path_param: bool,\n) -> ParamDetails:\n field_info = None\n depends
full
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "    value: Any,\n    is_path_param: bool,\n) -> ParamDetails:\n    field_info = None\n    depends = None\n    type_annotation: Any = Any\n    use_annotation: Any = Any\n    if annotation is not inspect.Signature.empty:\n        use_annotation = annotation\n        type_annotation = annotation\n    # Extract Annotated info\n    if get_origin(use_annotation) is Annotated:\n        annotated_args = get_args(annotation)\n        type_annotation = annotated_args[0]\n        fastapi_annotations = [\n            arg\n            for arg in annotated_args[1:]\n            if isinstance(arg, (FieldInfo, params.Depends))\n        ]\n        fastapi_specific_annotations = [\n            arg\n            for arg in fastapi_annotations\n            if isinstance(arg, (params.Param, params.Body, params.Depends))\n        ]\n        if fastapi_specific_annotations:\n            fastapi_annotation: Union[FieldInfo, params.Depends, None] = (\n                fastapi_specific_annotations[-1]\n            )\n        else:\n            fastapi_annotation = None\n        # Set default for Annotated FieldInfo\n        if isinstance(fastapi_annotation, FieldInfo):\n            # Copy `field_info` because we mutate `field_info.default` below.\n            field_info = copy_field_info(\n                field_info=fastapi_annotation, annotation=use_annotation\n            )\n            assert (\n                field_info.default is Undefined or field_info.default is RequiredParam\n            ), (\n                f\"`{field_info.__class__.__name__}` default value cannot be set in\"\n                f\" `Annotated` for {param_name!r}. Set the default value with `=` instead.\"\n            )\n            if value is not inspect.Signature.empty:\n                assert not is_path_param, \"Path parameters cannot have default values\"\n                field_info.default = value\n            else:\n                field_
7run_command
{
 "command": "grep -rn \"Discriminator\" fastapi/params.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
8run_command
{
 "command": "grep -rn \"Discriminator\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/openapi/models.py:106:class Discriminator(BaseModel):\nfastapi/openapi/models.py:205: discriminator: Optional[Discriminator]
full
{"status": "ok", "stdout": "fastapi/openapi/models.py:106:class Discriminator(BaseModel):\nfastapi/openapi/models.py:205:    discriminator: Optional[Discriminator] = None\n", "exit_code": 0}
9run_command
{
 "command": "grep -rn \"Discriminator\" . | grep \".py\" | head -20"
}
{"status": "ok", "stdout": "./fastapi/openapi/models.py:106:class Discriminator(BaseModel):\n./fastapi/openapi/models.py:205: discriminator: Optional[Discrimina
full
{"status": "ok", "stdout": "./fastapi/openapi/models.py:106:class Discriminator(BaseModel):\n./fastapi/openapi/models.py:205:    discriminator: Optional[Discriminator] = None\n", "exit_code": 0}
10run_command
{
 "command": "grep -rn \"Pydantic\" fastapi/dependencies/utils.py | head -20"
}
{"status": "ok", "stdout": "514: # For Pydantic v1\n765: # If headers are in a Pydantic model, the way to disable convert_underscores\n766: # would be with Head
full
{"status": "ok", "stdout": "514:                    # For Pydantic v1\n765:        # If headers are in a Pydantic model, the way to disable convert_underscores\n766:        # would be with Header(convert_underscores=False) at the Pydantic model level\n778:            # Handle fields extracted from a Pydantic Model for a header, each field\n", "exit_code": 0}
11run_command
{
 "command": "grep -rn \"Body\" fastapi/params.py | head -20"
}
{"status": "ok", "stdout": "479:class Body(FieldInfo): # type: ignore[misc]\n596:class Form(Body): # type: ignore[misc]\n", "exit_code": 0}
full
{"status": "ok", "stdout": "479:class Body(FieldInfo):  # type: ignore[misc]\n596:class Form(Body):  # type: ignore[misc]\n", "exit_code": 0}
12read_file
{
 "end_line": 500,
 "filepath": "fastapi/params.py",
 "start_line": 450
}
{"status": "ok", "filepath": "fastapi/params.py", "content": " alias_priority=alias_priority,\n validation_alias=validation_alias,\n serialization_alias=seriali
full
{"status": "ok", "filepath": "fastapi/params.py", "content": "            alias_priority=alias_priority,\n            validation_alias=validation_alias,\n            serialization_alias=serialization_alias,\n            title=title,\n            description=description,\n            gt=gt,\n            ge=ge,\n            lt=lt,\n            le=le,\n            min_length=min_length,\n            max_length=max_length,\n            pattern=pattern,\n            regex=regex,\n            discriminator=discriminator,\n            strict=strict,\n            multiple_of=multiple_of,\n            allow_inf_nan=allow_inf_nan,\n            max_digits=max_digits,\n            decimal_places=decimal_places,\n            deprecated=deprecated,\n            example=example,\n            examples=examples,\n            openapi_examples=openapi_examples,\n            include_in_schema=include_in_schema,\n            json_schema_extra=json_schema_extra,\n            **extra,\n        )\n\n\nclass Body(FieldInfo):  # type: ignore[misc]\n    def __init__(\n        self,\n        default: Any = Undefined,\n        *,\n        default_factory: Union[Callable[[], Any], None] = _Unset,\n        annotation: Optional[Any] = None,\n        embed: Union[bool, None] = None,\n        media_type: str = \"application/json\",\n        alias: Optional[str] = None,\n        alias_priority: Union[int, None] = _Unset,\n        # TODO: update when deprecating Pydantic v1, import these types\n        # validation_alias: str | AliasPath | AliasChoices | None\n        validation_alias: Union[str, None] = None,\n        serialization_alias: Union[str, None] = None,\n        title: Optional[str] = None,\n        description: Optional[str] = None,\n        gt: Optional[float] = None,\n        ge: Optional[float] = None,\n        lt: Optional[float] = None,\n        le: Optional[float] = None,\n        min_length: Optional[int] = None,\n", "start_line": 450, "end_line": 500, "total_lines": 786, "is_trunca
13run_command
{
 "command": "grep -rn \"Discriminator\" . | grep \"pydantic\" | head -20"
}
{"status": "ok", "stdout": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "exit_code": 0}
14run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom typing import Any, Literal, Annotated, Union\nfrom pydantic import BaseModel\nfrom fastapi import FastAPI, Body\nfrom fastapi.testclient import TestClient\n\nclass Pie(BaseModel):\n    time_to_cook: int\n    num_ingredients: int\n\nclass ApplePie(Pie):\n    fruit: Literal[\"apple\"] = \"apple\"\n\nclass PumpkinPie(Pie):\n    filling: Literal[\"pumpkin\"] = \"pumpkin\"\n\ndef get_discriminator_value(v: Any) -> str:\n    if isinstance(v, dict):\n        return v.get(\"fruit\", v.get(\"filling\"))\n    return getattr(v, \"fruit\", getattr(v, \"filling\", None))\n\n# Using Pydantic's Discriminator if available, or just a type that looks like it\n# The issue says:\n# Dessert = Annotated[\n#     Annotated[ApplePie, Tag(\"apple\")] | Annotated[PumpkinPie, Tag(\"pumpkin\")],\n#     Discriminator(get_discriminator_value),\n# ]\n# But Tag and Discriminator are not in fastapi.params.\n# They might be from pydantic.\n\ntry:\n    from pydantic import Discriminator\nexcept ImportError:\n    # Mock Discriminator for the sake of reproduction if not present\n    class Discriminator:\n        def __init__(self, func):\n            self.func = func\n\n# Mock Tag since it's not found\nclass Tag:\n    def __init__(self, value):\n        self.value = value\n\nDessert = Annotated[\n    Annotated[ApplePie, Tag(\"apple\")] | Annotated[PumpkinPie, Tag(\"pumpkin\")],\n    Discriminator(get_discriminator_value),\n]\n\napp = FastAPI()\n@app.post(\"/dessert\")\nasync def dinner(dessert: Dessert) -> dict[str, Any]:\n    return dessert.model_dump()\n\nclient = TestClient(app)\n\ndef test_repro():\n    # This should be recognized as a body parameter\n    response = client.post(\"/dessert\", json={\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"})\n    print(f\"Status code: {response.status_code}\")\n    print(f\"Response: {response.json()}\")\n    assert response.status_code == 200\n    assert response.json() == {\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"}\n\nif __name__ == \"__main__\":\n    try:\n        test_repro()\n        print(\"Success\")\n    except Exception as e:\n        import traceback\n        traceback.print_exc()\n        print(f\"Failure: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval_submission_nothink/fastapi_12942/a/tmp/repro.py\", line 49, in <module>\n    @app.post(\"/dessert\")\n     ~~~~~~~~^^^^^^^^^^^^\n  File \"/workspace/fastapi/routing.py\", line 1070, in decorator\n    self.add_api_route(\n    ~~~~~~~~~~~~~~~~~~^\n        path,\n        ^^^^^\n    ...<23 lines>...\n        generate_unique_id_function=generate_unique_id_function,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/routing.py\", line 1009, in add_api_route\n    route = route_class(\n        self.prefix + path,\n    ...<24 lines>...\n        generate_unique_id_function=current_generate_unique_id,\n    )\n  File \"/workspace/fastapi/routing.py\", line 630, in __init__\n    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)\n                     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/workspace/fastapi/dependencies/utils.py\", line 293, in get_dependant\n    param_details = analyze_param(\n        param_name=param_name,\n    ...<2 lines>...\n        is_path_param=is_path_param,\n    )\n  File \"/workspace/fastapi/dependencies/utils.py\", line 496, in analyze_param\n    field = create_model_field(\n        name=param_name,\n    ...<4 lines>...\n        field_info=field_info,\n    )\n  File \"/workspace/fastapi/utils.py\", line 98, in create_model_field\n    raise fastapi.exceptions.FastAPIError(\n    ...<7 lines>...\n    ) from None\nfastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.Annotated[typing.Union[typing.Annotated[__main__.ApplePie, <__main__.Tag object at 0x7af19417bb60>], typing.Annotated[__main__.PumpkinPie, <__main__.Tag object at 0x7af194973ed0>]], Discriminator(discriminator=<function get_discriminator_value at 0x7a
15run_command
{
 "command": "grep -rn \"Discriminator\" /workspace/fastapi/params.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "grep: /tmp/swe_work/e
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "grep: /tmp/swe_work/eval_submission_nothink/fastapi_12942/a/workspace/fastapi/params.py: No such file or directory\n", "exit_code": 2}}
16run_command
{
 "command": "grep -rn \"Discriminator\" /workspace/fastapi | head -20"
}
{"status": "ok", "stdout": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "exit_code": 0}
17run_command
{
 "command": "grep -rn \"Annotated\" fastapi/dependencies/utils.py | grep -C 5 \"get_origin\" | head -40"
}
{"status": "ok", "stdout": "75:from typing_extensions import Annotated, get_args, get_origin\n370: # Extract Annotated info\n371: if get_origin(use_annotation)
full
{"status": "ok", "stdout": "75:from typing_extensions import Annotated, get_args, get_origin\n370:    # Extract Annotated info\n371:    if get_origin(use_annotation) is Annotated:\n390:        # Set default for Annotated FieldInfo\n400:                f\" `Annotated` for {param_name!r}. Set the default value with `=` instead.\"\n407:        # Get Annotated Depends\n413:            \"Cannot specify `Depends` in `Annotated` and default value\"\n417:            \"Cannot specify a FastAPI annotation in `Annotated` and `Depends` as a\"\n", "exit_code": 0}
18read_file
{
 "end_line": 410,
 "filepath": "fastapi/dependencies/utils.py",
 "start_line": 360
}
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": " value: Any,\n is_path_param: bool,\n) -> ParamDetails:\n field_info = None\n depends
full
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "    value: Any,\n    is_path_param: bool,\n) -> ParamDetails:\n    field_info = None\n    depends = None\n    type_annotation: Any = Any\n    use_annotation: Any = Any\n    if annotation is not inspect.Signature.empty:\n        use_annotation = annotation\n        type_annotation = annotation\n    # Extract Annotated info\n    if get_origin(use_annotation) is Annotated:\n        annotated_args = get_args(annotation)\n        type_annotation = annotated_args[0]\n        fastapi_annotations = [\n            arg\n            for arg in annotated_args[1:]\n            if isinstance(arg, (FieldInfo, params.Depends))\n        ]\n        fastapi_specific_annotations = [\n            arg\n            for arg in fastapi_annotations\n            if isinstance(arg, (params.Param, params.Body, params.Depends))\n        ]\n        if fastapi_specific_annotations:\n            fastapi_annotation: Union[FieldInfo, params.Depends, None] = (\n                fastapi_specific_annotations[-1]\n            )\n        else:\n            fastapi_annotation = None\n        # Set default for Annotated FieldInfo\n        if isinstance(fastapi_annotation, FieldInfo):\n            # Copy `field_info` because we mutate `field_info.default` below.\n            field_info = copy_field_info(\n                field_info=fastapi_annotation, annotation=use_annotation\n            )\n            assert (\n                field_info.default is Undefined or field_info.default is RequiredParam\n            ), (\n                f\"`{field_info.__class__.__name__}` default value cannot be set in\"\n                f\" `Annotated` for {param_name!r}. Set the default value with `=` instead.\"\n            )\n            if value is not inspect.Signature.empty:\n                assert not is_path_param, \"Path parameters cannot have default values\"\n                field_info.default = value\n            else:\n                field_
19run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom typing import Any, Literal, Annotated, Union\nfrom pydantic import BaseModel, Discriminator\nfrom fastapi import FastAPI, Body\nfrom fastapi.testclient import TestClient\n\nclass Pie(BaseModel):\n    time_to_cook: int\n    num_ingredients: int\n\nclass ApplePie(Pie):\n    fruit: Literal[\"apple\"] = \"apple\"\n\nclass PumpkinPie(Pie):\n    filling: Literal[\"pumpkin\"] = \"pumpkin\"\n\ndef get_discriminator_value(v: Any) -> str:\n    if isinstance(v, dict):\n        return v.get(\"fruit\", v.get(\"filling\"))\n    return getattr(v, \"fruit\", getattr(v, \"filling\", None))\n\n# Mock Tag since it's not a standard Pydantic/FastAPI thing in the way used here\n# but the issue says Annotated[ApplePie, Tag(\"apple\")]\nclass Tag:\n    def __init__(self, value):\n        self.value = value\n\nDessert = Annotated[\n    Union[Annotated[ApplePie, Tag(\"apple\")], Annotated[PumpkinPie, Tag(\"pumpkin\")]],\n    Discriminator(get_discriminator_value),\n]\n\napp = FastAPI()\n@app.post(\"/dessert\")\nasync def dinner(dessert: Dessert) -> dict[str, Any]:\n    return dessert.model_dump()\n\nclient = TestClient(app)\n\ndef test_repro():\n    # This should be recognized as a body parameter\n    response = client.post(\"/dessert\", json={\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"})\n    print(f\"Status code: {response.status_code}\")\n    print(f\"Response: {response.json()}\")\n    assert response.status_code == 200\n    assert response.json() == {\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"}\n\nif __name__ == \"__main__\":\n    try:\n        test_repro()\n        print(\"Success\")\n    except Exception as e:\n        import traceback\n        traceback.print_exc()\n        print(f\"Failure: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval_submission_nothink/fastapi_12942/a/tmp/repro.py\", line 33, in <module>\n    @app.post(\"/dessert\")\n     ~~~~~~~~^^^^^^^^^^^^\n  File \"/workspace/fastapi/routing.py\", line 1070, in decorator\n    self.add_api_route(\n    ~~~~~~~~~~~~~~~~~~^\n        path,\n        ^^^^^\n    ...<23 lines>...\n        generate_unique_id_function=generate_unique_id_function,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/routing.py\", line 1009, in add_api_route\n    route = route_class(\n        self.prefix + path,\n    ...<24 lines>...\n        generate_unique_id_function=current_generate_unique_id,\n    )\n  File \"/workspace/fastapi/routing.py\", line 630, in __init__\n    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)\n                     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/workspace/fastapi/dependencies/utils.py\", line 293, in get_dependant\n    param_details = analyze_param(\n        param_name=param_name,\n    ...<2 lines>...\n        is_path_param=is_path_param,\n    )\n  File \"/workspace/fastapi/dependencies/utils.py\", line 496, in analyze_param\n    field = create_model_field(\n        name=param_name,\n    ...<4 lines>...\n        field_info=field_info,\n    )\n  File \"/workspace/fastapi/utils.py\", line 98, in create_model_field\n    raise fastapi.exceptions.FastAPIError(\n    ...<7 lines>...\n    ) from None\nfastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.Annotated[typing.Union[typing.Annotated[__main__.ApplePie, <__main__.Tag object at 0x7de203773b60>], typing.Annotated[__main__.PumpkinPie, <__main__.Tag object at 0x7de203f6bed0>]], Discriminator(discriminator=<function get_discriminator_value at 0x7d
20run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom typing import Any, Literal, Annotated, Union\nfrom pydantic import BaseModel, Discriminator\nfrom fastapi import FastAPI, Body\nfrom fastapi.testclient import TestClient\n\nclass Pie(BaseModel):\n    time_to_cook: int\n    num_ingredients: int\n\nclass ApplePie(Pie):\n    fruit: Literal[\"apple\"] = \"apple\"\n\nclass PumpkinPie(Pie):\n    filling: Literal[\"pumpkin\"] = \"pumpkin\"\n\ndef get_discriminator_value(v: Any) -> str:\n    if isinstance(v, dict):\n        return v.get(\"fruit\", v.get(\"filling\"))\n    return getattr(v, \"fruit\", getattr(v, \"filling\", None))\n\n# Using a simple type instead of Annotated[ApplePie, Tag(\"apple\")] to see if it's the nested Annotated\nDessert = Annotated[\n    Union[ApplePie, PumpkinPie],\n    Discriminator(get_discriminator_value),\n]\n\napp = FastAPI()\n@app.post(\"/dessert\")\nasync def dinner(dessert: Dessert) -> dict[str, Any]:\n    return dessert.model_dump()\n\nclient = TestClient(app)\n\ndef test_repro():\n    response = client.post(\"/dessert\", json={\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"})\n    print(f\"Status code: {response.status_code}\")\n    print(f\"Response: {response.json()}\")\n    assert response.status_code == 200\n    assert response.json() == {\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"}\n\nif __name__ == \"__main__\":\n    try:\n        test_repro()\n        print(\"Success\")\n    except Exception as e:\n        import traceback\n        traceback.print_exc()\n        print(f\"Failure: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval_submission_nothink/fastapi_12942/a/tmp/repro.py\", line 28, in <module>\n    @app.post(\"/dessert\")\n     ~~~~~~~~^^^^^^^^^^^^\n  File \"/workspace/fastapi/routing.py\", line 1070, in decorator\n    self.add_api_route(\n    ~~~~~~~~~~~~~~~~~~^\n        path,\n        ^^^^^\n    ...<23 lines>...\n        generate_unique_id_function=generate_unique_id_function,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/routing.py\", line 1009, in add_api_route\n    route = route_class(\n        self.prefix + path,\n    ...<24 lines>...\n        generate_unique_id_function=current_generate_unique_id,\n    )\n  File \"/workspace/fastapi/routing.py\", line 630, in __init__\n    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)\n                     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/workspace/fastapi/dependencies/utils.py\", line 293, in get_dependant\n    param_details = analyze_param(\n        param_name=param_name,\n    ...<2 lines>...\n        is_path_param=is_path_param,\n    )\n  File \"/workspace/fastapi/dependencies/utils.py\", line 496, in analyze_param\n    field = create_model_field(\n        name=param_name,\n    ...<4 lines>...\n        field_info=field_info,\n    )\n  File \"/workspace/fastapi/utils.py\", line 98, in create_model_field\n    raise fastapi.exceptions.FastAPIError(\n    ...<7 lines>...\n    ) from None\nfastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.Annotated[typing.Union[__main__.ApplePie, __main__.PumpkinPie], Discriminator(discriminator=<function get_discriminator_value at 0x7fc80592f7e0>, custom_error_type=None, custom_error_message=None, custom_error_context=None)] is a valid Pydantic field 
21run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom typing import Any, Literal, Annotated, Union\nfrom pydantic import BaseModel, Discriminator\nfrom fastapi import FastAPI, Body\nfrom fastapi.testclient import TestClient\n\nclass Pie(BaseModel):\n    time_to_cook: int\n    num_ingredients: int\n\nclass ApplePie(Pie):\n    fruit: Literal[\"apple\"] = \"apple\"\n\nclass PumpkinPie(Pie):\n    filling: Literal[\"pumpkin\"] = \"pumpkin\"\n\ndef get_discriminator_value(v: Any) -> str:\n    if isinstance(v, dict):\n        return v.get(\"fruit\", v.get(\"filling\"))\n    return getattr(v, \"fruit\", getattr(v, \"filling\", None))\n\n# Use a simple Pydantic model to see if it works\nclass DessertModel(BaseModel):\n    dessert: Union[ApplePie, PumpkinPie]\n\napp = FastAPI()\n@app.post(\"/dessert\")\nasync def dinner(dessert: DessertModel) -> dict[str, Any]:\n    return dessert.model_dump()\n\nclient = TestClient(app)\n\ndef test_repro():\n    response = client.post(\"/dessert\", json={\"dessert\": {\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"}})\n    print(f\"Status code: {response.status_code}\")\n    print(f\"Response: {response.json()}\")\n    assert response.status_code == 200\n\nif __name__ == \"__main__\":\n    try:\n        test_repro()\n        print(\"Success\")\n    except Exception as e:\n        import traceback\n        traceback.print_exc()\n        print(f\"Failure: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "ok", "stdout": "Status code: 200\nResponse: {'dessert': {'time_to_cook': 10, 'num_ingredients': 5, 'fruit': 'apple'}}\nSuccess\n", "exit_code": 0}
full
{"status": "ok", "stdout": "Status code: 200\nResponse: {'dessert': {'time_to_cook': 10, 'num_ingredients': 5, 'fruit': 'apple'}}\nSuccess\n", "exit_code": 0}
22run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom typing import Any, Literal, Annotated, Union\nfrom pydantic import BaseModel, Discriminator\nfrom fastapi import FastAPI, Body\nfrom fastapi.testclient import TestClient\n\nclass Pie(BaseModel):\n    time_to_cook: int\n    num_ingredients: int\n\nclass ApplePie(Pie):\n    fruit: Literal[\"apple\"] = \"apple\"\n\nclass PumpkinPie(Pie):\n    filling: Literal[\"pumpkin\"] = \"pumpkin\"\n\ndef get_discriminator_value(v: Any) -> str:\n    if isinstance(v, dict):\n        return v.get(\"fruit\", v.get(\"filling\"))\n    return getattr(v, \"fruit\", getattr(v, \"filling\", None))\n\n# The issue is that when using Annotated[Union[...], Discriminator(...)], \n# FastAPI doesn't recognize it as a body parameter.\nDessert = Annotated[\n    Union[ApplePie, PumpkinPie],\n    Discriminator(get_discriminator_value),\n]\n\napp = FastAPI()\n@app.post(\"/dessert\")\nasync def dinner(dessert: Dessert) -> dict[str, Any]:\n    return dessert.model_dump()\n\nclient = TestClient(app)\n\ndef test_repro():\n    # If it's recognized as a body parameter, this should work.\n    # If it's recognized as a query parameter, it will fail with 422.\n    response = client.post(\"/dessert\", json={\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"})\n    print(f\"Status code: {response.status_code}\")\n    print(f\"Response: {response.json()}\")\n    assert response.status_code == 200\n\nif __name__ == \"__main__\":\n    try:\n        test_repro()\n        print(\"Success\")\n    except Exception as e:\n        import traceback\n        traceback.print_exc()\n        print(f\"Failure: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval_submission_nothink/fastapi_12942/a/tmp/repro.py\", line 29, in <module>\n    @app.post(\"/dessert\")\n     ~~~~~~~~^^^^^^^^^^^^\n  File \"/workspace/fastapi/routing.py\", line 1070, in decorator\n    self.add_api_route(\n    ~~~~~~~~~~~~~~~~~~^\n        path,\n        ^^^^^\n    ...<23 lines>...\n        generate_unique_id_function=generate_unique_id_function,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/routing.py\", line 1009, in add_api_route\n    route = route_class(\n        self.prefix + path,\n    ...<24 lines>...\n        generate_unique_id_function=current_generate_unique_id,\n    )\n  File \"/workspace/fastapi/routing.py\", line 630, in __init__\n    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)\n                     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/workspace/fastapi/dependencies/utils.py\", line 293, in get_dependant\n    param_details = analyze_param(\n        param_name=param_name,\n    ...<2 lines>...\n        is_path_param=is_path_param,\n    )\n  File \"/workspace/fastapi/dependencies/utils.py\", line 496, in analyze_param\n    field = create_model_field(\n        name=param_name,\n    ...<4 lines>...\n        field_info=field_info,\n    )\n  File \"/workspace/fastapi/utils.py\", line 98, in create_model_field\n    raise fastapi.exceptions.FastAPIError(\n    ...<7 lines>...\n    ) from None\nfastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.Annotated[typing.Union[__main__.ApplePie, __main__.PumpkinPie], Discriminator(discriminator=<function get_discriminator_value at 0x7c825b979620>, custom_error_type=None, custom_error_message=None, custom_error_context=None)] is a valid Pydantic field 
23read_file
{
 "end_line": 460,
 "filepath": "fastapi/dependencies/utils.py",
 "start_line": 360
}
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": " value: Any,\n is_path_param: bool,\n) -> ParamDetails:\n field_info = None\n depends
full
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "content": "    value: Any,\n    is_path_param: bool,\n) -> ParamDetails:\n    field_info = None\n    depends = None\n    type_annotation: Any = Any\n    use_annotation: Any = Any\n    if annotation is not inspect.Signature.empty:\n        use_annotation = annotation\n        type_annotation = annotation\n    # Extract Annotated info\n    if get_origin(use_annotation) is Annotated:\n        annotated_args = get_args(annotation)\n        type_annotation = annotated_args[0]\n        fastapi_annotations = [\n            arg\n            for arg in annotated_args[1:]\n            if isinstance(arg, (FieldInfo, params.Depends))\n        ]\n        fastapi_specific_annotations = [\n            arg\n            for arg in fastapi_annotations\n            if isinstance(arg, (params.Param, params.Body, params.Depends))\n        ]\n        if fastapi_specific_annotations:\n            fastapi_annotation: Union[FieldInfo, params.Depends, None] = (\n                fastapi_specific_annotations[-1]\n            )\n        else:\n            fastapi_annotation = None\n        # Set default for Annotated FieldInfo\n        if isinstance(fastapi_annotation, FieldInfo):\n            # Copy `field_info` because we mutate `field_info.default` below.\n            field_info = copy_field_info(\n                field_info=fastapi_annotation, annotation=use_annotation\n            )\n            assert (\n                field_info.default is Undefined or field_info.default is RequiredParam\n            ), (\n                f\"`{field_info.__class__.__name__}` default value cannot be set in\"\n                f\" `Annotated` for {param_name!r}. Set the default value with `=` instead.\"\n            )\n            if value is not inspect.Signature.empty:\n                assert not is_path_param, \"Path parameters cannot have default values\"\n                field_info.default = value\n            else:\n                field_
24run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nfrom typing import Any, Literal, Annotated, Union\nfrom pydantic import BaseModel, Discriminator\nfrom fastapi import FastAPI, Body\nfrom fastapi.testclient import TestClient\n\nclass Pie(BaseModel):\n    time_to_cook: int\n    num_ingredients: int\n\nclass ApplePie(Pie):\n    fruit: Literal[\"apple\"] = \"apple\"\n\nclass PumpkinPie(Pie):\n    filling: Literal[\"pumpkin\"] = \"pumpkin\"\n\ndef get_discriminator_value(v: Any) -> str:\n    if isinstance(v, dict):\n        return v.get(\"fruit\", v.get(\"filling\"))\n    return getattr(v, \"fruit\", getattr(v, \"filling\", None))\n\n# The issue is that when using Annotated[Union[...], Discriminator(...)], \n# FastAPI doesn't recognize it as a body parameter.\nDessert = Annotated[\n    Union[ApplePie, PumpkinPie],\n    Discriminator(get_discriminator_value),\n]\n\napp = FastAPI()\n@app.post(\"/dessert\")\nasync def dinner(dessert: Annotated[Dessert, Body()]) -> dict[str, Any]:\n    return dessert.model_dump()\n\nclient = TestClient(app)\n\ndef test_repro():\n    response = client.post(\"/dessert\", json={\"time_to_cook\": 10, \"num_ingredients\": 5, \"fruit\": \"apple\"})\n    print(f\"Status code: {response.status_code}\")\n    print(f\"Response: {response.json()}\")\n    assert response.status_code == 200\n\nif __name__ == \"__main__\":\n    try:\n        test_repro()\n        print(\"Success\")\n    except Exception as e:\n        import traceback\n        traceback.print_exc()\n        print(f\"Failure: {e}\")\nEOF\npython3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"/tmp/swe_work/eval_submission_nothink/fastapi_12942/a/tmp/repro.py\", line 29, in <module>\n    @app.post(\"/dessert\")\n     ~~~~~~~~^^^^^^^^^^^^\n  File \"/workspace/fastapi/routing.py\", line 1070, in decorator\n    self.add_api_route(\n    ~~~~~~~~~~~~~~~~~~^\n        path,\n        ^^^^^\n    ...<23 lines>...\n        generate_unique_id_function=generate_unique_id_function,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/workspace/fastapi/routing.py\", line 1009, in add_api_route\n    route = route_class(\n        self.prefix + path,\n    ...<24 lines>...\n        generate_unique_id_function=current_generate_unique_id,\n    )\n  File \"/workspace/fastapi/routing.py\", line 630, in __init__\n    self.dependant = get_dependant(path=self.path_format, call=self.endpoint)\n                     ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/workspace/fastapi/dependencies/utils.py\", line 293, in get_dependant\n    param_details = analyze_param(\n        param_name=param_name,\n    ...<2 lines>...\n        is_path_param=is_path_param,\n    )\n  File \"/workspace/fastapi/dependencies/utils.py\", line 496, in analyze_param\n    field = create_model_field(\n        name=param_name,\n    ...<4 lines>...\n        field_info=field_info,\n    )\n  File \"/workspace/fastapi/utils.py\", line 98, in create_model_field\n    raise fastapi.exceptions.FastAPIError(\n    ...<7 lines>...\n    ) from None\nfastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that typing.Annotated[typing.Union[__main__.ApplePie, __main__.PumpkinPie], Discriminator(discriminator=<function get_discriminator_value at 0x7c95ed72d620>, custom_error_type=None, custom_error_message=None, custom_error_context=None), Body(PydanticUndefined)] i