diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b3434d32..454d8969 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.2.0-alpha.54" + ".": "0.2.0-alpha.55" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 5fc516db..11f2aabc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,3 +1,3 @@ configured_endpoints: 15 -openapi_spec_hash: 9a0b363025305f6b086bcdfe43274830 +openapi_spec_hash: 7dd38774b534c352620bca63efa85b19 config_hash: 21fb9730d1cdc9e3fd38724c4774b894 diff --git a/CHANGELOG.md b/CHANGELOG.md index e64ea3dc..857a7f1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## 0.2.0-alpha.55 (2025-04-19) + +Full Changelog: [v0.2.0-alpha.54...v0.2.0-alpha.55](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.54...v0.2.0-alpha.55) + +### Features + +* **api:** api update ([b40ca02](https://github.com/openlayer-ai/openlayer-python/commit/b40ca0253f502e9d249c901e7f878b7f9461a0c1)) + + +### Chores + +* **internal:** base client updates ([9afcd88](https://github.com/openlayer-ai/openlayer-python/commit/9afcd88c21786e5903f04227e314164699aeddea)) +* **internal:** bump pyright version ([0301486](https://github.com/openlayer-ai/openlayer-python/commit/03014864bcb6e69d5040435521cfdc76f3189641)) +* **internal:** update models test ([97be493](https://github.com/openlayer-ai/openlayer-python/commit/97be4939dc8a3d16f3316cc513a5cad8d2311d41)) + ## 0.2.0-alpha.54 (2025-04-15) Full Changelog: [v0.2.0-alpha.53...v0.2.0-alpha.54](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.53...v0.2.0-alpha.54) diff --git a/pyproject.toml b/pyproject.toml index b0c5b338..ceebb8c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openlayer" -version = "0.2.0-alpha.54" +version = "0.2.0-alpha.55" description = "The official Python library for the openlayer API" dynamic = ["readme"] license = "Apache-2.0" @@ -48,7 +48,7 @@ Repository = "https://github.com/openlayer-ai/openlayer-python" managed = true # version pins are in requirements-dev.lock dev-dependencies = [ - "pyright>=1.1.359", + "pyright==1.1.399", "mypy", "respx", "pytest", diff --git a/requirements-dev.lock b/requirements-dev.lock index 3a355bb0..0524201f 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -81,7 +81,7 @@ pydantic-core==2.27.1 # via pydantic pygments==2.18.0 # via rich -pyright==1.1.392.post0 +pyright==1.1.399 pytest==8.3.3 # via pytest-asyncio pytest-asyncio==0.24.0 diff --git a/src/openlayer/_base_client.py b/src/openlayer/_base_client.py index dcc1a63e..d4c98dc0 100644 --- a/src/openlayer/_base_client.py +++ b/src/openlayer/_base_client.py @@ -98,7 +98,11 @@ _AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) if TYPE_CHECKING: - from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + from httpx._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG else: try: from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT @@ -115,6 +119,7 @@ class PageInfo: url: URL | NotGiven params: Query | NotGiven + json: Body | NotGiven @overload def __init__( @@ -130,19 +135,30 @@ def __init__( params: Query, ) -> None: ... + @overload + def __init__( + self, + *, + json: Body, + ) -> None: ... + def __init__( self, *, url: URL | NotGiven = NOT_GIVEN, + json: Body | NotGiven = NOT_GIVEN, params: Query | NotGiven = NOT_GIVEN, ) -> None: self.url = url + self.json = json self.params = params @override def __repr__(self) -> str: if self.url: return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" return f"{self.__class__.__name__}(params={self.params})" @@ -191,6 +207,19 @@ def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: options.url = str(url) return options + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + raise ValueError("Unexpected PageInfo state") diff --git a/src/openlayer/_models.py b/src/openlayer/_models.py index 34935716..58b9263e 100644 --- a/src/openlayer/_models.py +++ b/src/openlayer/_models.py @@ -19,7 +19,6 @@ ) import pydantic -import pydantic.generics from pydantic.fields import FieldInfo from ._types import ( diff --git a/src/openlayer/_utils/_typing.py b/src/openlayer/_utils/_typing.py index 1958820f..1bac9542 100644 --- a/src/openlayer/_utils/_typing.py +++ b/src/openlayer/_utils/_typing.py @@ -110,7 +110,7 @@ class MyResponse(Foo[_T]): ``` """ cls = cast(object, get_origin(typ) or typ) - if cls in generic_bases: + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] # we're given the class directly return extract_type_arg(typ, index) diff --git a/src/openlayer/_version.py b/src/openlayer/_version.py index a07de584..f34e00e9 100644 --- a/src/openlayer/_version.py +++ b/src/openlayer/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "openlayer" -__version__ = "0.2.0-alpha.54" # x-release-please-version +__version__ = "0.2.0-alpha.55" # x-release-please-version diff --git a/src/openlayer/types/commits/test_result_list_response.py b/src/openlayer/types/commits/test_result_list_response.py index c62a3efc..af98b7c6 100644 --- a/src/openlayer/types/commits/test_result_list_response.py +++ b/src/openlayer/types/commits/test_result_list_response.py @@ -8,21 +8,34 @@ from ..._models import BaseModel -__all__ = ["TestResultListResponse", "Item", "ItemGoal", "ItemGoalThreshold"] +__all__ = ["TestResultListResponse", "Item", "ItemGoal", "ItemGoalThreshold", "ItemGoalThresholdInsightParameter"] + + +class ItemGoalThresholdInsightParameter(BaseModel): + name: str + """The name of the insight filter.""" + + value: object class ItemGoalThreshold(BaseModel): insight_name: Optional[str] = FieldInfo(alias="insightName", default=None) """The insight name to be evaluated.""" - insight_parameters: Optional[List[object]] = FieldInfo(alias="insightParameters", default=None) + insight_parameters: Optional[List[ItemGoalThresholdInsightParameter]] = FieldInfo( + alias="insightParameters", default=None + ) + """The insight parameters. Required only for some test subtypes.""" measurement: Optional[str] = None """The measurement to be evaluated.""" - operator: Optional[str] = None + operator: Optional[Literal["is", ">", ">=", "<", "<=", "!="]] = None """The operator to be used for the evaluation.""" + threshold_mode: Optional[Literal["automatic", "manual"]] = FieldInfo(alias="thresholdMode", default=None) + """Whether to use automatic anomaly detection or manual thresholds""" + value: Union[float, bool, str, List[str], None] = None """The value to be compared.""" diff --git a/src/openlayer/types/inference_pipelines/test_result_list_response.py b/src/openlayer/types/inference_pipelines/test_result_list_response.py index c62a3efc..af98b7c6 100644 --- a/src/openlayer/types/inference_pipelines/test_result_list_response.py +++ b/src/openlayer/types/inference_pipelines/test_result_list_response.py @@ -8,21 +8,34 @@ from ..._models import BaseModel -__all__ = ["TestResultListResponse", "Item", "ItemGoal", "ItemGoalThreshold"] +__all__ = ["TestResultListResponse", "Item", "ItemGoal", "ItemGoalThreshold", "ItemGoalThresholdInsightParameter"] + + +class ItemGoalThresholdInsightParameter(BaseModel): + name: str + """The name of the insight filter.""" + + value: object class ItemGoalThreshold(BaseModel): insight_name: Optional[str] = FieldInfo(alias="insightName", default=None) """The insight name to be evaluated.""" - insight_parameters: Optional[List[object]] = FieldInfo(alias="insightParameters", default=None) + insight_parameters: Optional[List[ItemGoalThresholdInsightParameter]] = FieldInfo( + alias="insightParameters", default=None + ) + """The insight parameters. Required only for some test subtypes.""" measurement: Optional[str] = None """The measurement to be evaluated.""" - operator: Optional[str] = None + operator: Optional[Literal["is", ">", ">=", "<", "<=", "!="]] = None """The operator to be used for the evaluation.""" + threshold_mode: Optional[Literal["automatic", "manual"]] = FieldInfo(alias="thresholdMode", default=None) + """Whether to use automatic anomaly detection or manual thresholds""" + value: Union[float, bool, str, List[str], None] = None """The value to be compared.""" diff --git a/tests/conftest.py b/tests/conftest.py index 554ab710..1e038ff9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ from openlayer import Openlayer, AsyncOpenlayer if TYPE_CHECKING: - from _pytest.fixtures import FixtureRequest + from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] pytest.register_assert_rewrite("tests.utils") diff --git a/tests/test_models.py b/tests/test_models.py index d9f8dc55..1f71a02e 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -492,12 +492,15 @@ class Model(BaseModel): resource_id: Optional[str] = None m = Model.construct() + assert m.resource_id is None assert "resource_id" not in m.model_fields_set m = Model.construct(resource_id=None) + assert m.resource_id is None assert "resource_id" in m.model_fields_set m = Model.construct(resource_id="foo") + assert m.resource_id == "foo" assert "resource_id" in m.model_fields_set @@ -832,7 +835,7 @@ class B(BaseModel): @pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") def test_type_alias_type() -> None: - Alias = TypeAliasType("Alias", str) + Alias = TypeAliasType("Alias", str) # pyright: ignore class Model(BaseModel): alias: Alias