โ† pod_step15_v1_lora

fastapi_13207

failed CONTEXT LOOP UNSUBMITTED agent_error ยท 32 tool calls ยท 94 s ยท fastapi/fastapi

Task input

๐Ÿ› Fix OpenAPI schema support for computed fields when using `separate_input_output_schemas=False`

This demonstrates how the output schema differs when the FastAPI app runs with different values for `separate_input_output_schemas` - namely, the computed fields are omitted.

Initially reported here: https://github.com/fastapi/fastapi/discussions/10342

Tool calls (32)

#ToolArgumentsResult
1-3 ร—3run_command
{
 "command": "find /workspace -maxdepth 2 -not -path '*/.*'"
}
{"status": "ok", "stdout": "/workspace\n/workspace/pytest.ini\n/workspace/LICENSE\n/workspace/requirements.txt\n/workspace/CITATION.cff\n/workspace/pyproject.to
full
{"status": "ok", "stdout": "/workspace\n/workspace/pytest.ini\n/workspace/LICENSE\n/workspace/requirements.txt\n/workspace/CITATION.cff\n/workspace/pyproject.toml\n/workspace/tests\n/workspace/tests/test_datastructures.py\n/workspace/tests/test_filter_pydantic_sub_model\n/workspace/tests/test_starlette_exception.py\n/workspace/tests/test_multi_body_errors.py\n/workspace/tests/test_security_http_digest_description.py\n/workspace/tests/test_computed_fields.py\n/workspace/tests/test_security_oauth2.py\n/workspace/tests/test_openapi_schema_type.py\n/workspace/tests/test_param_in_path_and_dependency.py\n/workspace/tests/test_pydantic_v1_v2_01.py\n/workspace/tests/test_security_http_basic_optional.py\n/workspace/tests/test_depends_hashable.py\n/workspace/tests/test_response_model_invalid.py\n/workspace/tests/test_dependency_yield_scope.py\n/workspace/tests/test_multi_query_errors.py\n/workspace/tests/test_additional_responses_bad.py\n/workspace/tests/test_dependency_security_overrides.py\n/workspace/tests/test_pydantic_v1_v2_list.py\n/workspace/tests/test_response_change_status_code.py\n/workspace/tests/test_compat_params_v1.py\n/workspace/tests/test_include_route.py\n/workspace/tests/test_forms_from_non_typing_sequences.py\n/workspace/tests/test_union_forms.py\n/workspace/tests/test_security_scopes_dont_propagate.py\n/workspace/tests/test_schema_extra_examples.py\n/workspace/tests/test_default_response_class_router.py\n/workspace/tests/test_invalid_sequence_param.py\n/workspace/tests/test_custom_swagger_ui_redirect.py\n/workspace/tests/test_response_model_sub_types.py\n/workspace/tests/test_params_repr.py\n/workspace/tests/test_schema_ref_pydantic_v2.py\n/workspace/tests/test_modules_same_name_body\n/workspace/tests/test_security_openid_connect_description.py\n/workspace/tests/test_inherited_custom_class.py\n/workspace/tests/test_union_inherited_body.py\n/workspace/tests/test_response_model_as_return_annotation.py\n/workspace/tests/test_invalid_path_param.py\n/workspace/
4run_command
{
 "command": "grep -r \"separate_input_output_schemas\" ."
}
{"status": "ok", "stdout": "./tests/test_openapi_separate_input_output_schemas.py:def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\
full
{"status": "ok", "stdout": "./tests/test_openapi_separate_input_output_schemas.py:def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\n./tests/test_openapi_separate_input_output_schemas.py:    app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client = get_app_client(separate_input_output_schemas=False)\n./docs/ru/docs/how-to/separate-openapi-schemas.md:\u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432 FastAPI \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 `separate_input_output_schemas=False`.\n./docs/ru/docs/how-to/separate-openapi-schemas.md:\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 `separate_input_output_schemas` \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0432 FastAPI `0.102.0`. \ud83e\udd13\n./docs/pt/docs/how-to/separate-openapi-schemas.md:Nesse caso, voc\u00ea pode desativar esse recurso no **FastAPI**, com o par\u00e2metro `separate_input_output_schemas=False`.\n./docs/pt/docs/how-to/separate-openapi-schemas.md:O suporte para `separate_input_output_schemas` foi adicionado no FastAPI `0.102.0`. \ud83e\udd13\n./docs/de/docs/how-to/separate
5run_command
{
 "command": "grep -r \"computed_field\" ."
}
{"status": "ok", "stdout": "./tests/test_computed_fields.py: from pydantic import BaseModel, computed_field\n./tests/test_computed_fields.py: @computed_field\n.
full
{"status": "ok", "stdout": "./tests/test_computed_fields.py:    from pydantic import BaseModel, computed_field\n./tests/test_computed_fields.py:        @computed_field\n./docs/en/docs/release-notes.md:* \u2728 Enable Pydantic's serialization mode for responses, add support for Pydantic's `computed_field`, better OpenAPI for response models, proper required attributes, better generated clients. PR [#10011](https://github.com/tiangolo/fastapi/pull/10011) by [@tiangolo](https://github.com/tiangolo).\nBinary file ./.git/index matches\n", "stderr": "", "exit_code": 0}
6read_file
{
 "filepath": "tests/test_computed_fields.py"
}
{"status": "ok", "filepath": "tests/test_computed_fields.py", "content": "import pytest\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\
full
{"status": "ok", "filepath": "tests/test_computed_fields.py", "content": "import pytest\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\n\nfrom .utils import needs_pydanticv2\n\n\n@pytest.fixture(name=\"client\")\ndef get_client():\n    app = FastAPI()\n\n    from pydantic import BaseModel, computed_field\n\n    class Rectangle(BaseModel):\n        width: int\n        length: int\n\n        @computed_field\n        @property\n        def area(self) -> int:\n            return self.width * self.length\n\n    @app.get(\"/\")\n    def read_root() -> Rectangle:\n        return Rectangle(width=3, length=4)\n\n    @app.get(\"/responses\", responses={200: {\"model\": Rectangle}})\n    def read_responses() -> Rectangle:\n        return Rectangle(width=3, length=4)\n\n    client = TestClient(app)\n    return client\n\n\n@pytest.mark.parametrize(\"path\", [\"/\", \"/responses\"])\n@needs_pydanticv2\ndef test_get(client: TestClient, path: str):\n    response = client.get(path)\n    assert response.status_code == 200, response.text\n    assert response.json() == {\"width\": 3, \"length\": 4, \"area\": 12}\n\n\n@needs_pydanticv2\ndef test_openapi_schema(client: TestClient):\n    response = client.get(\"/openapi.json\")\n    assert response.status_code == 200, response.text\n    assert response.json() == {\n        \"openapi\": \"3.1.0\",\n        \"info\": {\"title\": \"FastAPI\", \"version\": \"0.1.0\"},\n        \"paths\": {\n            \"/\": {\n                \"get\": {\n                    \"summary\": \"Read Root\",\n                    \"operationId\": \"read_root__get\",\n                    \"responses\": {\n                        \"200\": {\n                            \"description\": \"Successful Response\",\n                            \"content\": {\n                                \"application/json\": {\n                                    \"schema\": {\"$ref\": \"#/components/schemas/Rectangle\"}\n                                }\n   
7write_file
{
 "content": "#!/usr/bin/env python3\n\"\"\"\nScript to reproduce the issue with computed fields and separate_input_output_schemas=False\n\"\"\"\n\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\nfrom pydantic import BaseModel, computed_field\n\ndef test_computed_fields_with_separate_input_output_schemas_false():\n    \"\"\"\n    Test that computed fields are included in the OpenAPI schema when separate_input_output_schemas=False\n    \"\"\"\n    app = FastAPI(separate_input_output_schemas=False)\n\n    class Rectangle(BaseModel):\n        width: int\n        length: int\n\n        @computed_field\n        @property\n        def area(self) -> int:\n            return self.width * self.length\n\n    @app.get(\"/\")\n    def read_root() -> Rectangle:\n        return Rectangle(width=3, length=4)\n\n    client = TestClient(app)\n    \n    # Test that the endpoint works\n    response = client.get(\"/\")\n    assert response.status_code == 200\n    assert response.json() == {\"width\": 3, \"length\": 4, \"area\": 12}\n    \n    # Test that the OpenAPI schema includes the computed field\n    openapi_response = client.get(\"/openapi.json\")\n    assert openapi_response.status_code == 200\n    openapi_json = openapi_response.json()\n    \n    # Find the Rectangle schema\n    schemas = openapi_json.get(\"components\", {}).get(\"schemas\", {})\n    rectangle_schema = schemas.get(\"Rectangle\")\n    \n    assert rectangle_schema is not None, \"Rectangle schema should exist\"\n    \n    # Test that the computed field 'area' is present\n    properties = rectangle_schema.get(\"properties\", {})\n    assert \"area\" in properties, \"Computed field 'area' should be present in the OpenAPI schema\"\n    assert properties[\"area\"].get(\"readOnly\") is True, \"Computed field 'area' should be readOnly\"\n\nif __name__ == \"__main__\":\n    try:\n        test_computed_fields_with_separate_input_output_schemas_false()\n        print(\"โœ… Test passed!\")\n    except Exception as e:\n        print(f\"โŒ Test failed: {e}\")\n        exit(1)",
 "filepath": "reproduce_issue.py"
}
{"status": "ok", "filepath": "reproduce_issue.py", "size": 1951}
full
{"status": "ok", "filepath": "reproduce_issue.py", "size": 1951}
8run_command
{
 "command": "python reproduce_issue.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "\u274c Test failed: Computed field 'area' should be present in the OpenAPI schema\n", "detai
full
{"status": "error", "error_type": "CommandError", "error_message": "\u274c Test failed: Computed field 'area' should be present in the OpenAPI schema\n", "details": {"stdout": "\u274c Test failed: Computed field 'area' should be present in the OpenAPI schema\n", "stderr": "", "exit_code": 1}}
9read_file
{
 "end_line": "100",
 "filepath": "tests/test_openapi_separate_input_output_schemas.py",
 "start_line": "1"
}
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
full
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
10run_command
{
 "command": "find . -maxdepth 2 -not -path '*/.*'"
}
{"status": "ok", "stdout": ".\n./reproduce_issue.py\n./pytest.ini\n./LICENSE\n./requirements.txt\n./CITATION.cff\n./pyproject.toml\n./tests\n./tests/test_datast
full
{"status": "ok", "stdout": ".\n./reproduce_issue.py\n./pytest.ini\n./LICENSE\n./requirements.txt\n./CITATION.cff\n./pyproject.toml\n./tests\n./tests/test_datastructures.py\n./tests/test_filter_pydantic_sub_model\n./tests/test_starlette_exception.py\n./tests/test_multi_body_errors.py\n./tests/test_security_http_digest_description.py\n./tests/test_computed_fields.py\n./tests/test_security_oauth2.py\n./tests/test_openapi_schema_type.py\n./tests/test_param_in_path_and_dependency.py\n./tests/test_pydantic_v1_v2_01.py\n./tests/test_security_http_basic_optional.py\n./tests/test_depends_hashable.py\n./tests/test_response_model_invalid.py\n./tests/test_dependency_yield_scope.py\n./tests/test_multi_query_errors.py\n./tests/test_additional_responses_bad.py\n./tests/test_dependency_security_overrides.py\n./tests/test_pydantic_v1_v2_list.py\n./tests/test_response_change_status_code.py\n./tests/test_compat_params_v1.py\n./tests/test_include_route.py\n./tests/test_forms_from_non_typing_sequences.py\n./tests/test_union_forms.py\n./tests/test_security_scopes_dont_propagate.py\n./tests/test_schema_extra_examples.py\n./tests/test_default_response_class_router.py\n./tests/test_invalid_sequence_param.py\n./tests/test_custom_swagger_ui_redirect.py\n./tests/test_response_model_sub_types.py\n./tests/test_params_repr.py\n./tests/test_schema_ref_pydantic_v2.py\n./tests/test_modules_same_name_body\n./tests/test_security_openid_connect_description.py\n./tests/test_inherited_custom_class.py\n./tests/test_union_inherited_body.py\n./tests/test_response_model_as_return_annotation.py\n./tests/test_invalid_path_param.py\n./tests/test_schema_compat_pydantic_v2.py\n./tests/test_param_include_in_schema.py\n./tests/test_dependency_paramless.py\n./tests/test_custom_middleware_exception.py\n./tests/test_get_request_body.py\n./tests/test_dependency_yield_except_httpexception.py\n./tests/test_datetime_custom_encoder.py\n./tests/test_router_prefix_with_template.py\n./tests/test_security_http_base_description
11run_command
{
 "command": "grep -n \"separate_input_output_schemas\" tests/test_openapi_separate_input_output_schemas.py"
}
{"status": "ok", "stdout": "27:def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\n28: app = FastAPI(separate_input_output_schemas=se
full
{"status": "ok", "stdout": "27:def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\n28:    app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)\n55:    client_no = get_app_client(separate_input_output_schemas=False)\n68:    client_no = get_app_client(separate_input_output_schemas=False)\n89:    client_no = get_app_client(separate_input_output_schemas=False)\n116:    client_no = get_app_client(separate_input_output_schemas=False)\n360:    client = get_app_client(separate_input_output_schemas=False)\n", "stderr": "", "exit_code": 0}
12read_file
{
 "end_line": "100",
 "filepath": "tests/test_openapi_separate_input_output_schemas.py",
 "start_line": "1"
}
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
full
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
13run_command
{
 "command": "head -n 100 tests/test_openapi_separate_input_output_schemas.py"
}
{"status": "ok", "stdout": "from typing import List, Optional\n\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\nfrom inline_snapshot im
full
{"status": "ok", "stdout": "from typing import List, Optional\n\nfrom fastapi import FastAPI\nfrom fastapi.testclient import TestClient\nfrom inline_snapshot import snapshot\nfrom pydantic import BaseModel\n\nfrom .utils import PYDANTIC_V2, needs_pydanticv2\n\n\nclass SubItem(BaseModel):\n    subname: str\n    sub_description: Optional[str] = None\n    tags: List[str] = []\n    if PYDANTIC_V2:\n        model_config = {\"json_schema_serialization_defaults_required\": True}\n\n\nclass Item(BaseModel):\n    name: str\n    description: Optional[str] = None\n    sub: Optional[SubItem] = None\n    if PYDANTIC_V2:\n        model_config = {\"json_schema_serialization_defaults_required\": True}\n\n\ndef get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\n    app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)\n\n    @app.post(\"/items/\", responses={402: {\"model\": Item}})\n    def create_item(item: Item) -> Item:\n        return item\n\n    @app.post(\"/items-list/\")\n    def create_item_list(item: List[Item]):\n        return item\n\n    @app.get(\"/items/\")\n    def read_items() -> List[Item]:\n        return [\n            Item(\n                name=\"Portal Gun\",\n                description=\"Device to travel through the multi-rick-verse\",\n                sub=SubItem(subname=\"subname\"),\n            ),\n            Item(name=\"Plumbus\"),\n        ]\n\n    client = TestClient(app)\n    return client\n\n\ndef test_create_item():\n    client = get_app_client()\n    client_no = get_app_client(separate_input_output_schemas=False)\n    response = client.post(\"/items/\", json={\"name\": \"Plumbus\"})\n    response2 = client_no.post(\"/items/\", json={\"name\": \"Plumbus\"})\n    assert response.status_code == response2.status_code == 200, response.text\n    assert (\n        response.json()\n        == response2.json()\n        == {\"name\": \"Plumbus\", \"description\": None, \"sub\": None}\n    )\n\n\ndef test_create_
14run_command
{
 "command": "grep -r \"computed_field\" . --include=\"*.py\""
}
{"status": "ok", "stdout": "./reproduce_issue.py:from pydantic import BaseModel, computed_field\n./reproduce_issue.py:def test_computed_fields_with_separate_inp
full
{"status": "ok", "stdout": "./reproduce_issue.py:from pydantic import BaseModel, computed_field\n./reproduce_issue.py:def test_computed_fields_with_separate_input_output_schemas_false():\n./reproduce_issue.py:        @computed_field\n./reproduce_issue.py:        test_computed_fields_with_separate_input_output_schemas_false()\n./tests/test_computed_fields.py:    from pydantic import BaseModel, computed_field\n./tests/test_computed_fields.py:        @computed_field\n", "stderr": "", "exit_code": 0}
15run_command
{
 "command": "grep -r \"separate_input_output_schemas\" . --include=\"*.py\""
}
{"status": "ok", "stdout": "./reproduce_issue.py:Script to reproduce the issue with computed fields and separate_input_output_schemas=False\n./reproduce_issue.p
full
{"status": "ok", "stdout": "./reproduce_issue.py:Script to reproduce the issue with computed fields and separate_input_output_schemas=False\n./reproduce_issue.py:def test_computed_fields_with_separate_input_output_schemas_false():\n./reproduce_issue.py:    Test that computed fields are included in the OpenAPI schema when separate_input_output_schemas=False\n./reproduce_issue.py:    app = FastAPI(separate_input_output_schemas=False)\n./reproduce_issue.py:        test_computed_fields_with_separate_input_output_schemas_false()\n./tests/test_openapi_separate_input_output_schemas.py:def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:\n./tests/test_openapi_separate_input_output_schemas.py:    app = FastAPI(separate_input_output_schemas=separate_input_output_schemas)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client_no = get_app_client(separate_input_output_schemas=False)\n./tests/test_openapi_separate_input_output_schemas.py:    client = get_app_client(separate_input_output_schemas=False)\n./fastapi/applications.py:        separate_input_output_schemas: Annotated[\n./fastapi/applications.py:        self.separate_input_output_schemas = separate_input_output_schemas\n./fastapi/applications.py:                separate_input_output_schemas=self.separate_input_output_schemas,\n./fastapi/openapi/utils.py:    separate_input_output_schemas: bool = True,\n./fastapi/openapi/utils.py:                separate_input_output_schemas=separate_input_output_schemas,\n./fastapi/openapi/utils.py:    separate_input_output_schemas: bool = True,\n./fastapi/openapi/utils.py:        separate_i
16read_file
{
 "end_line": "100",
 "filepath": "fastapi/openapi/utils.py",
 "start_line": "1"
}
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
full
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
17run_command
{
 "command": "head -n 100 fastapi/openapi/utils.py"
}
{"status": "ok", "stdout": "import http.client\nimport inspect\nimport warnings\nfrom typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
full
{"status": "ok", "stdout": "import http.client\nimport inspect\nimport warnings\nfrom typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast\n\nfrom fastapi import routing\nfrom fastapi._compat import (\n    JsonSchemaValue,\n    ModelField,\n    Undefined,\n    get_compat_model_name_map,\n    get_definitions,\n    get_schema_from_model_field,\n    lenient_issubclass,\n)\nfrom fastapi.datastructures import DefaultPlaceholder\nfrom fastapi.dependencies.models import Dependant\nfrom fastapi.dependencies.utils import (\n    _get_flat_fields_from_params,\n    get_flat_dependant,\n    get_flat_params,\n)\nfrom fastapi.encoders import jsonable_encoder\nfrom fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX\nfrom fastapi.openapi.models import OpenAPI\nfrom fastapi.params import Body, ParamTypes\nfrom fastapi.responses import Response\nfrom fastapi.types import ModelNameMap\nfrom fastapi.utils import (\n    deep_dict_update,\n    generate_operation_id_for_path,\n    is_body_allowed_for_status_code,\n)\nfrom pydantic import BaseModel\nfrom starlette.responses import JSONResponse\nfrom starlette.routing import BaseRoute\nfrom typing_extensions import Literal\n\nfrom .._compat import _is_model_field\n\nvalidation_error_definition = {\n    \"title\": \"ValidationError\",\n    \"type\": \"object\",\n    \"properties\": {\n        \"loc\": {\n            \"title\": \"Location\",\n            \"type\": \"array\",\n            \"items\": {\"anyOf\": [{\"type\": \"string\"}, {\"type\": \"integer\"}]},\n        },\n        \"msg\": {\"title\": \"Message\", \"type\": \"string\"},\n        \"type\": {\"title\": \"Error Type\", \"type\": \"string\"},\n    },\n    \"required\": [\"loc\", \"msg\", \"type\"],\n}\n\nvalidation_error_response_definition = {\n    \"title\": \"HTTPValidationError\",\n    \"type\": \"object\",\n    \"properties\": {\n        \"detail\": {\n            \"title\": \"Detail\",\n            \"type\": \"array\",\n            \"
18run_command
{
 "command": "grep -n \"separate_input_output_schemas\" fastapi/openapi/utils.py"
}
{"status": "ok", "stdout": "102: separate_input_output_schemas: bool = True,\n133: separate_input_output_schemas=separate_input_output_schemas,\n175: separate_i
full
{"status": "ok", "stdout": "102:    separate_input_output_schemas: bool = True,\n133:                separate_input_output_schemas=separate_input_output_schemas,\n175:    separate_input_output_schemas: bool = True,\n184:        separate_input_output_schemas=separate_input_output_schemas,\n258:    separate_input_output_schemas: bool = True,\n288:                separate_input_output_schemas=separate_input_output_schemas,\n309:                    separate_input_output_schemas=separate_input_output_schemas,\n326:                            separate_input_output_schemas=separate_input_output_schemas,\n356:                            separate_input_output_schemas=separate_input_output_schemas,\n389:                            separate_input_output_schemas=separate_input_output_schemas,\n481:    separate_input_output_schemas: bool = True,\n507:        separate_input_output_schemas=separate_input_output_schemas,\n516:                separate_input_output_schemas=separate_input_output_schemas,\n535:                separate_input_output_schemas=separate_input_output_schemas,\n", "stderr": "", "exit_code": 0}
19read_file
{
 "end_line": "150",
 "filepath": "fastapi/openapi/utils.py",
 "start_line": "100"
}
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
full
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
20run_command
{
 "command": "sed -n '100,150p' fastapi/openapi/utils.py"
}
{"status": "ok", "stdout": " Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n ],\n separate_input_output_schemas: bool = True,\n
full
{"status": "ok", "stdout": "        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\n    separate_input_output_schemas: bool = True,\n) -> List[Dict[str, Any]]:\n    parameters = []\n    flat_dependant = get_flat_dependant(dependant, skip_repeats=True)\n    path_params = _get_flat_fields_from_params(flat_dependant.path_params)\n    query_params = _get_flat_fields_from_params(flat_dependant.query_params)\n    header_params = _get_flat_fields_from_params(flat_dependant.header_params)\n    cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)\n    parameter_groups = [\n        (ParamTypes.path, path_params),\n        (ParamTypes.query, query_params),\n        (ParamTypes.header, header_params),\n        (ParamTypes.cookie, cookie_params),\n    ]\n    default_convert_underscores = True\n    if len(flat_dependant.header_params) == 1:\n        first_field = flat_dependant.header_params[0]\n        if lenient_issubclass(first_field.type_, BaseModel):\n            default_convert_underscores = getattr(\n                first_field.field_info, \"convert_underscores\", True\n            )\n    for param_type, param_group in parameter_groups:\n        for param in param_group:\n            field_info = param.field_info\n            # field_info = cast(Param, field_info)\n            if not getattr(field_info, \"include_in_schema\", True):\n                continue\n            param_schema = get_schema_from_model_field(\n                field=param,\n                model_name_map=model_name_map,\n                field_mapping=field_mapping,\n                separate_input_output_schemas=separate_input_output_schemas,\n            )\n            name = param.alias\n            convert_underscores = getattr(\n                param.field_info,\n                \"convert_underscores\",\n                default_convert_underscores,\n            )\n            if (\n                param_type == ParamTypes.header\n        
21run_command
{
 "command": "sed -n '170,210p' fastapi/openapi/utils.py"
}
{"status": "ok", "stdout": " body_field: Optional[ModelField],\n model_name_map: ModelNameMap,\n field_mapping: Dict[\n Tuple[ModelField, Literal[\"validation\"
full
{"status": "ok", "stdout": "    body_field: Optional[ModelField],\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\n    separate_input_output_schemas: bool = True,\n) -> Optional[Dict[str, Any]]:\n    if not body_field:\n        return None\n    assert _is_model_field(body_field)\n    body_schema = get_schema_from_model_field(\n        field=body_field,\n        model_name_map=model_name_map,\n        field_mapping=field_mapping,\n        separate_input_output_schemas=separate_input_output_schemas,\n    )\n    field_info = cast(Body, body_field.field_info)\n    request_media_type = field_info.media_type\n    required = body_field.required\n    request_body_oai: Dict[str, Any] = {}\n    if required:\n        request_body_oai[\"required\"] = required\n    request_media_content: Dict[str, Any] = {\"schema\": body_schema}\n    if field_info.openapi_examples:\n        request_media_content[\"examples\"] = jsonable_encoder(\n            field_info.openapi_examples\n        )\n    elif field_info.example != Undefined:\n        request_media_content[\"example\"] = jsonable_encoder(field_info.example)\n    request_body_oai[\"content\"] = {request_media_type: request_media_content}\n    return request_body_oai\n\n\ndef generate_operation_id(\n    *, route: routing.APIRoute, method: str\n) -> str:  # pragma: nocover\n    warnings.warn(\n        \"fastapi.openapi.utils.generate_operation_id() was deprecated, \"\n        \"it is not used internally, and will be removed soon\",\n        DeprecationWarning,\n        stacklevel=2,\n", "stderr": "", "exit_code": 0}
22run_command
{
 "command": "sed -n '250,300p' fastapi/openapi/utils.py"
}
{"status": "ok", "stdout": "def get_openapi_path(\n *,\n route: routing.APIRoute,\n operation_ids: Set[str],\n model_name_map: ModelNameMap,\n field_mapping: Di
full
{"status": "ok", "stdout": "def get_openapi_path(\n    *,\n    route: routing.APIRoute,\n    operation_ids: Set[str],\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\n    separate_input_output_schemas: bool = True,\n) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:\n    path = {}\n    security_schemes: Dict[str, Any] = {}\n    definitions: Dict[str, Any] = {}\n    assert route.methods is not None, \"Methods must be a list\"\n    if isinstance(route.response_class, DefaultPlaceholder):\n        current_response_class: Type[Response] = route.response_class.value\n    else:\n        current_response_class = route.response_class\n    assert current_response_class, \"A response class is needed to generate OpenAPI\"\n    route_response_media_type: Optional[str] = current_response_class.media_type\n    if route.include_in_schema:\n        for method in route.methods:\n            operation = get_openapi_operation_metadata(\n                route=route, method=method, operation_ids=operation_ids\n            )\n            parameters: List[Dict[str, Any]] = []\n            flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)\n            security_definitions, operation_security = get_openapi_security_definitions(\n                flat_dependant=flat_dependant\n            )\n            if operation_security:\n                operation.setdefault(\"security\", []).extend(operation_security)\n            if security_definitions:\n                security_schemes.update(security_definitions)\n            operation_parameters = _get_openapi_operation_parameters(\n                dependant=route.dependant,\n                model_name_map=model_name_map,\n                field_mapping=field_mapping,\n                separate_input_output_schemas=separate_input_output_schemas,\n            )\n            parameters.extend(operation_parameters)\
23run_command
{
 "command": "grep -n \"separate_input_output_schemas\" fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": "181: separate_input_output_schemas: bool = True,\n184: None if separate_input_output_schemas else \"validation\"\n201: separate_inpu
full
{"status": "ok", "stdout": "181:    separate_input_output_schemas: bool = True,\n184:        None if separate_input_output_schemas else \"validation\"\n201:    separate_input_output_schemas: bool = True,\n208:        None if separate_input_output_schemas else \"validation\"\n", "stderr": "", "exit_code": 0}
24read_file
{
 "end_line": "215",
 "filepath": "fastapi/_compat/v2.py",
 "start_line": "170"
}
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
full
{"status": "error", "error_type": "FileReadError", "error_message": "'>' not supported between instances of 'int' and 'str'"}
25run_command
{
 "command": "sed -n '170,215p' fastapi/_compat/v2.py"
}
{"status": "ok", "stdout": "def _get_model_config(model: BaseModel) -> Any:\n return model.model_config\n\n\ndef get_schema_from_model_field(\n *,\n field: Mode
full
{"status": "ok", "stdout": "def _get_model_config(model: BaseModel) -> Any:\n    return model.model_config\n\n\ndef get_schema_from_model_field(\n    *,\n    field: ModelField,\n    model_name_map: ModelNameMap,\n    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    # This expects that GenerateJsonSchema was already used to generate the definitions\n    json_schema = field_mapping[(field, override_mode or field.mode)]\n    if \"$ref\" not in json_schema:\n        # TODO remove when deprecating Pydantic v1\n        # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207\n        json_schema[\"title\"] = field.field_info.title or field.alias.title().replace(\n            \"_\", \" \"\n        )\n    return json_schema\n\n\ndef get_definitions(\n    *,\n    fields: Sequence[ModelField],\n    model_name_map: ModelNameMap,\n    separate_input_output_schemas: bool = True,\n) -> Tuple[\n    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Dict[str, Any]],\n]:\n    schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )\n    validation_fields = [field for field in fields if field.mode == \"validation\"]\n    serialization_fields = [field for field in fields if field.mode == \"serialization\"]\n    flat_validation_models = get_flat_models_from_fields(\n        validation_fields, known_models=set()\n    )\n    flat_serialization_models = get_flat_models_from_fields(\n", "stderr": "", "exit_code": 0}
26run_command
{
 "command": "grep -n \"computed_field\" fastapi/_compat/v2.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
27run_command
{
 "command": "grep -n \"computed\" fastapi/_compat/v2.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
28run_command
{
 "command": "grep -n \"computed\" fastapi/_compat/main.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
29run_command
{
 "command": "grep -n \"computed\" fastapi/_compat/v1.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
30run_command
{
 "command": "grep -n \"computed\" fastapi/_compat/may_v1.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
31run_command
{
 "command": "grep -n \"computed\" fastapi/openapi/utils.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
32run_command
{
 "command": "grep -n \"computed\" fastapi/applications.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}
full
{"status": "error", "error_type": "CommandError", "error_message": "", "details": {"stdout": "", "stderr": "", "exit_code": 1}}

Patch

diff --git a/reproduce_issue.py b/reproduce_issue.py
new file mode 100644
index 00000000..b7247815
--- /dev/null
+++ b/reproduce_issue.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+"""
+Script to reproduce the issue with computed fields and separate_input_output_schemas=False
+"""
+
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+from pydantic import BaseModel, computed_field
+
+def test_computed_fields_with_separate_input_output_schemas_false():
+    """
+    Test that computed fields are included in the OpenAPI schema when separate_input_output_schemas=False
+    """
+    app = FastAPI(separate_input_output_schemas=False)
+
+    class Rectangle(BaseModel):
+        width: int
+        length: int
+
+        @computed_field
+        @property
+        def area(self) -> int:
+            return self.width * self.length
+
+    @app.get("/")
+    def read_root() -> Rectangle:
+        return Rectangle(width=3, length=4)
+
+    client = TestClient(app)
+    
+    # Test that the endpoint works
+    response = client.get("/")
+    assert response.status_code == 200
+    assert response.json() == {"width": 3, "length": 4, "area": 12}
+    
+    # Test that the OpenAPI schema includes the computed field
+    openapi_response = client.get("/openapi.json")
+    assert openapi_response.status_code == 200
+    openapi_json = openapi_response.json()
+    
+    # Find the Rectangle schema
+    schemas = openapi_json.get("components", {}).get("schemas", {})
+    rectangle_schema = schemas.get("Rectangle")
+    
+    assert rectangle_schema is not None, "Rectangle schema should exist"
+    
+    # Test that the computed field 'area' is present
+    properties = rectangle_schema.get("properties", {})
+    assert "area" in properties, "Computed field 'area' should be present in the OpenAPI schema"
+    assert properties["area"].get("readOnly") is True, "Computed field 'area' should be readOnly"
+
+if __name__ == "__main__":
+    try:
+        test_computed_fields_with_separate_input_output_schemas_false()
+        print("โœ… Test passed!")
+    except Exception as e:
+        print(f"โŒ Test failed: {e}")
+        exit(1)
\ No newline at end of file

Test output

show
.....F
=================================== FAILURES ===================================
__________________________ test_openapi_schema[False] __________________________

client = <starlette.testclient.TestClient object at 0x1035c8dd0>

    @pytest.mark.parametrize("client", [True, False], indirect=True)
    @needs_pydanticv2
    def test_openapi_schema(client: TestClient):
        response = client.get("/openapi.json")
        assert response.status_code == 200, response.text
>       assert response.json() == {
            "openapi": "3.1.0",
            "info": {"title": "FastAPI", "version": "0.1.0"},
            "paths": {
                "/": {
                    "get": {
                        "summary": "Read Root",
                        "operationId": "read_root__get",
                        "responses": {
                            "200": {
                                "description": "Successful Response",
                                "content": {
                                    "application/json": {
                                        "schema": {"$ref": "#/components/schemas/Rectangle"}
                                    }
                                },
                            }
                        },
                    }
                },
                "/responses": {
                    "get": {
                        "summary": "Read Responses",
                        "operationId": "read_responses_responses_get",
                        "responses": {
                            "200": {
                                "description": "Successful Response",
                                "content": {
                                    "application/json": {
                                        "schema": {"$ref": "#/components/schemas/Rectangle"}
                                    }
                                },
                            }
                        },
                    }
                },
            },
            "components": {
                "schemas": {
                    "Rectangle": {
                        "properties": {
                            "width": {"type": "integer", "title": "Width"},
                            "length": {"type": "integer", "title": "Length"},
                            "area": {"type": "integer", "title": "Area", "readOnly": True},
                        },
                        "type": "object",
                        "required": ["width", "length", "area"],
                        "title": "Rectangle",
                    }
                }
            },
        }
E       AssertionError: assert {'openapi': '...Rectangle'}}}} == {'openapi': '...Rectangle'}}}}
E         
E         Omitting 3 identical items, use -vv to show
E         Differing items:
E         {'components': {'schemas': {'Rectangle': {'properties': {'width': {'type': 'integer', 'title': 'Width'}, 'length': {'type': 'integer', 'title': 'Length'}}, 'type': 'object', 'required': ['width', 'length'], 'title': 'Rectangle'}}}} != {'components': {'schemas': {'Rectangle': {'properties': {'width': {'type': 'integer', 'title': 'Width'}, 'length': {'t...title': 'Area', 'readOnly': True}}, 'type': 'object', 'required': ['width', 'length', 'area'], 'title': 'Rectangle'}}}}
E         Use -v to get more diff

tests/test_computed_fields.py:50: AssertionError
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
  /Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
    _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 5 passed, 1 warning in 0.46s