Skip to content

release: 4.1.0 - #89

Open
stainless-app[bot] wants to merge 140 commits into
mainfrom
release-please--branches--main--changes--next
Open

release: 4.1.0#89
stainless-app[bot] wants to merge 140 commits into
mainfrom
release-please--branches--main--changes--next

Conversation

@stainless-app

@stainless-app stainless-app Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Automated Release PR

4.1.0 (2026-08-05)

Full Changelog: v4.0.0...v4.1.0

Features

  • api: update via SDK Studio (#61) (9c0c551)
  • clean up environment call outs (696f18b)
  • client: add custom JSON encoder for extended type support (87eaded)
  • client: add follow_redirects request option (6eb41c9)
  • client: add support for aiohttp (6f6ddd9)
  • client: add support for binary request streaming (41c399b)
  • client: allow passing NotGiven for body (#67) (3ad7f25)
  • client: send X-Stainless-Read-Timeout header (#63) (a594c75)
  • client: support file upload requests (fde965a)
  • improve future compat with pydantic v3 (bccbddf)
  • internal/types: support eagerly validating pydantic iterators (8e8ca68)
  • internal: implement indices array format for query and form serialization (c601950)
  • stlc: configurable CI runner and private-production-repo support in workflow templates (2da8c4d)
  • support setting headers via env (20336c3)
  • types: replace List[str] with SequenceNotStr in params (0578887)

Bug Fixes

  • asyncify on non-asyncio runtimes (#66) (ca310cd)
  • avoid newer type syntax (db10820)
  • ci: correct conditional (66eb0ef)
  • ci: ensure pip is always available (#78) (d3d295a)
  • ci: release-doctor — report correct token name (65cdccf)
  • ci: remove publishing patch (#79) (493f504)
  • client: add missing f-string prefix in file type error message (8026d49)
  • client: close streams without requiring full consumption (e783145)
  • client: correctly parse binary response | stream (3924997)
  • client: don't send Content-Type header on GET requests (6bcbc4e)
  • client: mark some request bodies as optional (3ad7f25)
  • client: preserve hardcoded query params when merging with user params (12d00c8)
  • compat with Python 3.14 (f7d6103)
  • compat: update signatures of model_dump and model_dump_json for Pydantic v1 (898ca7b)
  • deps: bump minimum typing-extensions version (323a703)
  • ensure file data are only sent as 1 parameter (2a24f49)
  • ensure streams are always closed (f89af68)
  • package: support direct resource imports (ad2d130)
  • parsing: correctly handle nested discriminated unions (cd511d2)
  • parsing: ignore empty metadata (bdd8ead)
  • parsing: parse extra field types (470d8a8)
  • perf: optimize some hot paths (7cc4937)
  • perf: skip traversing types for NotGiven values (38509ba)
  • pydantic v1: more robust ModelField.annotation check (3dc3480)
  • pydantic: do not pass by_alias unless set (887a8ec)
  • sanitize endpoint path params (40f9b7b)
  • tests: fix: tests which call HTTP endpoints directly with the example parameters (539215f)
  • types: allow pyright to infer TypedDict types within SequenceNotStr (84b4806)
  • types: handle more discriminated union shapes (#77) (8b6dcf0)
  • use async_to_httpx_files in patch method (6bfd0c0)
  • use correct field name format for multipart file arrays (0f7a6a4)

Performance Improvements

  • client: optimize file structure copying in multipart requests (639db35)

Chores

  • add Python 3.14 classifier and testing (c172e9c)
  • broadly detect json family of content-type headers (febefbc)
  • bump httpx-aiohttp version to 0.1.9 (7aeb4c8)
  • ci: add timeout thresholds for CI jobs (d5cbcd0)
  • ci: change upload type (d7e4405)
  • ci: enable for pull requests (1ea6fbc)
  • ci: fix installation instructions (6291f4a)
  • ci: only run for pushes and fork pull requests (5d00f3e)
  • ci: only use depot for staging repos (19ee773)
  • ci: skip lint on metadata-only changes (287d210)
  • ci: skip uploading artifacts on stainless-internal branches (d12f89a)
  • ci: upgrade actions/github-script (bdad5ed)
  • ci: upload sdks to package manager (598ec7e)
  • client: minor internal fixes (1e29d3b)
  • deps: mypy 1.18.1 has a regression, pin to 1.17 (fd940b6)
  • do not install brew dependencies in ./scripts/bootstrap by default (67c48f0)
  • docs: grammar improvements (540e711)
  • docs: remove reference to rye shell (ec32daa)
  • docs: update client docstring (#71) (b41543a)
  • docs: use environment variables for authentication in code snippets (ff7f0ef)
  • fix typos (#80) (c1576cc)
  • format all api.md files (034e708)
  • internal/tests: avoid race condition with implicit client cleanup (d3a5435)
  • internal: add --fix argument to lint script (0b1e68e)
  • internal: add missing files argument to base client (5547d1b)
  • internal: add request options to SSE classes (931a27e)
  • internal: add Sequence related utils (5f815ef)
  • internal: avoid errors for isinstance checks on proxies (5dc0949)
  • internal: base client updates (0ac179a)
  • internal: bump dependencies (5c298f6)
  • internal: bump pinned h11 dep (6cafe07)
  • internal: bump pyright version (81c2baf)
  • internal: bump rye to 0.44.0 (#76) (21a20b3)
  • internal: change ci workflow machines (656643d)
  • internal: codegen related update (0b4efb7)
  • internal: codegen related update (2f8fbc4)
  • internal: codegen related update (d6d5a1d)
  • internal: codegen related update (a145cee)
  • internal: codegen related update (#75) (db19786)
  • internal: detect missing future annotations with ruff (23b94ed)
  • internal: expand CI branch coverage (7fd1145)
  • internal: fix devcontainers setup (#68) (97b7254)
  • internal: fix lint error on Python 3.14 (1d9c80a)
  • internal: fix list file params (1b5e333)
  • internal: fix ruff target version (1437b86)
  • internal: fix type traversing dictionary params (#64) (1322c80)
  • internal: grammar fix (it's -> its) (8b1cdb7)
  • internal: import reformatting (8a3f6f0)
  • internal: make test_proxy_environment_variables more resilient (f79d30c)
  • internal: make test_proxy_environment_variables more resilient to env (08e33e4)
  • internal: minor type handling changes (#65) (7e69125)
  • internal: more robust bootstrap script (449a9c3)
  • internal: move mypy configurations to pyproject.toml file (f87b268)
  • internal: properly set pydantic_private (#69) (bc25b84)
  • internal: reduce CI branch coverage (2492996)
  • internal: refactor retries to not use recursion (055e329)
  • internal: reformat pyproject.toml (5b57987)
  • internal: remove extra empty newlines (#74) (3d90dff)
  • internal: remove mock server code (cb06a16)
  • internal: remove trailing character (#81) (4cfa80b)
  • internal: remove unused http client options forwarding (#72) (69a44e3)
  • internal: slight transform perf improvement (#82) (5498eaf)
  • internal: tweak CI branches (2372dd8)
  • internal: update actions/checkout version (54d6bd9)
  • internal: update comment in script (103820e)
  • internal: update conftest.py (83531b4)
  • internal: update gitignore (15b9637)
  • internal: update models test (421a2b5)
  • internal: update pydantic dependency (5dd09a4)
  • internal: update pyright exclude list (f306088)
  • internal: update pyright settings (2d267e1)
  • package: drop Python 3.8 support (95ca18d)
  • package: mark python 3.13 as supported (06ad64f)
  • project: add settings file for vscode (6754b39)
  • readme: fix version rendering on pypi (b1e9e51)
  • readme: update badges (f3f214f)
  • speedup initial import (3b3f4e6)
  • tests: add tests for httpx client instantiation & proxies (5e172cd)
  • tests: run tests in parallel (497b381)
  • tests: simplify get_platform test (ef07d85)
  • tests: skip some failing tests on the latest python versions (39d037c)
  • types: change optional parameter type from NotGiven to Omit (14cb9a9)
  • update @stainless-api/prism-cli to v5.15.0 (d766a01)
  • update github action (d0f9d9e)
  • update lockfile (77726a0)
  • update mock server docs (1241c00)

Documentation

  • client: fix httpx.Timeout documentation reference (3341b85)
  • update URLs from stainlessapi.com to stainless.com (#70) (08062c4)

This pull request is managed by Stainless's GitHub App.

The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.

For a better experience, it is recommended to use either rebase-merge or squash-merge when merging this pull request.

🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions

Greptile Summary

This is the automated Stainless-generated release PR bumping the Brainbase Python SDK from v4.0.0 to v4.1.0. The release contains a substantial set of infrastructure improvements alongside a small API surface update.

  • API change: Three separate voice-deployment response types (VoiceCreateResponse, VoiceUpdateResponse, VoiceRetrieveResponse) are consolidated into a single VoiceDeployment model; f-string URL construction is replaced with a new path_template utility that properly percent-encodes path parameters per RFC 3986 and rejects dot-segment traversal.
  • Client infrastructure: Retry logic is refactored from recursion to an explicit loop (idempotency key is now set once and reused across retries), resource attributes are lazy-loaded via cached_property, the deprecated transport/proxies/limits constructor arguments are removed, and new features are added including aiohttp support, binary request streaming, follow_redirects, X-Stainless-Read-Timeout header, and BRAINBASE_CUSTOM_HEADERS env-var-driven header injection.
  • Type system: Pydantic compatibility layer is refactored to a PYDANTIC_V1 flag, supporting v1/v2/v3 without the pydantic.v1 shim; SequenceNotStr, BinaryTypes/AsyncBinaryTypes, and module-level not_given/omit singletons are added to _types.py.

Confidence Score: 4/5

Safe to merge — the changes are auto-generated, well-structured, and the core request/retry logic refactoring is internally consistent.

The retry refactoring (recursion → loop) is the most load-bearing change; reviewing it carefully shows the idempotency key is correctly set once before retries start, and remaining_retries arithmetic is correct. The two findings — VoiceDeployment missing a name field and path_template's silent None→null conversion — are worth confirming against the API spec but neither causes a runtime failure in the current call sites. The Pydantic compatibility overhaul is broad but follows a clean PYDANTIC_V1 flag pattern.

Files Needing Attention: src/brainbase/types/workers/deployments/voice_deployment.py (confirm name field against API spec) and src/brainbase/_utils/_path.py (None parameter behaviour).

Important Files Changed

Filename Overview
src/brainbase/_version.py Version bump from 4.0.0 to 4.1.0 — straightforward and consistent with the release PR title.
src/brainbase/resources/workers/deployments/voice.py Voice deployment resource updated: three separate response types collapsed into VoiceDeployment, optional params changed from NotGiven to Omit, and f-string URL construction replaced with path_template for proper percent-encoding. The response model is missing a name field despite name being required in create/update params.
src/brainbase/_utils/_path.py New path_template utility that percent-encodes path/query/fragment parameters per RFC 3986 and rejects dot-segment traversal. None values are silently converted to the literal string "null" rather than raising an error.
src/brainbase/_base_client.py Major refactor: retry logic moved from recursion to an explicit loop, idempotency key now set once before retries start (correctly reused across attempts), GET requests no longer set Content-Type, added follow_redirects support, X-Stainless-Read-Timeout header, and JSON-based pagination via PageInfo.json. Deprecated transport/proxies/limits args removed.
src/brainbase/_client.py Eagerly-initialized resource attributes converted to cached_property for lazy loading, BRAINBASE_CUSTOM_HEADERS env var support added, deprecated Union/NOT_GIVEN cleaned up. Changes are internally consistent.
src/brainbase/_compat.py Pydantic compatibility layer refactored from PYDANTIC_V2-centric to PYDANTIC_V1 flag, supporting v1, v2, and future v3. Custom utility functions in _utils replace pydantic.typing imports for non-v1 builds.
src/brainbase/types/workers/deployments/voice_deployment.py New unified VoiceDeployment model consolidating VoiceCreateResponse, VoiceUpdateResponse, and VoiceRetrieveResponse. Model lacks a name field despite name being required in create/update request parameters.
src/brainbase/_utils/_json.py New custom JSON encoder (openapi_dumps) that serializes datetime to ISO format and pydantic models using model_dump, matching httpx's default JSON settings.
src/brainbase/_utils/_resources_proxy.py New lazy proxy for brainbase.resources module enabling import-time acceleration by deferring the resources subpackage load.
src/brainbase/_types.py Added module-level singletons not_given and omit, SequenceNotStr protocol, BinaryTypes/AsyncBinaryTypes for streaming, ArrayFormat/NestedFormat literals, and follow_redirects in RequestOptions.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Client.workers.voice.create / update / retrieve / list] --> B[path_template\nPercent-encode path params\nReject dot-segments]
    B --> C[_build_request\nSet idempotency key once\nAdd X-Stainless-Read-Timeout\nStrip Content-Type on GET]
    C --> D{Retry loop\nmax_retries+1 iterations}
    D --> E[httpx.send / aiohttp.send]
    E -->|TimeoutException| F{remaining_retries > 0?}
    E -->|ConnectionError| F
    E -->|HTTP 4xx/5xx + should_retry| F
    F -->|Yes| G[_sleep_for_retry\ncontinue loop]
    G --> D
    F -->|No| H[Raise APIError]
    E -->|Success| I[_process_response\ncast_to VoiceDeployment\nor other model]
Loading

Comments Outside Diff (1)

  1. src/brainbase/types/workers/deployments/voice_deployment.py, line 12-21 (link)

    P2 VoiceDeployment missing name field

    The name parameter is required in both VoiceCreateParams and VoiceUpdateParams, but VoiceDeployment — the unified response model for create, retrieve, update, and list — has no name field. If the API returns a name in its response, callers cannot access it as a typed attribute. It will still be stored in the model's extra fields (because BaseModel has extra="allow"), but there's no IDE-visible accessor. Worth confirming the API spec intentionally omits name from responses.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/brainbase/types/workers/deployments/voice_deployment.py
    Line: 12-21
    
    Comment:
    **VoiceDeployment missing `name` field**
    
    The `name` parameter is required in both `VoiceCreateParams` and `VoiceUpdateParams`, but `VoiceDeployment` — the unified response model for create, retrieve, update, and list — has no `name` field. If the API returns a `name` in its response, callers cannot access it as a typed attribute. It will still be stored in the model's extra fields (because `BaseModel` has `extra="allow"`), but there's no IDE-visible accessor. Worth confirming the API spec intentionally omits `name` from responses.
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix All With AI
### Issue 1
src/brainbase/types/workers/deployments/voice_deployment.py:12-21
**VoiceDeployment missing `name` field**

The `name` parameter is required in both `VoiceCreateParams` and `VoiceUpdateParams`, but `VoiceDeployment` — the unified response model for create, retrieve, update, and list — has no `name` field. If the API returns a `name` in its response, callers cannot access it as a typed attribute. It will still be stored in the model's extra fields (because `BaseModel` has `extra="allow"`), but there's no IDE-visible accessor. Worth confirming the API spec intentionally omits `name` from responses.

### Issue 2
src/brainbase/_utils/_path.py:72-77
**`None` path parameter silently becomes `"null"`**

When a placeholder value is `None`, `_interpolate` inserts the literal string `"null"` into the URL (e.g. `path_template("/api/workers/{id}", id=None)``/api/workers/null`). In practice the callers all guard against empty strings before reaching this function, so the path is never triggered, but the behaviour is surprising enough to warrant either a docstring note or a `TypeError` raise for `None` inputs instead of a silent sentinel substitution.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "release: 4.1.0" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

fix(client): mark some request bodies as optional
Note that we still want to run tests, as these depend on the metadata.
Pin all GitHub Actions referenced in generated workflows (both
first-party `actions/*` and third-party) to immutable commit SHAs.
Updating pinned actions is now a deliberate codegen-side bump rather
than implicit on every workflow run.
Comment on lines +72 to +77
else:
parts[i] = quoter(str(values[name]))

return "".join(parts)


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 None path parameter silently becomes "null"

When a placeholder value is None, _interpolate inserts the literal string "null" into the URL (e.g. path_template("/api/workers/{id}", id=None)/api/workers/null). In practice the callers all guard against empty strings before reaching this function, so the path is never triggered, but the behaviour is surprising enough to warrant either a docstring note or a TypeError raise for None inputs instead of a silent sentinel substitution.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/brainbase/_utils/_path.py
Line: 72-77

Comment:
**`None` path parameter silently becomes `"null"`**

When a placeholder value is `None`, `_interpolate` inserts the literal string `"null"` into the URL (e.g. `path_template("/api/workers/{id}", id=None)``/api/workers/null`). In practice the callers all guard against empty strings before reaching this function, so the path is never triggered, but the behaviour is surprising enough to warrant either a docstring note or a `TypeError` raise for `None` inputs instead of a silent sentinel substitution.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants