Skip to content

Commit dd74edd

Browse files
release: 0.10.0 (#88)
Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
1 parent 20899c0 commit dd74edd

23 files changed

+1085
-219
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.9.0"
2+
".": "0.10.0"
33
}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 34
1+
configured_endpoints: 36
22
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/contextual-ai%2Fsunrise-c8152db455001be3f09a3bc60d63711699d2c2a4ea5f7bbc1d71726efda0fd9b.yml
33
openapi_spec_hash: 97719df292ca220de5d35d36f9756b95
4-
config_hash: ae81af9b7eb88a788a80bcf3480e0b6b
4+
config_hash: fdaf751580ba8a60e222e560847af1ac

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Changelog
22

3+
## 0.10.0 (2025-11-11)
4+
5+
Full Changelog: [v0.9.0...v0.10.0](https://github.com/ContextualAI/contextual-client-python/compare/v0.9.0...v0.10.0)
6+
7+
### Features
8+
9+
* **api:** update via SDK Studio ([921ea1c](https://github.com/ContextualAI/contextual-client-python/commit/921ea1c3e6e4432638c535c7e413c92d2e1398f5))
10+
11+
12+
### Bug Fixes
13+
14+
* **client:** close streams without requiring full consumption ([3f212eb](https://github.com/ContextualAI/contextual-client-python/commit/3f212ebb31085b404c72d827f1d6992dd4bed24c))
15+
* compat with Python 3.14 ([6f2d195](https://github.com/ContextualAI/contextual-client-python/commit/6f2d1958bb397cedb94f970c361e617e01c3fdf6))
16+
17+
18+
### Chores
19+
20+
* **internal/tests:** avoid race condition with implicit client cleanup ([f7f3568](https://github.com/ContextualAI/contextual-client-python/commit/f7f35681c6ac40661872fbdc3159e79ff764d135))
21+
* **internal:** grammar fix (it's -&gt; its) ([12b822d](https://github.com/ContextualAI/contextual-client-python/commit/12b822dcede4ba84a7889775254f8b02b311ae5f))
22+
* **package:** drop Python 3.8 support ([c2ddf6a](https://github.com/ContextualAI/contextual-client-python/commit/c2ddf6a2d51ff845cb2dcd872dc37b934ef97199))
23+
324
## 0.9.0 (2025-10-28)
425

526
Full Changelog: [v0.8.0...v0.9.0](https://github.com/ContextualAI/contextual-client-python/compare/v0.8.0...v0.9.0)

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<!-- prettier-ignore -->
44
[![PyPI version](https://img.shields.io/pypi/v/contextual-client.svg?label=pypi%20(stable))](https://pypi.org/project/contextual-client/)
55

6-
The Contextual AI Python library provides convenient access to the Contextual AI REST API from any Python 3.8+
6+
The Contextual AI Python library provides convenient access to the Contextual AI REST API from any Python 3.9+
77
application. The library includes type definitions for all request params and response fields,
88
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
99

@@ -467,7 +467,7 @@ print(contextual.__version__)
467467

468468
## Requirements
469469

470-
Python 3.8 or higher.
470+
Python 3.9 or higher.
471471

472472
## Contributing
473473

api.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ Methods:
4545
- <code title="get /datastores/{datastore_id}/documents/{document_id}/metadata">client.datastores.documents.<a href="./src/contextual/resources/datastores/documents.py">metadata</a>(document_id, \*, datastore_id) -> <a href="./src/contextual/types/datastores/document_metadata.py">DocumentMetadata</a></code>
4646
- <code title="put /datastores/{datastore_id}/documents/{document_id}/metadata">client.datastores.documents.<a href="./src/contextual/resources/datastores/documents.py">set_metadata</a>(document_id, \*, datastore_id, \*\*<a href="src/contextual/types/datastores/document_set_metadata_params.py">params</a>) -> <a href="./src/contextual/types/datastores/document_metadata.py">DocumentMetadata</a></code>
4747

48+
## Contents
49+
50+
Types:
51+
52+
```python
53+
from contextual.types.datastores import ContentListResponse, ContentMetadataResponse
54+
```
55+
56+
Methods:
57+
58+
- <code title="get /datastores/{datastore_id}/contents">client.datastores.contents.<a href="./src/contextual/resources/datastores/contents.py">list</a>(datastore_id, \*\*<a href="src/contextual/types/datastores/content_list_params.py">params</a>) -> <a href="./src/contextual/types/datastores/content_list_response.py">SyncContentsPage[ContentListResponse]</a></code>
59+
- <code title="get /datastores/{datastore_id}/contents/{content_id}/metadata">client.datastores.contents.<a href="./src/contextual/resources/datastores/contents.py">metadata</a>(content_id, \*, datastore_id, \*\*<a href="src/contextual/types/datastores/content_metadata_params.py">params</a>) -> <a href="./src/contextual/types/datastores/content_metadata_response.py">ContentMetadataResponse</a></code>
60+
4861
# Agents
4962

5063
Types:

pyproject.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "contextual-client"
3-
version = "0.9.0"
3+
version = "0.10.0"
44
description = "The official Python library for the Contextual AI API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"
@@ -15,11 +15,10 @@ dependencies = [
1515
"distro>=1.7.0, <2",
1616
"sniffio",
1717
]
18-
requires-python = ">= 3.8"
18+
requires-python = ">= 3.9"
1919
classifiers = [
2020
"Typing :: Typed",
2121
"Intended Audience :: Developers",
22-
"Programming Language :: Python :: 3.8",
2322
"Programming Language :: Python :: 3.9",
2423
"Programming Language :: Python :: 3.10",
2524
"Programming Language :: Python :: 3.11",
@@ -141,7 +140,7 @@ filterwarnings = [
141140
# there are a couple of flags that are still disabled by
142141
# default in strict mode as they are experimental and niche.
143142
typeCheckingMode = "strict"
144-
pythonVersion = "3.8"
143+
pythonVersion = "3.9"
145144

146145
exclude = [
147146
"_dev",

src/contextual/_models.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
import inspect
5+
import weakref
56
from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
67
from datetime import date, datetime
78
from typing_extensions import (
@@ -573,6 +574,9 @@ class CachedDiscriminatorType(Protocol):
573574
__discriminator__: DiscriminatorDetails
574575

575576

577+
DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary()
578+
579+
576580
class DiscriminatorDetails:
577581
field_name: str
578582
"""The name of the discriminator field in the variant class, e.g.
@@ -615,8 +619,9 @@ def __init__(
615619

616620

617621
def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:
618-
if isinstance(union, CachedDiscriminatorType):
619-
return union.__discriminator__
622+
cached = DISCRIMINATOR_CACHE.get(union)
623+
if cached is not None:
624+
return cached
620625

621626
discriminator_field_name: str | None = None
622627

@@ -669,7 +674,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
669674
discriminator_field=discriminator_field_name,
670675
discriminator_alias=discriminator_alias,
671676
)
672-
cast(CachedDiscriminatorType, union).__discriminator__ = details
677+
DISCRIMINATOR_CACHE.setdefault(union, details)
673678
return details
674679

675680

src/contextual/_streaming.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,8 @@ def __stream__(self) -> Iterator[_T]:
5757
for sse in iterator:
5858
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
5959

60-
# Ensure the entire stream is consumed
61-
for _sse in iterator:
62-
...
60+
# As we might not fully consume the response stream, we need to close it explicitly
61+
response.close()
6362

6463
def __enter__(self) -> Self:
6564
return self
@@ -121,9 +120,8 @@ async def __stream__(self) -> AsyncIterator[_T]:
121120
async for sse in iterator:
122121
yield process_data(data=sse.json(), cast_to=cast_to, response=response)
123122

124-
# Ensure the entire stream is consumed
125-
async for _sse in iterator:
126-
...
123+
# As we might not fully consume the response stream, we need to close it explicitly
124+
await response.aclose()
127125

128126
async def __aenter__(self) -> Self:
129127
return self

src/contextual/_utils/_sync.py

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
from __future__ import annotations
22

3-
import sys
43
import asyncio
54
import functools
6-
import contextvars
7-
from typing import Any, TypeVar, Callable, Awaitable
5+
from typing import TypeVar, Callable, Awaitable
86
from typing_extensions import ParamSpec
97

108
import anyio
@@ -15,34 +13,11 @@
1513
T_ParamSpec = ParamSpec("T_ParamSpec")
1614

1715

18-
if sys.version_info >= (3, 9):
19-
_asyncio_to_thread = asyncio.to_thread
20-
else:
21-
# backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
22-
# for Python 3.8 support
23-
async def _asyncio_to_thread(
24-
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
25-
) -> Any:
26-
"""Asynchronously run function *func* in a separate thread.
27-
28-
Any *args and **kwargs supplied for this function are directly passed
29-
to *func*. Also, the current :class:`contextvars.Context` is propagated,
30-
allowing context variables from the main thread to be accessed in the
31-
separate thread.
32-
33-
Returns a coroutine that can be awaited to get the eventual result of *func*.
34-
"""
35-
loop = asyncio.events.get_running_loop()
36-
ctx = contextvars.copy_context()
37-
func_call = functools.partial(ctx.run, func, *args, **kwargs)
38-
return await loop.run_in_executor(None, func_call)
39-
40-
4116
async def to_thread(
4217
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
4318
) -> T_Retval:
4419
if sniffio.current_async_library() == "asyncio":
45-
return await _asyncio_to_thread(func, *args, **kwargs)
20+
return await asyncio.to_thread(func, *args, **kwargs)
4621

4722
return await anyio.to_thread.run_sync(
4823
functools.partial(func, *args, **kwargs),
@@ -53,10 +28,7 @@ async def to_thread(
5328
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
5429
"""
5530
Take a blocking function and create an async one that receives the same
56-
positional and keyword arguments. For python version 3.9 and above, it uses
57-
asyncio.to_thread to run the function in a separate thread. For python version
58-
3.8, it uses locally defined copy of the asyncio.to_thread function which was
59-
introduced in python 3.9.
31+
positional and keyword arguments.
6032
6133
Usage:
6234

src/contextual/_utils/_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
133133
# Type safe methods for narrowing types with TypeVars.
134134
# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
135135
# however this cause Pyright to rightfully report errors. As we know we don't
136-
# care about the contained types we can safely use `object` in it's place.
136+
# care about the contained types we can safely use `object` in its place.
137137
#
138138
# There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
139139
# `is_*` is for when you're dealing with an unknown input

0 commit comments

Comments
 (0)