Skip to content

release: 0.2.0-alpha.52 #450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ jobs:
lint:
name: lint
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.2.0-alpha.51"
".": "0.2.0-alpha.52"
}
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@ 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.52 (2025-04-14)

Full Changelog: [v0.2.0-alpha.51...v0.2.0-alpha.52](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.51...v0.2.0-alpha.52)

### Features

* feat: allow publish without ssl verification ([24dbdef](https://github.com/openlayer-ai/openlayer-python/commit/24dbdef53ccb988e6cd807094ae2a15a4e40fa7f))


### Bug Fixes

* **perf:** optimize some hot paths ([badc2bb](https://github.com/openlayer-ai/openlayer-python/commit/badc2bb1b915c70045a4f9150792746788a61b79))
* **perf:** skip traversing types for NotGiven values ([afb0108](https://github.com/openlayer-ai/openlayer-python/commit/afb01083b15f4b4f4878176f2d34a74c72ef3c57))


### Chores

* **internal:** expand CI branch coverage ([121cc4c](https://github.com/openlayer-ai/openlayer-python/commit/121cc4cf1e7276aba8fde9ca216db17242b641ed))
* **internal:** reduce CI branch coverage ([05f20c8](https://github.com/openlayer-ai/openlayer-python/commit/05f20c8ff1b471a9a3f3d6f688d0cc7d78cf680b))
* **internal:** slight transform perf improvement ([#448](https://github.com/openlayer-ai/openlayer-python/issues/448)) ([3c5cd0a](https://github.com/openlayer-ai/openlayer-python/commit/3c5cd0a60b3d33248568075ccb3576536d5cfe7e))
* **tests:** improve enum examples ([#449](https://github.com/openlayer-ai/openlayer-python/issues/449)) ([3508728](https://github.com/openlayer-ai/openlayer-python/commit/350872865c9f574048c4d6acb112ee72f81e5046))

## 0.2.0-alpha.51 (2025-04-04)

Full Changelog: [v0.2.0-alpha.50...v0.2.0-alpha.51](https://github.com/openlayer-ai/openlayer-python/compare/v0.2.0-alpha.50...v0.2.0-alpha.51)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "openlayer"
version = "0.2.0-alpha.51"
version = "0.2.0-alpha.52"
description = "The official Python library for the openlayer API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
47 changes: 46 additions & 1 deletion src/openlayer/_utils/_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
import pathlib
from typing import Any, Mapping, TypeVar, cast
from datetime import date, datetime
from typing_extensions import Literal, get_args, override, get_type_hints
from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints

import anyio
import pydantic

from ._utils import (
is_list,
is_given,
lru_cache,
is_mapping,
is_iterable,
)
Expand Down Expand Up @@ -108,6 +110,7 @@ class Params(TypedDict, total=False):
return cast(_T, transformed)


@lru_cache(maxsize=8096)
def _get_annotated_type(type_: type) -> type | None:
"""If the given type is an `Annotated` type then it is returned, if not `None` is returned.

Expand Down Expand Up @@ -142,6 +145,10 @@ def _maybe_transform_key(key: str, type_: type) -> str:
return key


def _no_transform_needed(annotation: type) -> bool:
return annotation == float or annotation == int


def _transform_recursive(
data: object,
*,
Expand Down Expand Up @@ -184,6 +191,15 @@ def _transform_recursive(
return cast(object, data)

inner_type = extract_type_arg(stripped_type, 0)
if _no_transform_needed(inner_type):
# for some types there is no need to transform anything, so we can get a small
# perf boost from skipping that work.
#
# but we still need to convert to a list to ensure the data is json-serializable
if is_list(data):
return data
return list(data)

return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]

if is_union_type(stripped_type):
Expand Down Expand Up @@ -245,6 +261,11 @@ def _transform_typeddict(
result: dict[str, object] = {}
annotations = get_type_hints(expected_type, include_extras=True)
for key, value in data.items():
if not is_given(value):
# we don't need to include `NotGiven` values here as they'll
# be stripped out before the request is sent anyway
continue

type_ = annotations.get(key)
if type_ is None:
# we do not have a type annotation for this field, leave it as is
Expand Down Expand Up @@ -332,6 +353,15 @@ async def _async_transform_recursive(
return cast(object, data)

inner_type = extract_type_arg(stripped_type, 0)
if _no_transform_needed(inner_type):
# for some types there is no need to transform anything, so we can get a small
# perf boost from skipping that work.
#
# but we still need to convert to a list to ensure the data is json-serializable
if is_list(data):
return data
return list(data)

return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]

if is_union_type(stripped_type):
Expand Down Expand Up @@ -393,10 +423,25 @@ async def _async_transform_typeddict(
result: dict[str, object] = {}
annotations = get_type_hints(expected_type, include_extras=True)
for key, value in data.items():
if not is_given(value):
# we don't need to include `NotGiven` values here as they'll
# be stripped out before the request is sent anyway
continue

type_ = annotations.get(key)
if type_ is None:
# we do not have a type annotation for this field, leave it as is
result[key] = value
else:
result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_)
return result


@lru_cache(maxsize=8096)
def get_type_hints(
obj: Any,
globalns: dict[str, Any] | None = None,
localns: Mapping[str, Any] | None = None,
include_extras: bool = False,
) -> dict[str, Any]:
return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras)
2 changes: 2 additions & 0 deletions src/openlayer/_utils/_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
get_origin,
)

from ._utils import lru_cache
from .._types import InheritsGeneric
from .._compat import is_union as _is_union

Expand Down Expand Up @@ -66,6 +67,7 @@ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]:


# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]]
@lru_cache(maxsize=8096)
def strip_annotated_type(typ: type) -> type:
if is_required_type(typ) or is_annotated_type(typ):
return strip_annotated_type(cast(type, get_args(typ)[0]))
Expand Down
2 changes: 1 addition & 1 deletion src/openlayer/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "openlayer"
__version__ = "0.2.0-alpha.51" # x-release-please-version
__version__ = "0.2.0-alpha.52" # x-release-please-version
41 changes: 27 additions & 14 deletions src/openlayer/lib/tracing/tracer.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
"""Module with the logic to create and manage traces and steps."""

import time
import asyncio
import contextvars
import inspect
import logging
import time
from contextlib import contextmanager
import contextvars
from typing import Any, Dict, List, Tuple, Optional, Awaitable, Generator
from functools import wraps
from typing import Any, Awaitable, Dict, Generator, List, Optional, Tuple
from contextlib import contextmanager

from . import enums, steps, traces
from .. import utils
from ..._client import Openlayer
from ..._base_client import DefaultHttpxClient
from ...types.inference_pipelines.data_stream_params import ConfigLlmData
from .. import utils
from . import enums, steps, traces

logger = logging.getLogger(__name__)

_publish = utils.get_env_variable("OPENLAYER_DISABLE_PUBLISH") != "true"
TRUE_LIST = ["true", "on", "1"]

_publish = utils.get_env_variable("OPENLAYER_DISABLE_PUBLISH") not in TRUE_LIST
_verify_ssl = utils.get_env_variable("OPENLAYER_VERIFY_SSL").lower() in TRUE_LIST
_client = None
if _publish:
_client = Openlayer()
if _verify_ssl:
_client = Openlayer()
else:
_client = Openlayer(
http_client=DefaultHttpxClient(
verify=False,
),
)

_current_step = contextvars.ContextVar("current_step")
_current_trace = contextvars.ContextVar("current_trace")
Expand Down Expand Up @@ -142,8 +153,8 @@ def trace(
Examples
--------

To trace a function, simply decorate it with the ``@trace()`` decorator. By doing so,
the functions inputs, outputs, and metadata will be automatically logged to your
To trace a function, simply decorate it with the ``@trace()`` decorator. By doing
so, the functions inputs, outputs, and metadata will be automatically logged to your
Openlayer project.

>>> import os
Expand Down Expand Up @@ -204,7 +215,8 @@ def wrapper(*func_args, **func_kwargs):
log_context(inputs.get(context_kwarg))
else:
logger.warning(
"Context kwarg `%s` not found in inputs of the current function.",
"Context kwarg `%s` not found in inputs of the "
"current function.",
context_kwarg,
)

Expand Down Expand Up @@ -235,8 +247,8 @@ def trace_async(
Examples
--------

To trace a function, simply decorate it with the ``@trace()`` decorator. By doing so,
the functions inputs, outputs, and metadata will be automatically logged to your
To trace a function, simply decorate it with the ``@trace()`` decorator. By doing
so, the functions inputs, outputs, and metadata will be automatically logged to your
Openlayer project.

>>> import os
Expand Down Expand Up @@ -297,7 +309,8 @@ async def wrapper(*func_args, **func_kwargs):
log_context(inputs.get(context_kwarg))
else:
logger.warning(
"Context kwarg `%s` not found in inputs of the current function.",
"Context kwarg `%s` not found in inputs of the "
"current function.",
context_kwarg,
)

Expand Down
4 changes: 2 additions & 2 deletions tests/api_resources/commits/test_test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_method_list_with_all_params(self, client: Openlayer) -> None:
include_archived=True,
page=1,
per_page=1,
status="running",
status="passing",
type="integrity",
)
assert_matches_type(TestResultListResponse, test_result, path=["response"])
Expand Down Expand Up @@ -85,7 +85,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -
include_archived=True,
page=1,
per_page=1,
status="running",
status="passing",
type="integrity",
)
assert_matches_type(TestResultListResponse, test_result, path=["response"])
Expand Down
4 changes: 2 additions & 2 deletions tests/api_resources/inference_pipelines/test_test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_method_list_with_all_params(self, client: Openlayer) -> None:
inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
page=1,
per_page=1,
status="running",
status="passing",
type="integrity",
)
assert_matches_type(TestResultListResponse, test_result, path=["response"])
Expand Down Expand Up @@ -83,7 +83,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncOpenlayer) -
inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
page=1,
per_page=1,
status="running",
status="passing",
type="integrity",
)
assert_matches_type(TestResultListResponse, test_result, path=["response"])
Expand Down
21 changes: 20 additions & 1 deletion tests/test_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import pytest

from openlayer._types import Base64FileInput
from openlayer._types import NOT_GIVEN, Base64FileInput
from openlayer._utils import (
PropertyInfo,
transform as _transform,
Expand Down Expand Up @@ -432,3 +432,22 @@ async def test_base64_file_input(use_async: bool) -> None:
assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == {
"foo": "SGVsbG8sIHdvcmxkIQ=="
} # type: ignore[comparison-overlap]


@parametrize
@pytest.mark.asyncio
async def test_transform_skipping(use_async: bool) -> None:
# lists of ints are left as-is
data = [1, 2, 3]
assert await transform(data, List[int], use_async) is data

# iterables of ints are converted to a list
data = iter([1, 2, 3])
assert await transform(data, Iterable[int], use_async) == [1, 2, 3]


@parametrize
@pytest.mark.asyncio
async def test_strips_notgiven(use_async: bool) -> None:
assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"}
assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {}