Skip to content
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

release: 0.0.8 #5

Merged
merged 4 commits into from
May 16, 2024
Merged
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
16 changes: 3 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -25,20 +25,10 @@ jobs:
RYE_INSTALL_OPTION: '--yes'

- name: Install dependencies
run: |
rye sync --all-features

- name: Run ruff
run: |
rye run check:ruff
run: rye sync --all-features

- name: Run type checking
run: |
rye run typecheck

- name: Ensure importable
run: |
rye run python -c 'import honcho'
- name: Run lints
run: ./scripts/lint
test:
name: test
runs-on: ubuntu-latest
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.0.8-alpha.1"
".": "0.0.8"
}
2 changes: 1 addition & 1 deletion .stats.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
configured_endpoints: 38
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/plastic-labs%2FHoncho-a370506bdacaf58567fea52cb6312d99b0e211dd67c8d1ffb896fcf6abfee16b.yml
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/plastic-labs%2Fhoncho-a370506bdacaf58567fea52cb6312d99b0e211dd67c8d1ffb896fcf6abfee16b.yml
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 0.0.8 (2024-05-16)

Full Changelog: [v0.0.8-alpha.1...v0.0.8](https://github.com/plastic-labs/honcho-python/compare/v0.0.8-alpha.1...v0.0.8)

### Features

* **api:** update via SDK Studio ([#4](https://github.com/plastic-labs/honcho-python/issues/4)) ([c8a8dbc](https://github.com/plastic-labs/honcho-python/commit/c8a8dbcf788c346ee2427762669974eb072027f0))
* **api:** update via SDK Studio ([#6](https://github.com/plastic-labs/honcho-python/issues/6)) ([5f224f6](https://github.com/plastic-labs/honcho-python/commit/5f224f611904a055860184f1d83628316ae3cf30))
* **api:** update via SDK Studio ([#7](https://github.com/plastic-labs/honcho-python/issues/7)) ([9488493](https://github.com/plastic-labs/honcho-python/commit/948849360d12d434196372b5614467ee6daf1860))

## 0.0.8-alpha.1 (2024-05-09)

Full Changelog: [v0.0.1-alpha.0...v0.0.8-alpha.1](https://github.com/plastic-labs/honcho-python/compare/v0.0.1-alpha.0...v0.0.8-alpha.1)
27 changes: 27 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Security Policy

## Reporting Security Issues

This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.

To report a security issue, please contact the Stainless team at security@stainlessapi.com.

## Responsible Disclosure

We appreciate the efforts of security researchers and individuals who help us maintain the security of
SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible
disclosure practices by allowing us a reasonable amount of time to investigate and address the issue
before making any information public.

## Reporting Non-SDK Related Security Issues

If you encounter security issues that are not directly related to SDKs but pertain to the services
or products provided by Honcho please follow the respective company's security reporting guidelines.

### Honcho Terms and Policies

Please contact hello@plasticlabs.ai for any questions or concerns regarding security of our services.

---

Thank you for helping us keep the SDKs and systems they interact with secure.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "honcho-ai"
version = "0.0.8-alpha.1"
version = "0.0.8"
description = "The official Python library for the honcho API"
dynamic = ["readme"]
license = "Apache-2.0"
4 changes: 2 additions & 2 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
@@ -59,9 +59,9 @@ pluggy==1.3.0
# via pytest
py==1.11.0
# via pytest
pydantic==2.4.2
pydantic==2.7.1
# via honcho-ai
pydantic-core==2.10.1
pydantic-core==2.18.2
# via pydantic
pyright==1.1.359
pytest==7.1.1
4 changes: 2 additions & 2 deletions requirements.lock
Original file line number Diff line number Diff line change
@@ -29,9 +29,9 @@ httpx==0.25.2
idna==3.4
# via anyio
# via httpx
pydantic==2.4.2
pydantic==2.7.1
# via honcho-ai
pydantic-core==2.10.1
pydantic-core==2.18.2
# via pydantic
sniffio==1.3.0
# via anyio
2 changes: 1 addition & 1 deletion scripts/format
Original file line number Diff line number Diff line change
@@ -4,5 +4,5 @@ set -e

cd "$(dirname "$0")/.."

echo "==> Running formatters"
rye run format

4 changes: 4 additions & 0 deletions scripts/lint
Original file line number Diff line number Diff line change
@@ -4,5 +4,9 @@ set -e

cd "$(dirname "$0")/.."

echo "==> Running lints"
rye run lint

echo "==> Making sure it imports"
rye run python -c 'import honcho'

1 change: 0 additions & 1 deletion scripts/test
Original file line number Diff line number Diff line change
@@ -52,6 +52,5 @@ else
echo
fi

# Run tests
echo "==> Running tests"
rye run pytest "$@"
18 changes: 7 additions & 11 deletions src/honcho/_client.py
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@
)
from ._version import __version__
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import HonchoError, APIStatusError
from ._exceptions import APIStatusError
from ._base_client import (
DEFAULT_MAX_RETRIES,
SyncAPIClient,
@@ -57,7 +57,7 @@ class Honcho(SyncAPIClient):
with_streaming_response: HonchoWithStreamedResponse

# client options
api_key: str
api_key: str | None

_environment: Literal["local", "demo"] | NotGiven

@@ -91,10 +91,6 @@ def __init__(
"""
if api_key is None:
api_key = os.environ.get("HONCHO_AUTH_TOKEN")
if api_key is None:
raise HonchoError(
"The api_key client option must be set either by passing api_key to the client or by setting the HONCHO_AUTH_TOKEN environment variable"
)
self.api_key = api_key

self._environment = environment
@@ -147,6 +143,8 @@ def qs(self) -> Querystring:
@override
def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
if api_key is None:
return {}
return {"Authorization": f"Bearer {api_key}"}

@property
@@ -251,7 +249,7 @@ class AsyncHoncho(AsyncAPIClient):
with_streaming_response: AsyncHonchoWithStreamedResponse

# client options
api_key: str
api_key: str | None

_environment: Literal["local", "demo"] | NotGiven

@@ -285,10 +283,6 @@ def __init__(
"""
if api_key is None:
api_key = os.environ.get("HONCHO_AUTH_TOKEN")
if api_key is None:
raise HonchoError(
"The api_key client option must be set either by passing api_key to the client or by setting the HONCHO_AUTH_TOKEN environment variable"
)
self.api_key = api_key

self._environment = environment
@@ -341,6 +335,8 @@ def qs(self) -> Querystring:
@override
def auth_headers(self) -> dict[str, str]:
api_key = self.api_key
if api_key is None:
return {}
return {"Authorization": f"Bearer {api_key}"}

@property
20 changes: 16 additions & 4 deletions src/honcho/_models.py
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@
from ._constants import RAW_RESPONSE_HEADER

if TYPE_CHECKING:
from pydantic_core.core_schema import ModelField, ModelFieldsSchema
from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema

__all__ = ["BaseModel", "GenericModel"]

@@ -251,7 +251,9 @@ def model_dump(
exclude_defaults: bool = False,
exclude_none: bool = False,
round_trip: bool = False,
warnings: bool = True,
warnings: bool | Literal["none", "warn", "error"] = True,
context: dict[str, Any] | None = None,
serialize_as_any: bool = False,
) -> dict[str, Any]:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump

@@ -279,6 +281,10 @@ def model_dump(
raise ValueError("round_trip is only supported in Pydantic v2")
if warnings != True:
raise ValueError("warnings is only supported in Pydantic v2")
if context is not None:
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
return super().dict( # pyright: ignore[reportDeprecated]
include=include,
exclude=exclude,
@@ -300,7 +306,9 @@ def model_dump_json(
exclude_defaults: bool = False,
exclude_none: bool = False,
round_trip: bool = False,
warnings: bool = True,
warnings: bool | Literal["none", "warn", "error"] = True,
context: dict[str, Any] | None = None,
serialize_as_any: bool = False,
) -> str:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json

@@ -324,6 +332,10 @@ def model_dump_json(
raise ValueError("round_trip is only supported in Pydantic v2")
if warnings != True:
raise ValueError("warnings is only supported in Pydantic v2")
if context is not None:
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
return super().json( # type: ignore[reportDeprecated]
indent=indent,
include=include,
@@ -550,7 +562,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
field_schema = field["schema"]

if field_schema["type"] == "literal":
for entry in field_schema["expected"]:
for entry in cast("LiteralSchema", field_schema)["expected"]:
if isinstance(entry, str):
mapping[entry] = variant
else:
2 changes: 1 addition & 1 deletion src/honcho/_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__ = "honcho"
__version__ = "0.0.8-alpha.1" # x-release-please-version
__version__ = "0.0.8" # x-release-please-version
31 changes: 24 additions & 7 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -17,9 +17,10 @@
from pydantic import ValidationError

from honcho import Honcho, AsyncHoncho, APIResponseValidationError
from honcho._types import Omit
from honcho._models import BaseModel, FinalRequestOptions
from honcho._constants import RAW_RESPONSE_HEADER
from honcho._exceptions import HonchoError, APIStatusError, APITimeoutError, APIResponseValidationError
from honcho._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError
from honcho._base_client import DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, make_request_options

from .utils import update_env
@@ -326,9 +327,17 @@ def test_validate_headers(self) -> None:
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("Authorization") == f"Bearer {api_key}"

with pytest.raises(HonchoError):
client2 = Honcho(base_url=base_url, api_key=None, _strict_response_validation=True)
_ = client2
client2 = Honcho(base_url=base_url, api_key=None, _strict_response_validation=True)
with pytest.raises(
TypeError,
match="Could not resolve authentication method. Expected the api_key to be set. Or for the `Authorization` headers to be explicitly omitted",
):
client2._build_request(FinalRequestOptions(method="get", url="/foo"))

request2 = client2._build_request(
FinalRequestOptions(method="get", url="/foo", headers={"Authorization": Omit()})
)
assert request2.headers.get("Authorization") is None

def test_default_query_option(self) -> None:
client = Honcho(
@@ -1017,9 +1026,17 @@ def test_validate_headers(self) -> None:
request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("Authorization") == f"Bearer {api_key}"

with pytest.raises(HonchoError):
client2 = AsyncHoncho(base_url=base_url, api_key=None, _strict_response_validation=True)
_ = client2
client2 = AsyncHoncho(base_url=base_url, api_key=None, _strict_response_validation=True)
with pytest.raises(
TypeError,
match="Could not resolve authentication method. Expected the api_key to be set. Or for the `Authorization` headers to be explicitly omitted",
):
client2._build_request(FinalRequestOptions(method="get", url="/foo"))

request2 = client2._build_request(
FinalRequestOptions(method="get", url="/foo", headers={"Authorization": Omit()})
)
assert request2.headers.get("Authorization") is None

def test_default_query_option(self) -> None:
client = AsyncHoncho(
8 changes: 4 additions & 4 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ class NestedModel(BaseModel):

# mismatched types
m = NestedModel.construct(nested="hello!")
assert m.nested == "hello!"
assert cast(Any, m.nested) == "hello!"


def test_optional_nested_model() -> None:
@@ -48,7 +48,7 @@ class NestedModel(BaseModel):
# mismatched types
m3 = NestedModel.construct(nested={"foo"})
assert isinstance(cast(Any, m3.nested), set)
assert m3.nested == {"foo"}
assert cast(Any, m3.nested) == {"foo"}


def test_list_nested_model() -> None:
@@ -323,7 +323,7 @@ class Model(BaseModel):
assert len(m.items) == 2
assert isinstance(m.items[0], Submodel1)
assert m.items[0].level == -1
assert m.items[1] == 156
assert cast(Any, m.items[1]) == 156


def test_union_of_lists() -> None:
@@ -355,7 +355,7 @@ class Model(BaseModel):
assert len(m.items) == 2
assert isinstance(m.items[0], SubModel1)
assert m.items[0].level == -1
assert m.items[1] == 156
assert cast(Any, m.items[1]) == 156


def test_dict_of_union() -> None:
22 changes: 12 additions & 10 deletions tests/test_transform.py
Original file line number Diff line number Diff line change
@@ -260,20 +260,22 @@ class MyModel(BaseModel):
@parametrize
@pytest.mark.asyncio
async def test_pydantic_model_to_dictionary(use_async: bool) -> None:
assert await transform(MyModel(foo="hi!"), Any, use_async) == {"foo": "hi!"}
assert await transform(MyModel.construct(foo="hi!"), Any, use_async) == {"foo": "hi!"}
assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"}
assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"}


@parametrize
@pytest.mark.asyncio
async def test_pydantic_empty_model(use_async: bool) -> None:
assert await transform(MyModel.construct(), Any, use_async) == {}
assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {}


@parametrize
@pytest.mark.asyncio
async def test_pydantic_unknown_field(use_async: bool) -> None:
assert await transform(MyModel.construct(my_untyped_field=True), Any, use_async) == {"my_untyped_field": True}
assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == {
"my_untyped_field": True
}


@parametrize
@@ -285,7 +287,7 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None:
params = await transform(model, Any, use_async)
else:
params = await transform(model, Any, use_async)
assert params == {"foo": True}
assert cast(Any, params) == {"foo": True}


@parametrize
@@ -297,7 +299,7 @@ async def test_pydantic_mismatched_object_type(use_async: bool) -> None:
params = await transform(model, Any, use_async)
else:
params = await transform(model, Any, use_async)
assert params == {"foo": {"hello": "world"}}
assert cast(Any, params) == {"foo": {"hello": "world"}}


class ModelNestedObjects(BaseModel):
@@ -309,7 +311,7 @@ class ModelNestedObjects(BaseModel):
async def test_pydantic_nested_objects(use_async: bool) -> None:
model = ModelNestedObjects.construct(nested={"foo": "stainless"})
assert isinstance(model.nested, MyModel)
assert await transform(model, Any, use_async) == {"nested": {"foo": "stainless"}}
assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}}


class ModelWithDefaultField(BaseModel):
@@ -325,19 +327,19 @@ async def test_pydantic_default_field(use_async: bool) -> None:
model = ModelWithDefaultField.construct()
assert model.with_none_default is None
assert model.with_str_default == "foo"
assert await transform(model, Any, use_async) == {}
assert cast(Any, await transform(model, Any, use_async)) == {}

# should be included when the default value is explicitly given
model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo")
assert model.with_none_default is None
assert model.with_str_default == "foo"
assert await transform(model, Any, use_async) == {"with_none_default": None, "with_str_default": "foo"}
assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"}

# should be included when a non-default value is explicitly given
model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz")
assert model.with_none_default == "bar"
assert model.with_str_default == "baz"
assert await transform(model, Any, use_async) == {"with_none_default": "bar", "with_str_default": "baz"}
assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"}


class TypedDictIterableUnion(TypedDict):