diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2cf44ba6..4ad3fef3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.17.12" + ".": "0.18.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index f4bb289c..a47d0ba9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,3 +1,3 @@ -configured_endpoints: 27 -openapi_spec_hash: c70c3eccfe803e99c14e97e650b1e314 -config_hash: 1f7626e569e1a74574a58d7883170a0e +configured_endpoints: 28 +openapi_spec_hash: 5f7962599290c70cb47c05c3b29fdbd8 +config_hash: f1c0c034bd832878eb47146c51afdd55 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e7808c1..e536715b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ 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.18.0 (2026-03-06) + +Full Changelog: [v0.17.12...v0.18.0](https://github.com/openlayer-ai/openlayer-python/compare/v0.17.12...v0.18.0) + +### Features + +* **api:** update POST /rows to use list method name ([675f015](https://github.com/openlayer-ai/openlayer-python/commit/675f01562b6e284e31fb0170a827df5036ab4555)) +* **closes OPEN-9425:** document /rows endpoint in API reference and … ([9c293e6](https://github.com/openlayer-ai/openlayer-python/commit/9c293e60c0730a5dea1d53506c2c7ad42d6de698)) + ## 0.17.12 (2026-03-06) Full Changelog: [v0.17.11...v0.17.12](https://github.com/openlayer-ai/openlayer-python/compare/v0.17.11...v0.17.12) diff --git a/api.md b/api.md index 5e96f44e..64d5c73b 100644 --- a/api.md +++ b/api.md @@ -150,12 +150,13 @@ Methods: Types: ```python -from openlayer.types.inference_pipelines import RowUpdateResponse +from openlayer.types.inference_pipelines import RowUpdateResponse, RowListResponse ``` Methods: - client.inference_pipelines.rows.update(inference_pipeline_id, \*\*params) -> RowUpdateResponse +- client.inference_pipelines.rows.list(inference_pipeline_id, \*\*params) -> RowListResponse ## TestResults diff --git a/pyproject.toml b/pyproject.toml index 5ed916d3..bf96abb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openlayer" -version = "0.17.12" +version = "0.18.0" description = "The official Python library for the openlayer API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openlayer/_version.py b/src/openlayer/_version.py index ecca63ed..8cb28e8d 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.17.12" # x-release-please-version +__version__ = "0.18.0" # x-release-please-version diff --git a/src/openlayer/resources/inference_pipelines/rows.py b/src/openlayer/resources/inference_pipelines/rows.py index 0c77dfb1..4fa059f6 100644 --- a/src/openlayer/resources/inference_pipelines/rows.py +++ b/src/openlayer/resources/inference_pipelines/rows.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing import Optional +from typing import Iterable, Optional import httpx -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -17,7 +17,8 @@ async_to_streamed_response_wrapper, ) from ..._base_client import make_request_options -from ...types.inference_pipelines import row_update_params +from ...types.inference_pipelines import row_list_params, row_update_params +from ...types.inference_pipelines.row_list_response import RowListResponse from ...types.inference_pipelines.row_update_response import RowUpdateResponse __all__ = ["RowsResource", "AsyncRowsResource"] @@ -94,6 +95,84 @@ def update( cast_to=RowUpdateResponse, ) + def list( + self, + inference_pipeline_id: str, + *, + asc: bool | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + sort_column: str | Omit = omit, + column_filters: Optional[Iterable[row_list_params.ColumnFilter]] | Omit = omit, + exclude_row_id_list: Optional[Iterable[int]] | Omit = omit, + not_search_query_and: Optional[SequenceNotStr[str]] | Omit = omit, + not_search_query_or: Optional[SequenceNotStr[str]] | Omit = omit, + row_id_list: Optional[Iterable[int]] | Omit = omit, + search_query_and: Optional[SequenceNotStr[str]] | Omit = omit, + search_query_or: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RowListResponse: + """ + A list of rows for an inference pipeline. + + Args: + asc: Whether or not to sort on the sortColumn in ascending order. + + page: The page to return in a paginated query. + + per_page: Maximum number of items to return per page. + + sort_column: Name of the column to sort on + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not inference_pipeline_id: + raise ValueError( + f"Expected a non-empty value for `inference_pipeline_id` but received {inference_pipeline_id!r}" + ) + return self._post( + f"/inference-pipelines/{inference_pipeline_id}/rows", + body=maybe_transform( + { + "column_filters": column_filters, + "exclude_row_id_list": exclude_row_id_list, + "not_search_query_and": not_search_query_and, + "not_search_query_or": not_search_query_or, + "row_id_list": row_id_list, + "search_query_and": search_query_and, + "search_query_or": search_query_or, + }, + row_list_params.RowListParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "asc": asc, + "page": page, + "per_page": per_page, + "sort_column": sort_column, + }, + row_list_params.RowListParams, + ), + ), + cast_to=RowListResponse, + ) + class AsyncRowsResource(AsyncAPIResource): @cached_property @@ -166,6 +245,84 @@ async def update( cast_to=RowUpdateResponse, ) + async def list( + self, + inference_pipeline_id: str, + *, + asc: bool | Omit = omit, + page: int | Omit = omit, + per_page: int | Omit = omit, + sort_column: str | Omit = omit, + column_filters: Optional[Iterable[row_list_params.ColumnFilter]] | Omit = omit, + exclude_row_id_list: Optional[Iterable[int]] | Omit = omit, + not_search_query_and: Optional[SequenceNotStr[str]] | Omit = omit, + not_search_query_or: Optional[SequenceNotStr[str]] | Omit = omit, + row_id_list: Optional[Iterable[int]] | Omit = omit, + search_query_and: Optional[SequenceNotStr[str]] | Omit = omit, + search_query_or: Optional[SequenceNotStr[str]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RowListResponse: + """ + A list of rows for an inference pipeline. + + Args: + asc: Whether or not to sort on the sortColumn in ascending order. + + page: The page to return in a paginated query. + + per_page: Maximum number of items to return per page. + + sort_column: Name of the column to sort on + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not inference_pipeline_id: + raise ValueError( + f"Expected a non-empty value for `inference_pipeline_id` but received {inference_pipeline_id!r}" + ) + return await self._post( + f"/inference-pipelines/{inference_pipeline_id}/rows", + body=await async_maybe_transform( + { + "column_filters": column_filters, + "exclude_row_id_list": exclude_row_id_list, + "not_search_query_and": not_search_query_and, + "not_search_query_or": not_search_query_or, + "row_id_list": row_id_list, + "search_query_and": search_query_and, + "search_query_or": search_query_or, + }, + row_list_params.RowListParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "asc": asc, + "page": page, + "per_page": per_page, + "sort_column": sort_column, + }, + row_list_params.RowListParams, + ), + ), + cast_to=RowListResponse, + ) + class RowsResourceWithRawResponse: def __init__(self, rows: RowsResource) -> None: @@ -174,6 +331,9 @@ def __init__(self, rows: RowsResource) -> None: self.update = to_raw_response_wrapper( rows.update, ) + self.list = to_raw_response_wrapper( + rows.list, + ) class AsyncRowsResourceWithRawResponse: @@ -183,6 +343,9 @@ def __init__(self, rows: AsyncRowsResource) -> None: self.update = async_to_raw_response_wrapper( rows.update, ) + self.list = async_to_raw_response_wrapper( + rows.list, + ) class RowsResourceWithStreamingResponse: @@ -192,6 +355,9 @@ def __init__(self, rows: RowsResource) -> None: self.update = to_streamed_response_wrapper( rows.update, ) + self.list = to_streamed_response_wrapper( + rows.list, + ) class AsyncRowsResourceWithStreamingResponse: @@ -201,3 +367,6 @@ def __init__(self, rows: AsyncRowsResource) -> None: self.update = async_to_streamed_response_wrapper( rows.update, ) + self.list = async_to_streamed_response_wrapper( + rows.list, + ) diff --git a/src/openlayer/types/inference_pipelines/__init__.py b/src/openlayer/types/inference_pipelines/__init__.py index 3ccedd4e..d18f6463 100644 --- a/src/openlayer/types/inference_pipelines/__init__.py +++ b/src/openlayer/types/inference_pipelines/__init__.py @@ -2,6 +2,8 @@ from __future__ import annotations +from .row_list_params import RowListParams as RowListParams +from .row_list_response import RowListResponse as RowListResponse from .row_update_params import RowUpdateParams as RowUpdateParams from .data_stream_params import DataStreamParams as DataStreamParams from .row_update_response import RowUpdateResponse as RowUpdateResponse diff --git a/src/openlayer/types/inference_pipelines/row_list_params.py b/src/openlayer/types/inference_pipelines/row_list_params.py new file mode 100644 index 00000000..f7691dc3 --- /dev/null +++ b/src/openlayer/types/inference_pipelines/row_list_params.py @@ -0,0 +1,77 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union, Iterable, Optional +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._types import SequenceNotStr +from ..._utils import PropertyInfo + +__all__ = [ + "RowListParams", + "ColumnFilter", + "ColumnFilterSetColumnFilter", + "ColumnFilterNumericColumnFilter", + "ColumnFilterStringColumnFilter", +] + + +class RowListParams(TypedDict, total=False): + asc: bool + """Whether or not to sort on the sortColumn in ascending order.""" + + page: int + """The page to return in a paginated query.""" + + per_page: Annotated[int, PropertyInfo(alias="perPage")] + """Maximum number of items to return per page.""" + + sort_column: Annotated[str, PropertyInfo(alias="sortColumn")] + """Name of the column to sort on""" + + column_filters: Annotated[Optional[Iterable[ColumnFilter]], PropertyInfo(alias="columnFilters")] + + exclude_row_id_list: Annotated[Optional[Iterable[int]], PropertyInfo(alias="excludeRowIdList")] + + not_search_query_and: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="notSearchQueryAnd")] + + not_search_query_or: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="notSearchQueryOr")] + + row_id_list: Annotated[Optional[Iterable[int]], PropertyInfo(alias="rowIdList")] + + search_query_and: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="searchQueryAnd")] + + search_query_or: Annotated[Optional[SequenceNotStr[str]], PropertyInfo(alias="searchQueryOr")] + + +class ColumnFilterSetColumnFilter(TypedDict, total=False): + measurement: Required[str] + """The name of the column.""" + + operator: Required[Literal["contains_none", "contains_any", "contains_all", "one_of", "none_of"]] + + value: Required[SequenceNotStr[Union[str, float]]] + + +class ColumnFilterNumericColumnFilter(TypedDict, total=False): + measurement: Required[str] + """The name of the column.""" + + operator: Required[Literal[">", ">=", "is", "<", "<=", "!="]] + + value: Required[Optional[float]] + + +class ColumnFilterStringColumnFilter(TypedDict, total=False): + measurement: Required[str] + """The name of the column.""" + + operator: Required[Literal["is", "!="]] + + value: Required[Union[str, bool]] + + +ColumnFilter: TypeAlias = Union[ + ColumnFilterSetColumnFilter, ColumnFilterNumericColumnFilter, ColumnFilterStringColumnFilter +] diff --git a/src/openlayer/types/inference_pipelines/row_list_response.py b/src/openlayer/types/inference_pipelines/row_list_response.py new file mode 100644 index 00000000..ed36eb85 --- /dev/null +++ b/src/openlayer/types/inference_pipelines/row_list_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from ..._models import BaseModel + +__all__ = ["RowListResponse", "Item"] + + +class Item(BaseModel): + openlayer_row_id: int + + +class RowListResponse(BaseModel): + items: List[Item] diff --git a/tests/api_resources/inference_pipelines/test_rows.py b/tests/api_resources/inference_pipelines/test_rows.py index 1c3da6d1..4111e517 100644 --- a/tests/api_resources/inference_pipelines/test_rows.py +++ b/tests/api_resources/inference_pipelines/test_rows.py @@ -9,7 +9,7 @@ from openlayer import Openlayer, AsyncOpenlayer from tests.utils import assert_matches_type -from openlayer.types.inference_pipelines import RowUpdateResponse +from openlayer.types.inference_pipelines import RowListResponse, RowUpdateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -79,6 +79,68 @@ def test_path_params_update(self, client: Openlayer) -> None: row={}, ) + @parametrize + def test_method_list(self, client: Openlayer) -> None: + row = client.inference_pipelines.rows.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(RowListResponse, row, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Openlayer) -> None: + row = client.inference_pipelines.rows.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + asc=True, + page=1, + per_page=1, + sort_column="sortColumn", + column_filters=[ + { + "measurement": "openlayer_token_set", + "operator": "contains_none", + "value": ["cat"], + } + ], + exclude_row_id_list=[0], + not_search_query_and=["string"], + not_search_query_or=["string"], + row_id_list=[0], + search_query_and=["string"], + search_query_or=["string"], + ) + assert_matches_type(RowListResponse, row, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Openlayer) -> None: + response = client.inference_pipelines.rows.with_raw_response.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + row = response.parse() + assert_matches_type(RowListResponse, row, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Openlayer) -> None: + with client.inference_pipelines.rows.with_streaming_response.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + row = response.parse() + assert_matches_type(RowListResponse, row, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Openlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `inference_pipeline_id` but received ''"): + client.inference_pipelines.rows.with_raw_response.list( + inference_pipeline_id="", + ) + class TestAsyncRows: parametrize = pytest.mark.parametrize( @@ -146,3 +208,65 @@ async def test_path_params_update(self, async_client: AsyncOpenlayer) -> None: inference_id="inferenceId", row={}, ) + + @parametrize + async def test_method_list(self, async_client: AsyncOpenlayer) -> None: + row = await async_client.inference_pipelines.rows.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert_matches_type(RowListResponse, row, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -> None: + row = await async_client.inference_pipelines.rows.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + asc=True, + page=1, + per_page=1, + sort_column="sortColumn", + column_filters=[ + { + "measurement": "openlayer_token_set", + "operator": "contains_none", + "value": ["cat"], + } + ], + exclude_row_id_list=[0], + not_search_query_and=["string"], + not_search_query_or=["string"], + row_id_list=[0], + search_query_and=["string"], + search_query_or=["string"], + ) + assert_matches_type(RowListResponse, row, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpenlayer) -> None: + response = await async_client.inference_pipelines.rows.with_raw_response.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + row = await response.parse() + assert_matches_type(RowListResponse, row, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpenlayer) -> None: + async with async_client.inference_pipelines.rows.with_streaming_response.list( + inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + row = await response.parse() + assert_matches_type(RowListResponse, row, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncOpenlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `inference_pipeline_id` but received ''"): + await async_client.inference_pipelines.rows.with_raw_response.list( + inference_pipeline_id="", + )