Repository: pydantic/pydantic

Issue:
Type signature for Field() gt/lt/gte/lte validators should include timedelta
### Initial Checks

- [X] I confirm that I'm using Pydantic V2

### Description

The type signature for the numeric min/max value constraints on Field() (i.e. `gt`, `lt`, `gte`, `lte` ) [specified here](https://github.com/pydantic/pydantic/blob/903041574c49de873498b36daea5aadd5db63e50/pydantic/fields.py#L689) is too restrictive, allowing only `float` or `None`.

In actual use, the validation works correctly as expected with `timedelta()` instances, but static type checkers (at least Pyright) object.

### Example Code

```Python
from datetime import timedelta

from pydantic import BaseModel, Field

class Duration(BaseModel):
    td: timedelta = Field(timedelta(0), gt=timedelta(0))


Duration(td=timedelta(seconds=1))
Duration(td=timedelta(seconds=-1))
# ValidationError: 1 validation error for Duration
# td
#  Input should be greater than 0 seconds [type=greater_than, input_value=datetime.timedelta(days=-1), input_type=timedelta]
#    For further information visit https://errors.pydantic.dev/2.7/v/greater_than

# This is all perfectly valid code, but Pyright (and I assume also mypy) complains about the type signature

# $ pyright example.py 
# /home/bjmc/Sandbox/example.py
#  /home/bjmc/Sandbox/example.py:6:44 - error: Argument of type "timedelta" cannot be assigned to # parameter "gt" of type "float | None" in function "Field"
#    Type "timedelta" is incompatible with type "float | None"
#      "timedelta" is incompatible with "float"
#      "timedelta" is incompatible with "None" (reportArgumentType)
# 1 error, 0 warnings, 0 informations
```


### Python, Pydantic & OS Version

```Text
pydantic version: 2.7.1
        pydantic-core version: 2.18.2
          pydantic-core build: profile=release pgo=true
                 install path: /home/bjmc/.cache/pypoetry/virtualenvs/ska-oso-pdm-zPbyUnCb-py3.10/lib/python3.10/site-packages/pydantic
               python version: 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0]
                     platform: Linux-6.5.0-1022-oem-x86_64-with-glibc2.35
             related packages: typing_extensions-4.11.0 pyright-1.1.363
                       commit: unknown
```



Discussion:
I'm not a typing wizard, but I wonder if a good resolution here would be to use the [comparison protocols defined by typeshed](https://github.com/python/typeshed/blob/f4ae363b564de69bfad4b4346204a25a99cd6642/stdlib/_typeshed/__init__.pyi#L52) for these field validators?

I'm happy to open an PR if people like that approach, or have another suggestion.
@bjmc,

Hmm, I'm intrigued - feel free to open a PR and I can review. I don think it could be good to offer more general support here!
Seems reasonable to use the comparison protocols, internally Pydantic uses the functions defined in `pydantic._internal._validators` (e.g. `greater_than_validator`), so this seems like a safe improvement.

Two options here:
- Use the protocols defined in `annotated_types` (best option imo).
- Import the protocols from `_typeshed` in an `if TYPE_CHECKING:` block. They are not defined as being stable. 
Let's go with the `annotated_types` protocols
I agree that `annotated_types` is better since it's already a dependency.

Resolving patch:
diff --git a/pydantic/fields.py b/pydantic/fields.py
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -56,10 +56,10 @@ class _FromFieldInfoInputs(typing_extensions.TypedDict, total=False):
     description: str | None
     examples: list[Any] | None
     exclude: bool | None
-    gt: float | None
-    ge: float | None
-    lt: float | None
-    le: float | None
+    gt: annotated_types.SupportsGt | None
+    ge: annotated_types.SupportsGe | None
+    lt: annotated_types.SupportsLt | None
+    le: annotated_types.SupportsLe | None
     multiple_of: float | None
     strict: bool | None
     min_length: int | None
@@ -686,10 +686,10 @@ def Field(  # noqa: C901
     pattern: str | typing.Pattern[str] | None = _Unset,
     strict: bool | None = _Unset,
     coerce_numbers_to_str: bool | None = _Unset,
-    gt: float | None = _Unset,
-    ge: float | None = _Unset,
-    lt: float | None = _Unset,
-    le: float | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
     multiple_of: float | None = _Unset,
     allow_inf_nan: bool | None = _Unset,
     max_digits: int | None = _Unset,


Test patch: