← or20_submission

fastapi_13207

resolved RESOLVED PASS · 38 tool calls · 82 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 (39)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"separate_input_output_schemas\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/applications.py:791: separate_input_output_schemas: Annotated[\nfastapi/applications.py:875: self.separate_input_output_sche
full
{"status": "ok", "stdout": "fastapi/applications.py:791:        separate_input_output_schemas: Annotated[\nfastapi/applications.py:875:        self.separate_input_output_schemas = separate_input_output_schemas\nfastapi/applications.py:1078:                separate_input_output_schemas=self.separate_input_output_schemas,\nfastapi/openapi/utils.py:102:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:133:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:175:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:184:        separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:258:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:288:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:309:                    separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:326:                            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:356:                            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:389:                            separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:481:    separate_input_output_schemas: bool = True,\nfastapi/openapi/utils.py:507:        separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:516:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/openapi/utils.py:535:                separate_input_output_schemas=separate_input_output_schemas,\nfastapi/_compat/v1.py:243:    separate_input_output_schemas: bool = True,\nfastapi/_compat/v1.py:259:    separate_input_output_schemas: bool = True,\nfastapi/_compat/may_v1.py:65:        separate_input_output_schemas: bool = True,\n", "exit_code": 0}
2read_file
{
 "filepath": "fastapi/openapi/utils.py",
 "start_line": 100,
 "end_line": 300
}
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": " Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n ],\n sepa
full
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "        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                
3read_file
{
 "end_line": 450,
 "filepath": "fastapi/openapi/utils.py",
 "start_line": 250
}
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "def get_openapi_path(\n *,\n route: routing.APIRoute,\n operation_ids: Set[str],\n model_na
full
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "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           
4read_file
{
 "end_line": 558,
 "filepath": "fastapi/openapi/utils.py"
}
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "import http.client\nimport inspect\nimport warnings\nfrom typing import Any, Dict, List, Op
full
{"status": "ok", "filepath": "fastapi/openapi/utils.py", "content": "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       
5read_file
{
 "filepath": "fastapi/_compat/v1.py",
 "start_line": 240,
 "end_line": 300
}
{"status": "ok", "filepath": "fastapi/_compat/v1.py", "content": " field_mapping: Dict[\n Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSch
full
{"status": "ok", "filepath": "fastapi/_compat/v1.py", "content": "    field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    return field_schema(  # type: ignore[no-any-return]\n        field, model_name_map=model_name_map, ref_prefix=REF_PREFIX\n    )[0]\n\n\n# def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:\n#     models = get_flat_models_from_fields(fields, known_models=set())\n#     return get_model_name_map(models)  # type: ignore[no-any-return]\n\n\ndef get_definitions(\n    *,\n    fields: List[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    models = get_flat_models_from_fields(fields, known_models=set())\n    return {}, get_model_definitions(flat_models=models, model_name_map=model_name_map)\n\n\ndef is_scalar_field(field: ModelField) -> bool:\n    return is_pv1_scalar_field(field)\n\n\ndef is_sequence_field(field: ModelField) -> bool:\n    return field.shape in sequence_shapes or shared._annotation_is_sequence(field.type_)\n\n\ndef is_scalar_sequence_field(field: ModelField) -> bool:\n    return is_pv1_scalar_sequence_field(field)\n\n\ndef is_bytes_field(field: ModelField) -> bool:\n    return lenient_issubclass(field.type_, bytes)  # type: ignore[no-any-return]\n\n\ndef is_bytes_sequence_field(field: ModelField) -> bool:\n    return field.shape in sequence_shapes and lenient_issubclass(field.type_, bytes)\n\n\ndef copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:\n    return copy(field_info)\n\n\ndef serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:\n    return sequence_shape_to_type[field.shape](value)  # type: ignore[no-any-return]\n\n\ndef get_missing_fi
6read_file
{
 "end_line": 200,
 "filepath": "fastapi/_compat/v1.py",
 "start_line": 1
}
{"status": "ok", "filepath": "fastapi/_compat/v1.py", "content": "from copy import copy\nfrom dataclasses import dataclass, is_dataclass\nfrom enum import Enum\
full
{"status": "ok", "filepath": "fastapi/_compat/v1.py", "content": "from copy import copy\nfrom dataclasses import dataclass, is_dataclass\nfrom enum import Enum\nfrom typing import (\n    Any,\n    Callable,\n    Dict,\n    List,\n    Sequence,\n    Set,\n    Tuple,\n    Type,\n    Union,\n)\n\nfrom fastapi._compat import shared\nfrom fastapi.openapi.constants import REF_PREFIX as REF_PREFIX\nfrom fastapi.types import ModelNameMap\nfrom pydantic.version import VERSION as PYDANTIC_VERSION\nfrom typing_extensions import Literal\n\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2\n# Keeping old \"Required\" functionality from Pydantic V1, without\n# shadowing typing.Required.\nRequiredParam: Any = Ellipsis\n\nif not PYDANTIC_V2:\n    from pydantic import BaseConfig as BaseConfig\n    from pydantic import BaseModel as BaseModel\n    from pydantic import ValidationError as ValidationError\n    from pydantic import create_model as create_model\n    from pydantic.class_validators import Validator as Validator\n    from pydantic.color import Color as Color\n    from pydantic.error_wrappers import ErrorWrapper as ErrorWrapper\n    from pydantic.errors import MissingError\n    from pydantic.fields import (  # type: ignore[attr-defined]\n        SHAPE_FROZENSET,\n        SHAPE_LIST,\n        SHAPE_SEQUENCE,\n        SHAPE_SET,\n        SHAPE_SINGLETON,\n        SHAPE_TUPLE,\n        SHAPE_TUPLE_ELLIPSIS,\n    )\n    from pydantic.fields import FieldInfo as FieldInfo\n    from pydantic.fields import ModelField as ModelField  # type: ignore[attr-defined]\n    from pydantic.fields import Undefined as Undefined  # type: ignore[attr-defined]\n    from pydantic.fields import (  # type: ignore[attr-defined]\n        UndefinedType as UndefinedType,\n    )\n    from pydantic.networks import AnyUrl as AnyUrl\n    from pydantic.networks import NameEmail as NameEmail\n    from pydantic.schema import 
7read_file
{
 "end_line": 150,
 "filepath": "fastapi/_compat/__init__.py",
 "start_line": 1
}
{"status": "ok", "filepath": "fastapi/_compat/__init__.py", "content": "from .main import BaseConfig as BaseConfig\nfrom .main import PydanticSchemaGenerationEr
full
{"status": "ok", "filepath": "fastapi/_compat/__init__.py", "content": "from .main import BaseConfig as BaseConfig\nfrom .main import PydanticSchemaGenerationError as PydanticSchemaGenerationError\nfrom .main import RequiredParam as RequiredParam\nfrom .main import Undefined as Undefined\nfrom .main import UndefinedType as UndefinedType\nfrom .main import Url as Url\nfrom .main import Validator as Validator\nfrom .main import _get_model_config as _get_model_config\nfrom .main import _is_error_wrapper as _is_error_wrapper\nfrom .main import _is_model_class as _is_model_class\nfrom .main import _is_model_field as _is_model_field\nfrom .main import _is_undefined as _is_undefined\nfrom .main import _model_dump as _model_dump\nfrom .main import _model_rebuild as _model_rebuild\nfrom .main import copy_field_info as copy_field_info\nfrom .main import create_body_model as create_body_model\nfrom .main import evaluate_forwardref as evaluate_forwardref\nfrom .main import get_annotation_from_field_info as get_annotation_from_field_info\nfrom .main import get_cached_model_fields as get_cached_model_fields\nfrom .main import get_compat_model_name_map as get_compat_model_name_map\nfrom .main import get_definitions as get_definitions\nfrom .main import get_missing_field_error as get_missing_field_error\nfrom .main import get_schema_from_model_field as get_schema_from_model_field\nfrom .main import is_bytes_field as is_bytes_field\nfrom .main import is_bytes_sequence_field as is_bytes_sequence_field\nfrom .main import is_scalar_field as is_scalar_field\nfrom .main import is_scalar_sequence_field as is_scalar_sequence_field\nfrom .main import is_sequence_field as is_sequence_field\nfrom .main import serialize_sequence_value as serialize_sequence_value\nfrom .main import (\n    with_info_plain_validator_function as with_info_plain_validator_function,\n)\nfrom .may_v1 import CoreSchema as CoreSchema\nfrom .may_v1 import GetJsonSchemaHandler as GetJsonSchemaHandler\nfrom .may_v1 import
8read_file
{
 "end_line": 300,
 "filepath": "fastapi/_compat/main.py",
 "start_line": 1
}
{"status": "ok", "filepath": "fastapi/_compat/main.py", "content": "import sys\nfrom functools import lru_cache\nfrom typing import (\n Any,\n Dict,\n List,\n S
full
{"status": "ok", "filepath": "fastapi/_compat/main.py", "content": "import sys\nfrom functools import lru_cache\nfrom typing import (\n    Any,\n    Dict,\n    List,\n    Sequence,\n    Tuple,\n    Type,\n)\n\nfrom fastapi._compat import may_v1\nfrom fastapi._compat.shared import PYDANTIC_V2, lenient_issubclass\nfrom fastapi.types import ModelNameMap\nfrom pydantic import BaseModel\nfrom typing_extensions import Literal\n\nfrom .model_field import ModelField\n\nif PYDANTIC_V2:\n    from .v2 import BaseConfig as BaseConfig\n    from .v2 import FieldInfo as FieldInfo\n    from .v2 import PydanticSchemaGenerationError as PydanticSchemaGenerationError\n    from .v2 import RequiredParam as RequiredParam\n    from .v2 import Undefined as Undefined\n    from .v2 import UndefinedType as UndefinedType\n    from .v2 import Url as Url\n    from .v2 import Validator as Validator\n    from .v2 import evaluate_forwardref as evaluate_forwardref\n    from .v2 import get_missing_field_error as get_missing_field_error\n    from .v2 import (\n        with_info_plain_validator_function as with_info_plain_validator_function,\n    )\nelse:\n    from .v1 import BaseConfig as BaseConfig  # type: ignore[assignment]\n    from .v1 import FieldInfo as FieldInfo\n    from .v1 import (  # type: ignore[assignment]\n        PydanticSchemaGenerationError as PydanticSchemaGenerationError,\n    )\n    from .v1 import RequiredParam as RequiredParam\n    from .v1 import Undefined as Undefined\n    from .v1 import UndefinedType as UndefinedType\n    from .v1 import Url as Url  # type: ignore[assignment]\n    from .v1 import Validator as Validator\n    from .v1 import evaluate_forwardref as evaluate_forwardref\n    from .v1 import get_missing_field_error as get_missing_field_error\n    from .v1 import (  # type: ignore[assignment]\n        with_info_plain_validator_function as with_info_plain_validator_function,\n    )\n\n\n@lru_cache\ndef get_cached_model_fields(model: Type[BaseModel]) -> List[ModelFiel
9read_file
{
 "end_line": 362,
 "filepath": "fastapi/_compat/main.py",
 "start_line": 151
}
{"status": "ok", "filepath": "fastapi/_compat/main.py", "content": "\ndef is_bytes_field(field: ModelField) -> bool:\n if isinstance(field, may_v1.ModelField):\
full
{"status": "ok", "filepath": "fastapi/_compat/main.py", "content": "\ndef is_bytes_field(field: ModelField) -> bool:\n    if isinstance(field, may_v1.ModelField):\n        from fastapi._compat import v1\n\n        return v1.is_bytes_field(field)\n    else:\n        assert PYDANTIC_V2\n        from . import v2\n\n        return v2.is_bytes_field(field)  # type: ignore[arg-type]\n\n\ndef is_bytes_sequence_field(field: ModelField) -> bool:\n    if isinstance(field, may_v1.ModelField):\n        from fastapi._compat import v1\n\n        return v1.is_bytes_sequence_field(field)\n    else:\n        assert PYDANTIC_V2\n        from . import v2\n\n        return v2.is_bytes_sequence_field(field)  # type: ignore[arg-type]\n\n\ndef is_scalar_field(field: ModelField) -> bool:\n    if isinstance(field, may_v1.ModelField):\n        from fastapi._compat import v1\n\n        return v1.is_scalar_field(field)\n    else:\n        assert PYDANTIC_V2\n        from . import v2\n\n        return v2.is_scalar_field(field)  # type: ignore[arg-type]\n\n\ndef is_scalar_sequence_field(field: ModelField) -> bool:\n    if isinstance(field, may_v1.ModelField):\n        from fastapi._compat import v1\n\n        return v1.is_scalar_sequence_field(field)\n    else:\n        assert PYDANTIC_V2\n        from . import v2\n\n        return v2.is_scalar_sequence_field(field)  # type: ignore[arg-type]\n\n\ndef is_sequence_field(field: ModelField) -> bool:\n    if isinstance(field, may_v1.ModelField):\n        from fastapi._compat import v1\n\n        return v1.is_sequence_field(field)\n    else:\n        assert PYDANTIC_V2\n        from . import v2\n\n        return v2.is_sequence_field(field)  # type: ignore[arg-type]\n\n\ndef serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:\n    if isinstance(field, may_v1.ModelField):\n        from fastapi._compat import v1\n\n        return v1.serialize_sequence_value(field=field, value=value)\n    else:\n        assert PYDANTIC_V2\n       
10read_file
{
 "end_line": 362,
 "filepath": "fastapi/_compat/main.py",
 "start_line": 301
}
{"status": "ok", "filepath": "fastapi/_compat/main.py", "content": " # Pydantic v1 is not supported since Python 3.14\n else:\n from . import v2\n\n v2_fields =
full
{"status": "ok", "filepath": "fastapi/_compat/main.py", "content": "    # Pydantic v1 is not supported since Python 3.14\n    else:\n        from . import v2\n\n        v2_fields = [field for field in fields if isinstance(field, v2.ModelField)]\n        v2_field_maps, v2_definitions = v2.get_definitions(\n            fields=v2_fields,\n            model_name_map=model_name_map,\n            separate_input_output_schemas=separate_input_output_schemas,\n        )\n        return v2_field_maps, v2_definitions\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\"]],\n        may_v1.JsonSchemaValue,\n    ],\n    separate_input_output_schemas: bool = True,\n) -> Dict[str, Any]:\n    if isinstance(field, may_v1.ModelField):\n        from fastapi._compat import v1\n\n        return v1.get_schema_from_model_field(\n            field=field,\n            model_name_map=model_name_map,\n            field_mapping=field_mapping,\n            separate_input_output_schemas=separate_input_output_schemas,\n        )\n    else:\n        assert PYDANTIC_V2\n        from . import v2\n\n        return v2.get_schema_from_model_field(\n            field=field,  # type: ignore[arg-type]\n            model_name_map=model_name_map,\n            field_mapping=field_mapping,  # type: ignore[arg-type]\n            separate_input_output_schemas=separate_input_output_schemas,\n        )\n\n\ndef _is_model_field(value: Any) -> bool:\n    if isinstance(value, may_v1.ModelField):\n        return True\n    elif PYDANTIC_V2:\n        from . import v2\n\n        return isinstance(value, v2.ModelField)\n    return False\n\n\ndef _is_model_class(value: Any) -> bool:\n    if lenient_issubclass(value, may_v1.BaseModel):\n        return True\n    elif PYDANTIC_V2:\n        from . import v2\n\n        return lenient_issubclass(value, v2.BaseModel)  # type: ignore[at
11read_file
{
 "end_line": 200,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 1
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "import re\nimport warnings\nfrom copy import copy, deepcopy\nfrom dataclasses import dataclass
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "import re\nimport warnings\nfrom copy import copy, deepcopy\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom typing import (\n    Any,\n    Dict,\n    List,\n    Sequence,\n    Set,\n    Tuple,\n    Type,\n    Union,\n    cast,\n)\n\nfrom fastapi._compat import may_v1, shared\nfrom fastapi.openapi.constants import REF_TEMPLATE\nfrom fastapi.types import IncEx, ModelNameMap\nfrom pydantic import BaseModel, TypeAdapter, create_model\nfrom pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError\nfrom pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation\nfrom pydantic import ValidationError as ValidationError\nfrom pydantic._internal._schema_generation_shared import (  # type: ignore[attr-defined]\n    GetJsonSchemaHandler as GetJsonSchemaHandler,\n)\nfrom pydantic._internal._typing_extra import eval_type_lenient\nfrom pydantic._internal._utils import lenient_issubclass as lenient_issubclass\nfrom pydantic.fields import FieldInfo as FieldInfo\nfrom pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema\nfrom pydantic.json_schema import JsonSchemaValue as JsonSchemaValue\nfrom pydantic_core import CoreSchema as CoreSchema\nfrom pydantic_core import PydanticUndefined, PydanticUndefinedType\nfrom pydantic_core import Url as Url\nfrom typing_extensions import Annotated, Literal, get_args, get_origin\n\ntry:\n    from pydantic_core.core_schema import (\n        with_info_plain_validator_function as with_info_plain_validator_function,\n    )\nexcept ImportError:  # pragma: no cover\n    from pydantic_core.core_schema import (\n        general_plain_validator_function as with_info_plain_validator_function,  # noqa: F401\n    )\n\nRequiredParam = PydanticUndefined\nUndefined = PydanticUndefined\nUndefinedType = PydanticUndefinedType\nevaluate_forwardref = eval_type_lenient\nValidator = Any\n\n\nclass BaseConfig:\n    pass\n\n\nclass ErrorWr
12read_file
{
 "end_line": 486,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 151
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " return id(self)\n\n\ndef get_annotation_from_field_info(\n annotation: Any, field_info: Field
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "        return id(self)\n\n\ndef get_annotation_from_field_info(\n    annotation: Any, field_info: FieldInfo, field_name: str\n) -> Any:\n    return annotation\n\n\ndef _model_rebuild(model: Type[BaseModel]) -> None:\n    model.model_rebuild()\n\n\ndef _model_dump(\n    model: BaseModel, mode: Literal[\"json\", \"python\"] = \"json\", **kwargs: Any\n) -> Any:\n    return model.model_dump(mode=mode, **kwargs)\n\n\ndef _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 \"valid
13read_file
{
 "end_line": 486,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 301
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n Dict[
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "    Dict[Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue],\n    Dict[str, Any],\n]:\n    old_name_to_new_name_map = {}\n    for field_key, schema in field_mapping.items():\n        model = field_key[0].type_\n        if model not in model_name_map or \"$ref\" not in schema:\n            continue\n        new_name = model_name_map[model]\n        old_name = schema[\"$ref\"].split(\"/\")[-1]\n        if old_name in {f\"{new_name}-Input\", f\"{new_name}-Output\"}:\n            continue\n        old_name_to_new_name_map[old_name] = new_name\n\n    new_field_mapping: Dict[\n        Tuple[ModelField, Literal[\"validation\", \"serialization\"]], JsonSchemaValue\n    ] = {}\n    for field_key, schema in field_mapping.items():\n        new_schema = _replace_refs(\n            schema=schema,\n            old_name_to_new_name_map=old_name_to_new_name_map,\n        )\n        new_field_mapping[field_key] = new_schema\n\n    new_definitions = {}\n    for key, value in definitions.items():\n        if key in old_name_to_new_name_map:\n            new_key = old_name_to_new_name_map[key]\n        else:\n            new_key = key\n        new_value = _replace_refs(\n            schema=value,\n            old_name_to_new_name_map=old_name_to_new_name_map,\n        )\n        new_definitions[new_key] = new_value\n    return new_field_mapping, new_definitions\n\n\ndef is_scalar_field(field: ModelField) -> bool:\n    from fastapi import params\n\n    return shared.field_annotation_is_scalar(\n        field.field_info.annotation\n    ) and not isinstance(field.field_info, params.Body)\n\n\ndef is_sequence_field(field: ModelField) -> bool:\n    return shared.field_annotation_is_sequence(field.field_info.annotation)\n\n\ndef is_scalar_sequence_field(field: ModelField) -> bool:\n    return shared.field_annotation_is_scalar_sequence(field.field_info.annotation)\n\n\ndef is_bytes_field(field: Mode
14read_file
{
 "end_line": 486,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 451
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " for arg in get_args(annotation):\n if lenient_issubclass(arg, (BaseModel, Enum)) and arg not
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "        for arg in get_args(annotation):\n            if lenient_issubclass(arg, (BaseModel, Enum)) and arg not in known_models:\n                known_models.add(arg)\n                if lenient_issubclass(arg, BaseModel):\n                    get_flat_models_from_model(arg, known_models=known_models)\n            else:\n                get_flat_models_from_annotation(arg, known_models=known_models)\n    return known_models\n\n\ndef get_flat_models_from_field(\n    field: ModelField, known_models: TypeModelSet\n) -> TypeModelSet:\n    field_type = field.type_\n    if lenient_issubclass(field_type, BaseModel):\n        if field_type in known_models:\n            return known_models\n        known_models.add(field_type)\n        get_flat_models_from_model(field_type, known_models=known_models)\n    elif lenient_issubclass(field_type, Enum):\n        known_models.add(field_type)\n    else:\n        get_flat_models_from_annotation(field_type, known_models=known_models)\n    return known_models\n\n\ndef get_flat_models_from_fields(\n    fields: Sequence[ModelField], known_models: TypeModelSet\n) -> TypeModelSet:\n    for field in fields:\n        get_flat_models_from_field(field, known_models=known_models)\n    return known_models\n\n\ndef get_long_model_name(model: TypeModelOrEnum) -> str:\n    return f\"{model.__module__}__{model.__qualname__}\".replace(\".\", \"__\")\n", "start_line": 451, "end_line": 486, "total_lines": 486, "is_truncated": false}
15write_file
{
 "content": "\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nfrom fastapi.testclient import TestClient\n\napp = FastAPI(separate_input_output_schemas=False)\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\n    @computed_field\n    @property\n    def total(self) -> float:\n        return self.price * 1.2\n\n@app.get(\"/item\", response_model=Item)\ndef get_item():\n    return Item(name=\"Test\", price=10.0)\n\nclient = TestClient(app)\n\ndef test_openapi_computed_field():\n    openapi_schema = app.openapi()\n    # Find the Item schema in components/schemas\n    schemas = openapi_schema[\"components\"][\"schemas\"]\n    # The model name might be \"Item\"\n    item_schema = schemas.get(\"Item\")\n    if not item_schema:\n        print(\"Item schema not found in OpenAPI\")\n        assert False\n    \n    properties = item_schema.get(\"properties\", {})\n    print(f\"Properties: {list(properties.keys())}\")\n    assert \"total\" in properties, \"Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False\"\n\nif __name__ == \"__main__\":\n    test_openapi_computed_field()\n",
 "filepath": "/tmp/repro.py"
}
{"status": "ok", "filepath": "/tmp/repro.py", "size": 1087}
full
{"status": "ok", "filepath": "/tmp/repro.py", "size": 1087}
16run_command
{
 "command": "python3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "python3: can't open f
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "python3: can't open file '/tmp/swe_work/or20_submission/fastapi_13207/a/tmp/repro.py': [Errno 2] No such file or directory\n", "exit_code": 2}}
17run_command
{
 "command": "python3 /workspace/tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "python3: can't open f
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "python3: can't open file '/tmp/swe_work/or20_submission/fastapi_13207/a/workspace/tmp/repro.py': [Errno 2] No such file or directory\n", "exit_code": 2}}
18run_command
{
 "command": "mkdir -p /workspace/tmp && python3 /workspace/tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "python3: can't open f
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 2", "details": {"stdout": "", "stderr": "python3: can't open file '/tmp/swe_work/or20_submission/fastapi_13207/a/workspace/tmp/repro.py': [Errno 2] No such file or directory\n", "exit_code": 2}}
19run_command
{
 "command": "python3 -c \"from fastapi import FastAPI; from pydantic import BaseModel, computed_field; app = FastAPI(separate_input_output_schemas=False); class Item(BaseModel): name: str; price: float; @computed_field @property def total(self) -> float: return self.price * 1.2; @app.get('/item', response_model=Item) def get_item(): return Item(name='Test', price=10.0); print(app.openapi()['components']['schemas']['Item']['properties'].keys())\""
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": " File \"<string>\", l
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "  File \"<string>\", line 1\n    from fastapi import FastAPI; from pydantic import BaseModel, computed_field; app = FastAPI(separate_input_output_schemas=False); class Item(BaseModel): name: str; price: float; @computed_field @property def total(self) -> float: return self.price * 1.2; @app.get('/item', response_model=Item) def get_item(): return Item(name='Test', price=10.0); print(app.openapi()['components']['schemas']['Item']['properties'].keys())\n                                                                                                                                     ^^^^^\nSyntaxError: invalid syntax\n", "exit_code": 1}}
20write_file
{
 "content": "\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nimport json\n\napp = FastAPI(separate_input_output_schemas=False)\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\n    @computed_field\n    @property\n    def total(self) -> float:\n        return self.price * 1.2\n\n@app.get(\"/item\", response_model=Item)\ndef get_item():\n    return Item(name=\"Test\", price=10.0)\n\nopenapi_schema = app.openapi()\nitem_schema = openapi_schema[\"components\"][\"schemas\"][\"Item\"]\nprint(json.dumps(item_schema[\"properties\"], indent=2))\n",
 "filepath": "repro.py"
}
{"status": "ok", "filepath": "repro.py", "size": 538}
full
{"status": "ok", "filepath": "repro.py", "size": 538}
21run_command
{
 "command": "python3 repro.py"
}
{"status": "ok", "stdout": "{\n \"name\": {\n \"type\": \"string\",\n \"title\": \"Name\"\n },\n \"price\": {\n \"type\": \"number\",\n \"title\": \"Price\"\n }
full
{"status": "ok", "stdout": "{\n  \"name\": {\n    \"type\": \"string\",\n    \"title\": \"Name\"\n  },\n  \"price\": {\n    \"type\": \"number\",\n    \"title\": \"Price\"\n  }\n}\n", "exit_code": 0}
22run_command
{
 "command": "sed -i 's/separate_input_output_schemas=False/separate_input_output_schemas=True/' repro.py && python3 repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "sed: -I or -i may not
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "sed: -I or -i may not be used with stdin\n", "exit_code": 1}}
23write_file
{
 "content": "\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel, computed_field\nimport json\n\napp = FastAPI(separate_input_output_schemas=True)\n\nclass Item(BaseModel):\n    name: str\n    price: float\n\n    @computed_field\n    @property\n    def total(self) -> float:\n        return self.price * 1.2\n\n@app.get(\"/item\", response_model=Item)\ndef get_item():\n    return Item(name=\"Test\", price=10.0)\n\nopenapi_schema = app.openapi()\n# When separate_input_output_schemas=True, it might be Item-Output\nitem_schema = openapi_schema[\"components\"][\"schemas\"].get(\"Item-Output\") or openapi_schema[\"components\"][\"schemas\"].get(\"Item\")\nprint(json.dumps(item_schema[\"properties\"], indent=2))\n",
 "filepath": "repro_true.py"
}
{"status": "ok", "filepath": "repro_true.py", "size": 670}
full
{"status": "ok", "filepath": "repro_true.py", "size": 670}
24run_command
{
 "command": "python3 repro_true.py"
}
{"status": "ok", "stdout": "{\n \"name\": {\n \"type\": \"string\",\n \"title\": \"Name\"\n },\n \"price\": {\n \"type\": \"number\",\n \"title\": \"Price\"\n }
full
{"status": "ok", "stdout": "{\n  \"name\": {\n    \"type\": \"string\",\n    \"title\": \"Name\"\n  },\n  \"price\": {\n    \"type\": \"number\",\n    \"title\": \"Price\"\n  },\n  \"total\": {\n    \"type\": \"number\",\n    \"title\": \"Total\",\n    \"readOnly\": true\n  }\n}\n", "exit_code": 0}
25read_file
{
 "end_line": 230,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 170
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _get_model_config(model: BaseModel) -> Any:\n return model.model_config\n\n\ndef get_schem
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "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        serialization_fields, known_models=set()\n
26read_file
{
 "end_line": 280,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 231
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " )\n for model in flat_serialization_models\n ]\n flat_model_fields = flat_validation_model_fi
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "        )\n        for model in flat_serialization_models\n    ]\n    flat_model_fields = flat_validation_model_fields + flat_serialization_model_fields\n    input_types = {f.type_ for f in fields}\n    unique_flat_model_fields = {\n        f for f in flat_model_fields if f.type_ not in input_types\n    }\n\n    inputs = [\n        (field, override_mode or field.mode, field._type_adapter.core_schema)\n        for field in list(fields) + list(unique_flat_model_fields)\n    ]\n    field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs)\n    for item_def in cast(Dict[str, Dict[str, Any]], definitions).values():\n        if \"description\" in item_def:\n            item_description = cast(str, item_def[\"description\"]).split(\"\\f\")[0]\n            item_def[\"description\"] = item_description\n    new_mapping, new_definitions = _remap_definitions_and_field_mappings(\n        model_name_map=model_name_map,\n        definitions=definitions,  # type: ignore[arg-type]\n        field_mapping=field_mapping,\n    )\n    return new_mapping, new_definitions\n\n\ndef _replace_refs(\n    *,\n    schema: Dict[str, Any],\n    old_name_to_new_name_map: Dict[str, str],\n) -> Dict[str, Any]:\n    new_schema = deepcopy(schema)\n    for key, value in new_schema.items():\n        if key == \"$ref\":\n            value = schema[\"$ref\"]\n            if isinstance(value, str):\n                ref_name = schema[\"$ref\"].split(\"/\")[-1]\n                if ref_name in old_name_to_new_name_map:\n                    new_name = old_name_to_new_name_map[ref_name]\n                    new_schema[\"$ref\"] = REF_TEMPLATE.format(model=new_name)\n            continue\n        if isinstance(value, dict):\n            new_schema[key] = _replace_refs(\n                schema=value,\n                old_name_to_new_name_map=old_name_to_new_name_map,\n            )\n        elif isinstance(value, list):\n    
27edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )}"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
28read_file
{
 "end_line": 215,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 200
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": " model_name_map: ModelNameMap,\n separate_input_output_schemas: bool = True,\n) -> Tuple[\n Di
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "    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", "start_line": 200, "end_line": 215, "total_lines": 486, "is_truncated": false}
29edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )}"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
30edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
31read_file
{
 "end_line": 190,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 170
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _get_model_config(model: BaseModel) -> Any:\n return model.model_config\n\n\ndef get_schem
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "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", "start_line": 170, "end_line": 190, "total_lines": 486, "is_truncated": false}
32edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string matched 2 occurrences; provide more context or set allow_multiple"}
33edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "def 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    )"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -171,17 +171,8 @@\n     return model.model_config\n \n \n-def 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+        None if separate_input_output_schemas else \"serialization\"\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", "is_truncated": false}
34edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\n    )",
 "old_string": "    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"validation\"\n    )"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -196,7 +196,7 @@\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+        None if separate_input_output_schemas else \"serialization\"\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", "is_truncated": false}
35run_command
{
 "command": "python3 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 \"/private/workspace/repro.py\", line 21, in <module>\n    openapi_schema = app.openapi()\n  File \"/private/workspace/fastapi/applications.py\", line 1065, in openapi\n    self.openapi_schema = get_openapi(\n                          ~~~~~~~~~~~^\n        title=self.title,\n        ^^^^^^^^^^^^^^^^^\n    ...<12 lines>...\n        external_docs=self.openapi_external_docs,\n        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    )\n    ^\n  File \"/private/workspace/fastapi/openapi/utils.py\", line 511, in get_openapi\n    result = get_openapi_path(\n        route=route,\n    ...<3 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n    )\n  File \"/private/workspace/fastapi/openapi/utils.py\", line 352, in get_openapi_path\n    response_schema = get_schema_from_model_field(\n        field=route.response_field,\n    ...<2 lines>...\n        separate_input_output_schemas=separate_input_output_schemas,\n    )\n  File \"/private/workspace/fastapi/_compat/main.py\", line 337, in get_schema_from_model_field\n    return v2.get_schema_from_model_field(\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAttributeError: module 'fastapi._compat.v2' has no attribute 'get_schema_from_model_field'\n", "exit_code": 1}}
36read_file
{
 "end_line": 185,
 "filepath": "fastapi/_compat/v2.py",
 "start_line": 160
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _model_rebuild(model: Type[BaseModel]) -> None:\n model.model_rebuild()\n\n\ndef _model_du
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "content": "def _model_rebuild(model: Type[BaseModel]) -> None:\n    model.model_rebuild()\n\n\ndef _model_dump(\n    model: BaseModel, mode: Literal[\"json\", \"python\"] = \"json\", **kwargs: Any\n) -> Any:\n    return model.model_dump(mode=mode, **kwargs)\n\n\ndef _get_model_config(model: BaseModel) -> Any:\n    return model.model_config\n\n\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\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", "start_line": 160, "end_line": 185, "total_lines": 477, "is_truncated": false}
37edit_file
{
 "filepath": "fastapi/_compat/v2.py",
 "new_string": "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 \"serialization\"\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",
 "old_string": "def _get_model_config(model: BaseModel) -> Any:\n    return model.model_config\n\n\n    override_mode: Union[Literal[\"validation\"], None] = (\n        None if separate_input_output_schemas else \"serialization\"\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"
}
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n
full
{"status": "ok", "filepath": "fastapi/_compat/v2.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/_compat/v2.py\n+++ b/fastapi/_compat/v2.py\n@@ -171,6 +171,15 @@\n     return model.model_config\n \n \n+def 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 \"serialization\"\n     )\n", "is_truncated": false}
38run_command
{
 "command": "python3 repro.py"
}
{"status": "ok", "stdout": "{\n \"name\": {\n \"type\": \"string\",\n \"title\": \"Name\"\n },\n \"price\": {\n \"type\": \"number\",\n \"title\": \"Price\"\n }
full
{"status": "ok", "stdout": "{\n  \"name\": {\n    \"type\": \"string\",\n    \"title\": \"Name\"\n  },\n  \"price\": {\n    \"type\": \"number\",\n    \"title\": \"Price\"\n  },\n  \"total\": {\n    \"type\": \"number\",\n    \"title\": \"Total\",\n    \"readOnly\": true\n  }\n}\n", "exit_code": 0}
39submit_patch
[]
{"status": "ok", "patch_size": 3898, "files_changed": 4}
full
{"status": "ok", "patch_size": 3898, "files_changed": 4}

Patch

diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py
index 3d91814c..5df02214 100644
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -181,7 +181,7 @@ def get_schema_from_model_field(
     separate_input_output_schemas: bool = True,
 ) -> Dict[str, Any]:
     override_mode: Union[Literal["validation"], None] = (
-        None if separate_input_output_schemas else "validation"
+        None if separate_input_output_schemas else "serialization"
     )
     # This expects that GenerateJsonSchema was already used to generate the definitions
     json_schema = field_mapping[(field, override_mode or field.mode)]
@@ -205,7 +205,7 @@ def get_definitions(
 ]:
     schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
     override_mode: Union[Literal["validation"], None] = (
-        None if separate_input_output_schemas else "validation"
+        None if separate_input_output_schemas else "serialization"
     )
     validation_fields = [field for field in fields if field.mode == "validation"]
     serialization_fields = [field for field in fields if field.mode == "serialization"]
diff --git a/repro.py b/repro.py
new file mode 100644
index 00000000..fa846bd9
--- /dev/null
+++ b/repro.py
@@ -0,0 +1,23 @@
+
+from fastapi import FastAPI
+from pydantic import BaseModel, computed_field
+import json
+
+app = FastAPI(separate_input_output_schemas=False)
+
+class Item(BaseModel):
+    name: str
+    price: float
+
+    @computed_field
+    @property
+    def total(self) -> float:
+        return self.price * 1.2
+
+@app.get("/item", response_model=Item)
+def get_item():
+    return Item(name="Test", price=10.0)
+
+openapi_schema = app.openapi()
+item_schema = openapi_schema["components"]["schemas"]["Item"]
+print(json.dumps(item_schema["properties"], indent=2))
diff --git a/repro_true.py b/repro_true.py
new file mode 100644
index 00000000..979ca8c2
--- /dev/null
+++ b/repro_true.py
@@ -0,0 +1,24 @@
+
+from fastapi import FastAPI
+from pydantic import BaseModel, computed_field
+import json
+
+app = FastAPI(separate_input_output_schemas=True)
+
+class Item(BaseModel):
+    name: str
+    price: float
+
+    @computed_field
+    @property
+    def total(self) -> float:
+        return self.price * 1.2
+
+@app.get("/item", response_model=Item)
+def get_item():
+    return Item(name="Test", price=10.0)
+
+openapi_schema = app.openapi()
+# When separate_input_output_schemas=True, it might be Item-Output
+item_schema = openapi_schema["components"]["schemas"].get("Item-Output") or openapi_schema["components"]["schemas"].get("Item")
+print(json.dumps(item_schema["properties"], indent=2))
diff --git a/tmp/repro.py b/tmp/repro.py
new file mode 100644
index 00000000..0f8acebf
--- /dev/null
+++ b/tmp/repro.py
@@ -0,0 +1,38 @@
+
+from fastapi import FastAPI
+from pydantic import BaseModel, computed_field
+from fastapi.testclient import TestClient
+
+app = FastAPI(separate_input_output_schemas=False)
+
+class Item(BaseModel):
+    name: str
+    price: float
+
+    @computed_field
+    @property
+    def total(self) -> float:
+        return self.price * 1.2
+
+@app.get("/item", response_model=Item)
+def get_item():
+    return Item(name="Test", price=10.0)
+
+client = TestClient(app)
+
+def test_openapi_computed_field():
+    openapi_schema = app.openapi()
+    # Find the Item schema in components/schemas
+    schemas = openapi_schema["components"]["schemas"]
+    # The model name might be "Item"
+    item_schema = schemas.get("Item")
+    if not item_schema:
+        print("Item schema not found in OpenAPI")
+        assert False
+    
+    properties = item_schema.get("properties", {})
+    print(f"Properties: {list(properties.keys())}")
+    assert "total" in properties, "Computed field 'total' missing from OpenAPI schema when separate_input_output_schemas=False"
+
+if __name__ == "__main__":
+    test_openapi_computed_field()

Test output

show
......                                                                   [100%]
=============================== 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
6 passed, 1 warning in 0.43s