diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2b28d6e..34dc535 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.8.1" + ".": "0.8.2" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 38d124b..5c1470b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 19 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-84945582139b11633f792c1052a33e6af9cafc96bbafc2902a905312d14c4cc1.yml -openapi_spec_hash: c77be216626b789a543529a6de56faed +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-5e4716f7fce42bbcecc7ecb699c1c467ae778d74d558f7a260d531e2af1a7f30.yml +openapi_spec_hash: f545dcef9001b00c2604e3dcc6a12f7a config_hash: 65328ff206b8c0168c915914506d9dba diff --git a/CHANGELOG.md b/CHANGELOG.md index eed35ac..9a362ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.8.2 (2025-07-23) + +Full Changelog: [v0.8.1...v0.8.2](https://github.com/onkernel/kernel-python-sdk/compare/v0.8.1...v0.8.2) + +### Features + +* **api:** add action name to the response to invoke ([1a485b2](https://github.com/onkernel/kernel-python-sdk/commit/1a485b2ddd3cdbf97fcb67f1d389c07ce0a51d8e)) + + +### Bug Fixes + +* **parsing:** ignore empty metadata ([d839a20](https://github.com/onkernel/kernel-python-sdk/commit/d839a20c1bb2c0e35aff6fa59196cec9e725d346)) +* **parsing:** parse extra field types ([cb880bc](https://github.com/onkernel/kernel-python-sdk/commit/cb880bc6796acf8e44560580829229fb140586c9)) + ## 0.8.1 (2025-07-21) Full Changelog: [v0.8.0...v0.8.1](https://github.com/onkernel/kernel-python-sdk/compare/v0.8.0...v0.8.1) diff --git a/pyproject.toml b/pyproject.toml index 83d7c72..5a5d20e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kernel" -version = "0.8.1" +version = "0.8.2" description = "The official Python library for the kernel API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/kernel/_models.py b/src/kernel/_models.py index 528d568..b8387ce 100644 --- a/src/kernel/_models.py +++ b/src/kernel/_models.py @@ -208,14 +208,18 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] else: fields_values[name] = field_get_default(field) + extra_field_type = _get_extra_fields_type(__cls) + _extra = {} for key, value in values.items(): if key not in model_fields: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + if PYDANTIC_V2: - _extra[key] = value + _extra[key] = parsed else: _fields_set.add(key) - fields_values[key] = value + fields_values[key] = parsed object.__setattr__(m, "__dict__", fields_values) @@ -370,6 +374,23 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if not PYDANTIC_V2: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None + + def is_basemodel(type_: type) -> bool: """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" if is_union(type_): @@ -439,7 +460,7 @@ def construct_type(*, value: object, type_: object, metadata: Optional[List[Any] type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` - if metadata is not None: + if metadata is not None and len(metadata) > 0: meta: tuple[Any, ...] = tuple(metadata) elif is_annotated_type(type_): meta = get_args(type_)[1:] diff --git a/src/kernel/_version.py b/src/kernel/_version.py index 8e3e520..0401c33 100644 --- a/src/kernel/_version.py +++ b/src/kernel/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "kernel" -__version__ = "0.8.1" # x-release-please-version +__version__ = "0.8.2" # x-release-please-version diff --git a/src/kernel/types/invocation_create_response.py b/src/kernel/types/invocation_create_response.py index d58f262..21fbcf3 100644 --- a/src/kernel/types/invocation_create_response.py +++ b/src/kernel/types/invocation_create_response.py @@ -12,6 +12,9 @@ class InvocationCreateResponse(BaseModel): id: str """ID of the invocation""" + action_name: str + """Name of the action invoked""" + status: Literal["queued", "running", "succeeded", "failed"] """Status of the invocation""" diff --git a/tests/test_models.py b/tests/test_models.py index 1e7293a..72f55a8 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone from typing_extensions import Literal, Annotated, TypeAliasType @@ -934,3 +934,30 @@ class Type2(BaseModel): ) assert isinstance(model, Type1) assert isinstance(model.value, InnerType2) + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="this is only supported in pydantic v2 for now") +def test_extra_properties() -> None: + class Item(BaseModel): + prop: int + + class Model(BaseModel): + __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + other: str + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Item: ... + + model = construct_type( + type_=Model, + value={ + "a": {"prop": 1}, + "other": "foo", + }, + ) + assert isinstance(model, Model) + assert model.a.prop == 1 + assert isinstance(model.a, Item) + assert model.other == "foo"