Repository: litestar-org/litestar

Issue:
Bug: OpenAPI docs parse pydantic Field()
### Description

Referencing a discussion on Discord: https://discord.com/channels/919193495116337154/1093516724864811038

It seems that the pydantic `Field()` in the model isn't parsed and generating title/description fields in the openapi.json. 

Running on docker `python:3.11-alpine`

### URL to code causing the issue

_No response_

### MCVE

```python
from starlite import Starlite, post, Controller
from pydantic import BaseModel, Field
class SomeRequestData(BaseModel):
    hostname: str = Field(title="The hostname", description="Some description")
    interface: str = Field(title="Interface", description="Interface to use")
class SomeController(Controller):
    path = "/somepath"
    @post(
        path="/test", summary="testing", tags=["Test"], description="Test description"
    )
    async def request_example(self, data: SomeRequestData) -> str:
        return "test"
app = Starlite([SomeController])
```


### Steps to reproduce

_No response_

### Screenshots


Current (as in `2.0.0a4`):
![Current](https://cdn.discordapp.com/attachments/1093516724864811038/1097851092081065984/Screenshot_2023-04-18_at_13.46.09.png)

Expected (as in `1.51.9`): 
![Expected](https://cdn.discordapp.com/attachments/1093516724864811038/1093519951651950682/image.png)



### Logs

_No response_

### Litestar Version

litestar==2.0.0a4

### Platform

- [ ] Linux
- [X] Mac
- [ ] Windows
- [X] Other (Please specify in the description above)


Discussion:
I looked into this a bit since we happened to notice it today as well. 

It looks like it applies to both request bodies and responses.

Looking at the source it appears `litestart._signature.field.SignatureField` doesn't support descriptions. There's an `extra` attribute but that appears to be used for creating examples. 
> It looks like it applies to both request bodies and responses.

Correct.

I went down the rabbit hole and tried to figure out why this happens. Looks like `construct_open_api_with_schema_class` isn't called anymore from `app.py:update_openapi_schema` when I'm comparing it to 1.51.10. 

Simply adding this doesn't work because the `OpenAPI` class isnt based on pydantic `BaseModel` anymore but the litestar `BaseSchemaObject`. 

The function `construct_open_api_with_schema_class` relies on the `BaseModel.copy(deep=True)` which doesn't exist on `BaseSchemaObject`.

Resolving patch:
diff --git a/litestar/_openapi/schema_generation/schema.py b/litestar/_openapi/schema_generation/schema.py
--- a/litestar/_openapi/schema_generation/schema.py
+++ b/litestar/_openapi/schema_generation/schema.py
@@ -550,7 +550,9 @@ def create_schema_for_pydantic_model(
         required=sorted([field.alias or field.name for field in field_type.__fields__.values() if field.required]),
         properties={
             (f.alias or f.name): create_schema(
-                field=SignatureField.create(field_type=field_type_hints[f.name], name=f.alias or f.name),
+                field=SignatureField.create(
+                    field_type=field_type_hints[f.name], name=f.alias or f.name, default_value=f.field_info
+                ),
                 generate_examples=generate_examples,
                 plugins=plugins,
                 schemas=schemas,
diff --git a/litestar/_signature/field.py b/litestar/_signature/field.py
--- a/litestar/_signature/field.py
+++ b/litestar/_signature/field.py
@@ -4,6 +4,7 @@
 from typing import Any, Literal
 
 from msgspec import Meta
+from pydantic.fields import FieldInfo
 from typing_extensions import Annotated, get_args, get_origin
 
 from litestar.constants import UNDEFINED_SENTINELS
@@ -40,6 +41,28 @@ def _create_metadata_from_type(
             min_items=value.min_length if is_sequence_container else None,
             max_items=value.max_length if is_sequence_container else None,
         )
+    if isinstance(value, FieldInfo):
+        values: dict[str, Any] = {
+            k: v
+            for k, v in {
+                "gt": value.gt,
+                "ge": value.ge,
+                "lt": value.lt,
+                "le": value.le,
+                "multiple_of": value.multiple_of,
+                "regex": value.regex,
+                "min_length": value.min_length,
+                "max_length": value.max_length,
+                "min_items": value.min_items,
+                "max_items": value.max_items,
+                "description": value.description,
+                "title": value.title,
+                "const": value.const is not None,
+            }.items()
+            if v is not None
+        }
+        if values:
+            return model(**values)
     return None
 
 
@@ -191,15 +214,19 @@ def create(
         if kwarg_model and default_value is Empty:
             default_value = kwarg_model.default
 
+        elif isinstance(default_value, FieldInfo) and not kwarg_model:
+            kwarg_model = _create_metadata_from_type(
+                default_value, BodyKwarg if name == "data" else ParameterKwarg, field_type=field_type
+            )
+
         origin = get_origin(field_type)
 
         if not children and origin and (type_args := get_args(field_type)):
             if origin is Annotated:
                 field_type = type_args[0]
-                if not kwarg_model:
-                    kwarg_model = _create_metadata_from_type(
-                        type_args[1], BodyKwarg if name == "data" else ParameterKwarg, field_type=field_type
-                    )
+                kwarg_model = kwarg_model or _create_metadata_from_type(
+                    type_args[1], BodyKwarg if name == "data" else ParameterKwarg, field_type=field_type
+                )
             else:
                 children = tuple(SignatureField.create(arg) for arg in type_args)
 


Test patch:
diff --git a/tests/openapi/test_schema.py b/tests/openapi/test_schema.py
--- a/tests/openapi/test_schema.py
+++ b/tests/openapi/test_schema.py
@@ -4,7 +4,7 @@
 
 import msgspec
 import pytest
-from pydantic import BaseModel
+from pydantic import BaseModel, Field
 from typing_extensions import Annotated
 
 from litestar import Controller, MediaType, get
@@ -260,4 +260,22 @@ class Lookup(msgspec.Struct):
         plugins=[],
         schemas=schemas,
     )
-    assert schemas["Lookup"]
+    schema = schemas["Lookup"]
+    assert schema.properties["id"].type == OpenAPIType.STRING  # type: ignore
+
+
+def test_create_schema_for_pydantic_field() -> None:
+    class Model(BaseModel):
+        value: str = Field(title="title", description="description")
+
+    schemas: Dict[str, Schema] = {}
+    create_schema(
+        field=SignatureField.create(name="Model", field_type=Model),
+        generate_examples=False,
+        plugins=[],
+        schemas=schemas,
+    )
+    schema = schemas["Model"]
+
+    assert schema.properties["value"].description == "description"  # type: ignore
+    assert schema.properties["value"].title == "title"  # type: ignore