From 1ff08257a3f8a165d499e4ce8633719f4cb5f8ac Mon Sep 17 00:00:00 2001 From: speakeasybot Date: Sat, 19 Sep 2026 21:26:24 +0000 Subject: [PATCH 1/2] ## Python SDK Changes: * `open_router.byok.update()`: `error.status[403]` **Added** * `open_router.interns.list_interns()`: * `request.starting_after` **Added** * `response.next_cursor` **Added** --- .speakeasy/gen.lock | 56 ++++++++++--------- .speakeasy/gen.yaml | 2 +- .speakeasy/out.openapi.yaml | 28 +++++++++- .speakeasy/workflow.lock | 10 ++-- RELEASES.md | 12 +++- docs/components/internlistresponse.mdx | 9 +-- docs/operations/listinternsrequest.mdx | 1 + docs/sdks/byok/README.mdx | 1 + docs/sdks/interns/README.mdx | 1 + pyproject.toml | 2 +- src/openrouter/_version.py | 4 +- src/openrouter/byok.py | 10 ++++ .../components/internlistresponse.py | 28 +++++++++- src/openrouter/interns.py | 6 ++ src/openrouter/operations/listinterns.py | 9 +++ uv.lock | 2 +- 16 files changed, 135 insertions(+), 46 deletions(-) diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 5f181a91..9d72dd95 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,19 +1,19 @@ lockVersion: 2.0.0 id: c48cf606-fb42-4a45-9c23-8f0555307828 management: - docChecksum: 3f3470ebd639b0d6b50ab320a60f53a9 + docChecksum: 0563d6db7704a27f3b7a3beff8e602b3 docVersion: 1.0.0 speakeasyVersion: 1.787.0 generationVersion: 2.914.0 - releaseVersion: 1.2.5 - configChecksum: 7f2e7e3856c603e5ce6ad060be13db9f + releaseVersion: 1.2.6 + configChecksum: 05ae5cc8c6d5b877a33c4e2e32c287e9 repoURL: https://github.com/OpenRouterTeam/python-sdk.git installationURL: https://github.com/OpenRouterTeam/python-sdk.git published: true persistentEdits: - generation_id: e6bd2316-79aa-4e76-91ac-4c6df59413d7 - pristine_commit_hash: 95795231f2423ea695b6d92c4412eadf3fcad08f - pristine_tree_hash: 41f6f8d6a6b9875e122c91c680eb59c9c7d9495c + generation_id: cd46820e-de2a-481a-8817-f7dc33080f11 + pristine_commit_hash: 5355199f997528a92ceb1f3f42c4397c90c9c430 + pristine_tree_hash: 88a204ba1ded098bf077de095915e0893d2a3a08 features: python: acceptHeaders: 3.0.0 @@ -3091,8 +3091,8 @@ trackedFiles: pristine_git_object: c9eacd7d34fe43fbad032729e0dc9d130684ed72 docs/components/internlistresponse.mdx: id: 4c41e6189730 - last_write_checksum: sha1:b1bde1e620cf0cfc5533114aa7a6e8ca674527ba - pristine_git_object: c3d3d07cf0e16e5475f5e61816ac383c87ec42c8 + last_write_checksum: sha1:8253a947e183a40e10e54a21a5daecff3c1ca969 + pristine_git_object: 75702eed180998d3c69ffb0a32932147f9946c6b docs/components/internstatus.mdx: id: c90242af9f30 last_write_checksum: sha1:23f8d62521ceaa0468c43d9355dba84a260058a3 @@ -7487,8 +7487,8 @@ trackedFiles: pristine_git_object: 3a55ed59f02347866993fd5891898cec46a62c28 docs/operations/listinternsrequest.mdx: id: e6de03b26b5e - last_write_checksum: sha1:92926b5c50d73c315b37598d5b855a4235981196 - pristine_git_object: d1a3e4c68ed6ae8a16872fcf0226cad2130e1cb4 + last_write_checksum: sha1:21e87a5a17a54322e2ce07eb1a334bfbc9fa3d38 + pristine_git_object: 31c29ce6d21afd0e97d25329d704ad9e6f470f0b docs/operations/listinternvaultsecretsglobals.mdx: id: 83e0c9cb95c6 last_write_checksum: sha1:4def94f91a2e27836d1e4d8d313ad5bc31154d01 @@ -8047,8 +8047,8 @@ trackedFiles: pristine_git_object: 08ef8d73c050b6baab18ef6e246a1d074179b21f docs/sdks/byok/README.mdx: id: 17792f3b180d - last_write_checksum: sha1:ffe9c3546e5396fafeccd21ef2e26006c7f753ae - pristine_git_object: 90c7b0770dbea282347030a94c2dbd14ec4eac2f + last_write_checksum: sha1:8e2664cb1e98c4e170fc20d364e2a3bb40715cc8 + pristine_git_object: 4a64250d82ec39d28856b8dbcb3d3d6831ca4976 docs/sdks/chat/README.mdx: id: 1dd859c23fe1 last_write_checksum: sha1:75cf7ecf14bdaae4b3e2c767dc3d2c9d9e41d31c @@ -8099,8 +8099,8 @@ trackedFiles: pristine_git_object: 08c687694efbecf50406ae7929029d39fb121295 docs/sdks/interns/README.mdx: id: 2f8a053ffe78 - last_write_checksum: sha1:cd08ad1f1a3c790b2cc2cbdc51ba6ba7d7e80000 - pristine_git_object: b4aaf9cfe9f4859b9c6b41d5bd6133846bc07092 + last_write_checksum: sha1:a1a2bf31ac879e14767dddc88fa87e98046ef925 + pristine_git_object: 8fc5e6b9bb5d4626033ffd3f745f9fe49bd106fd docs/sdks/models/README.mdx: id: 58f1ca464e0b last_write_checksum: sha1:093dd30ebdd18b9763a6956915e0dcae4ae7d5e5 @@ -8163,8 +8163,8 @@ trackedFiles: pristine_git_object: 3e38f1a929f7d6b1d6de74604aa87e3d8f010544 pyproject.toml: id: 5d07e7d72637 - last_write_checksum: sha1:140a643bff5a9bd5da8b434162f752c62ef2d8ea - pristine_git_object: 18fe26f0d58a56819595fa9e6296bf8440564cdd + last_write_checksum: sha1:7d46ef8090d156637d78acb4572fce19c2dfca3a + pristine_git_object: 3a5ee39e26552427fbfee31d044fa1a2403e9bb9 scripts/prepare_readme.py: id: e0c5957a6035 last_write_checksum: sha1:77f44b60b98bc126557ec27391f91dfba764bb54 @@ -8191,8 +8191,8 @@ trackedFiles: pristine_git_object: 86713cfea633e09d33b3d4e65281071fe20e6137 src/openrouter/_version.py: id: d8d15ad6c586 - last_write_checksum: sha1:b82f3c4515d029546aa3cc8bc92de92ca3cd588c - pristine_git_object: 7a2e6e0536d24d9405a9e06f3db0db5cb492ae15 + last_write_checksum: sha1:3ab3cae82bacc5b29e795e4ae0476e3a305fa10f + pristine_git_object: 5c8bfcc8c2dba49f06b2d7e2c5cb57787224c2f1 src/openrouter/alpha.py: id: 306c4d93308d last_write_checksum: sha1:0c25a52ed5fb688ec1e923e4e82bcfc9cae49465 @@ -8223,8 +8223,8 @@ trackedFiles: pristine_git_object: 412aed86e32b1fd166d27e06e562463fa2b04395 src/openrouter/byok.py: id: bec352462ae1 - last_write_checksum: sha1:0dd1c12b4b192531a3d5926e2793d1ac421afa17 - pristine_git_object: 32d921e7d8d46ec11417a8e243185cd0f37633ca + last_write_checksum: sha1:ddb52d17b77021f5d53ab22fb9ffef447a61682e + pristine_git_object: 22834caf78784ff99d3be6cd4fe3f4eea01ac859 src/openrouter/chat.py: id: 723fdce15c1d last_write_checksum: sha1:1a4e5375fb95884be6db0f508e1c2c9cda27b20e @@ -9611,8 +9611,8 @@ trackedFiles: pristine_git_object: 589fcd031d428922ff76b506ea068fc746425a1c src/openrouter/components/internlistresponse.py: id: 1d6ce743dbed - last_write_checksum: sha1:51cf92ca51b0d90a127bf9fd8a150b4063a4ad70 - pristine_git_object: 941c3861bb2d78e8b696b1d7d3632e9baf96c343 + last_write_checksum: sha1:ffb97c9bdd1ccca6cfe9951cd0ae0297e4525765 + pristine_git_object: 2afe11472e1f3861b2b9725ddafd036b961d3088 src/openrouter/components/itemreferenceitem.py: id: d3c42b5f11fb last_write_checksum: sha1:25fd74f1c34307a97931e1d2c138a1db347218ad @@ -11135,8 +11135,8 @@ trackedFiles: pristine_git_object: 50643a126668498dd668c7cceb9f78c03d413d32 src/openrouter/interns.py: id: d18df05c5c6d - last_write_checksum: sha1:cf5c9ca74c5f31607eeb508bcb97078e8f0038be - pristine_git_object: 5272d44fdabfa2c5ab926de6c533875ff0571926 + last_write_checksum: sha1:cad4e1386e159c796d926eeacc0f658208e9c102 + pristine_git_object: 705236ee7435e3cd591571b81cf5f67ed9cbebd7 src/openrouter/models/__init__.py: id: ed73b93abb3f last_write_checksum: sha1:932a790ae66ccd7d7022b39c659bcf72a664ebea @@ -11499,8 +11499,8 @@ trackedFiles: pristine_git_object: 01655ae02036acf433ec5f54fd1d9323a55f0684 src/openrouter/operations/listinterns.py: id: 89bc4fa38c9f - last_write_checksum: sha1:4cafb99a7020b6199c7f57c36f111508ab18da49 - pristine_git_object: 003e212711b2436ee4af5efe3905932ea1e59371 + last_write_checksum: sha1:a73fccd1268f4586f5adc3477ed21011a914eb9a + pristine_git_object: f782b7b4230850b68af902f813083140ab28fb82 src/openrouter/operations/listinternvaultsecrets.py: id: d9f9db46871d last_write_checksum: sha1:31bd0f67cb4e7dffe16c4a4af5d27a08920d355c @@ -12717,6 +12717,8 @@ examples: application/json: {"error": {"code": 404, "message": "Resource not found"}} "500": application/json: {"error": {"code": 500, "message": "Internal Server Error"}} + "403": + application/json: {"error": {"code": 403, "message": "Only management keys can perform this operation"}} getAppRankings: speakeasy-default-get-app-rankings: parameters: @@ -14953,4 +14955,4 @@ examples: "502": application/json: {"error": {"code": 409, "message": "That question is no longer waiting for an answer."}} examplesVersion: 1.0.2 -releaseNotes: "## Python SDK Changes:\n* `open_router.interns.delete_intern()`: \n * `request.acknowledge_workspace_loss` **Added**\n * `error` **Changed**\n" +releaseNotes: "## Python SDK Changes:\n* `open_router.byok.update()`: `error.status[403]` **Added**\n* `open_router.interns.list_interns()`: \n * `request.starting_after` **Added**\n * `response.next_cursor` **Added**\n" diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 6fa73af5..edbd8d3f 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -36,7 +36,7 @@ generation: documentation: mintlify preApplyUnionDiscriminators: true python: - version: 1.2.5 + version: 1.2.6 additionalDependencies: dev: {} main: {} diff --git a/.speakeasy/out.openapi.yaml b/.speakeasy/out.openapi.yaml index 094b1848..0e9b8157 100644 --- a/.speakeasy/out.openapi.yaml +++ b/.speakeasy/out.openapi.yaml @@ -13088,6 +13088,10 @@ components: has_more: description: 'True when more interns match the current filters.' type: 'boolean' + next_cursor: + description: 'Opaque cursor, present when `has_more` is true. Pass it as `starting_after` to fetch the next page.' + example: 'MjAyNi0wOS0xNlQwODozMDowMC4wMDAwMDBafDdjOWU2Njc5LTc0MjUtNDBkZS05NDRiLWUwN2ZjMWY5MGFlNw' + type: 'string' required: - 'data' - 'has_more' @@ -31803,6 +31807,16 @@ paths: schema: $ref: '#/components/schemas/UnauthorizedResponse' description: 'Unauthorized - Authentication required or invalid credentials' + '403': + content: + application/json: + example: + error: + code: 403 + message: 'Only management keys can perform this operation' + schema: + $ref: '#/components/schemas/ForbiddenResponse' + description: 'Forbidden - Authentication successful but insufficient permissions' '404': content: application/json: @@ -36365,6 +36379,14 @@ paths: x-speakeasy-unknown-values: allow type: 'array' style: 'form' + - description: 'The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400.' + in: 'query' + name: 'starting_after' + required: false + schema: + description: 'The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400.' + example: 'MjAyNi0wOS0xNlQwODozMDowMC4wMDAwMDBafDdjOWU2Njc5LTc0MjUtNDBkZS05NDRiLWUwN2ZjMWY5MGFlNw' + type: 'string' - description: 'Only return interns in this workspace. It must match the API key workspace.' in: 'query' name: 'workspace_id' @@ -40584,9 +40606,13 @@ paths: '403': content: application/json: + example: + error: + code: 403 + message: 'Only management keys can perform this operation' schema: $ref: '#/components/schemas/ForbiddenResponse' - description: 'Privacy settings are locked' + description: 'Forbidden - Authentication successful but insufficient permissions' '404': content: application/json: diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index 31db30b7..30639281 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -2,8 +2,8 @@ speakeasyVersion: 1.787.0 sources: OpenRouter API: sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:6c09ee03206f9286c389f9cf9296b4e34c381c3b383ae873253e4f38cb64f11d - sourceBlobDigest: sha256:05e64270223712bbf5b880b35cee94962efcc130ff9773f4332e5fc94f4512a0 + sourceRevisionDigest: sha256:c66839243c891c6f582939dd68bc390fd56629095966346707bd300524c73e39 + sourceBlobDigest: sha256:0b66f3175e7b0f14ae3b22216c39295715cfd827ce73f569c2d4b012c4b91418 tags: - latest - 1.0.0 @@ -11,10 +11,10 @@ targets: open-router: source: OpenRouter API sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:6c09ee03206f9286c389f9cf9296b4e34c381c3b383ae873253e4f38cb64f11d - sourceBlobDigest: sha256:05e64270223712bbf5b880b35cee94962efcc130ff9773f4332e5fc94f4512a0 + sourceRevisionDigest: sha256:c66839243c891c6f582939dd68bc390fd56629095966346707bd300524c73e39 + sourceBlobDigest: sha256:0b66f3175e7b0f14ae3b22216c39295715cfd827ce73f569c2d4b012c4b91418 codeSamplesNamespace: open-router-python-code-samples - codeSamplesRevisionDigest: sha256:5f47abce5f532756d300ba4aa6fb317d628f6f66011cd233e89a9a24c6b4be2a + codeSamplesRevisionDigest: sha256:aa75ffe5aa1ebe652c8f1cf14a4813b6794989325e6798be7517cfcbdb81693f workflow: workflowVersion: 1.0.0 speakeasyVersion: 1.787.0 diff --git a/RELEASES.md b/RELEASES.md index b96dbf08..d8da5301 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2459,4 +2459,14 @@ Based on: ### Generated - [python v1.2.5] . ### Releases -- [PyPI v1.2.5] https://pypi.org/project/openrouter/1.2.5 - . \ No newline at end of file +- [PyPI v1.2.5] https://pypi.org/project/openrouter/1.2.5 - . + +## 2026-09-19 21:24:33 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.787.0 (2.914.0) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v1.2.6] . +### Releases +- [PyPI v1.2.6] https://pypi.org/project/openrouter/1.2.6 - . \ No newline at end of file diff --git a/docs/components/internlistresponse.mdx b/docs/components/internlistresponse.mdx index c3d3d07c..75702eed 100644 --- a/docs/components/internlistresponse.mdx +++ b/docs/components/internlistresponse.mdx @@ -7,7 +7,8 @@ Interns visible to the authenticated API key. ## Fields -| Field | Type | Required | Description | -| -------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------- | -| `data` | List[[components.Intern](../components/intern.mdx)] | :heavy_check_mark: | N/A | -| `has_more` | *bool* | :heavy_check_mark: | True when more interns match the current filters. | \ No newline at end of file +| Field | Type | Required | Description | Example | +| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| `data` | List[[components.Intern](../components/intern.mdx)] | :heavy_check_mark: | N/A | | +| `has_more` | *bool* | :heavy_check_mark: | True when more interns match the current filters. | | +| `next_cursor` | *Optional[str]* | :heavy_minus_sign: | Opaque cursor, present when `has_more` is true. Pass it as `starting_after` to fetch the next page. | MjAyNi0wOS0xNlQwODozMDowMC4wMDAwMDBafDdjOWU2Njc5LTc0MjUtNDBkZS05NDRiLWUwN2ZjMWY5MGFlNw | \ No newline at end of file diff --git a/docs/operations/listinternsrequest.mdx b/docs/operations/listinternsrequest.mdx index d1a3e4c6..31c29ce6 100644 --- a/docs/operations/listinternsrequest.mdx +++ b/docs/operations/listinternsrequest.mdx @@ -11,4 +11,5 @@ title: "ListInternsRequest" | `x_open_router_categories` | *Optional[str]* | :heavy_minus_sign: | Comma-separated list of app categories (e.g. "cli-agent,cloud-agent"). Used for marketplace rankings.
| | | `limit` | *Optional[int]* | :heavy_minus_sign: | Maximum number of interns to return, from 1 through 500. | 50 | | `status` | List[[operations.Status](../operations/status.mdx)] | :heavy_minus_sign: | Comma-separated lifecycle statuses to include. | [
"queued",
"running"
] | +| `starting_after` | *Optional[str]* | :heavy_minus_sign: | The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400. | MjAyNi0wOS0xNlQwODozMDowMC4wMDAwMDBafDdjOWU2Njc5LTc0MjUtNDBkZS05NDRiLWUwN2ZjMWY5MGFlNw | | `workspace_id` | *Optional[str]* | :heavy_minus_sign: | Only return interns in this workspace. It must match the API key workspace. | 89f9f5b2-3f89-4eaf-83ca-5ceae149e8bb | \ No newline at end of file diff --git a/docs/sdks/byok/README.mdx b/docs/sdks/byok/README.mdx index 90c7b077..4a64250d 100644 --- a/docs/sdks/byok/README.mdx +++ b/docs/sdks/byok/README.mdx @@ -277,6 +277,7 @@ with OpenRouter( | ---------------------------------- | ---------------------------------- | ---------------------------------- | | errors.BadRequestResponseError | 400 | application/json | | errors.UnauthorizedResponseError | 401 | application/json | +| errors.ForbiddenResponseError | 403 | application/json | | errors.NotFoundResponseError | 404 | application/json | | errors.InternalServerResponseError | 500 | application/json | | errors.OpenRouterDefaultError | 4XX, 5XX | \*/\* | \ No newline at end of file diff --git a/docs/sdks/interns/README.mdx b/docs/sdks/interns/README.mdx index b4aaf9cf..8fc5e6b9 100644 --- a/docs/sdks/interns/README.mdx +++ b/docs/sdks/interns/README.mdx @@ -52,6 +52,7 @@ with OpenRouter( | `x_open_router_categories` | *Optional[str]* | :heavy_minus_sign: | Comma-separated list of app categories (e.g. "cli-agent,cloud-agent"). Used for marketplace rankings.
| | | `limit` | *Optional[int]* | :heavy_minus_sign: | Maximum number of interns to return, from 1 through 500. | 50 | | `status` | List[[operations.Status](../../operations/status.mdx)] | :heavy_minus_sign: | Comma-separated lifecycle statuses to include. | [
"queued",
"running"
] | +| `starting_after` | *Optional[str]* | :heavy_minus_sign: | The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400. | MjAyNi0wOS0xNlQwODozMDowMC4wMDAwMDBafDdjOWU2Njc5LTc0MjUtNDBkZS05NDRiLWUwN2ZjMWY5MGFlNw | | `workspace_id` | *Optional[str]* | :heavy_minus_sign: | Only return interns in this workspace. It must match the API key workspace. | 89f9f5b2-3f89-4eaf-83ca-5ceae149e8bb | | `retries` | [Optional[utils.RetryConfig]](../../models/utils/retryconfig.mdx) | :heavy_minus_sign: | Configuration to override the default retry behavior of the client. | | diff --git a/pyproject.toml b/pyproject.toml index 18fe26f0..3a5ee39e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openrouter" -version = "1.2.5" +version = "1.2.6" description = "Official Python Client SDK for OpenRouter." authors = [{ name = "OpenRouter" },] readme = "README-PYPI.md" diff --git a/src/openrouter/_version.py b/src/openrouter/_version.py index 7a2e6e05..5c8bfcc8 100644 --- a/src/openrouter/_version.py +++ b/src/openrouter/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "openrouter" -__version__: str = "1.2.5" +__version__: str = "1.2.6" __openapi_doc_version__: str = "1.0.0" __gen_version__: str = "2.914.0" -__user_agent__: str = "speakeasy-sdk/python 1.2.5 2.914.0 1.0.0 openrouter" +__user_agent__: str = "speakeasy-sdk/python 1.2.6 2.914.0 1.0.0 openrouter" try: if __package__ is not None: diff --git a/src/openrouter/byok.py b/src/openrouter/byok.py index 32d921e7..22834caf 100644 --- a/src/openrouter/byok.py +++ b/src/openrouter/byok.py @@ -1378,6 +1378,11 @@ def update( errors.UnauthorizedResponseErrorData, http_res ) raise errors.UnauthorizedResponseError(response_data, http_res) + if utils.match_response(http_res, "403", "application/json"): + response_data = unmarshal_json_response( + errors.ForbiddenResponseErrorData, http_res + ) + raise errors.ForbiddenResponseError(response_data, http_res) if utils.match_response(http_res, "404", "application/json"): response_data = unmarshal_json_response( errors.NotFoundResponseErrorData, http_res @@ -1553,6 +1558,11 @@ async def update_async( errors.UnauthorizedResponseErrorData, http_res ) raise errors.UnauthorizedResponseError(response_data, http_res) + if utils.match_response(http_res, "403", "application/json"): + response_data = unmarshal_json_response( + errors.ForbiddenResponseErrorData, http_res + ) + raise errors.ForbiddenResponseError(response_data, http_res) if utils.match_response(http_res, "404", "application/json"): response_data = unmarshal_json_response( errors.NotFoundResponseErrorData, http_res diff --git a/src/openrouter/components/internlistresponse.py b/src/openrouter/components/internlistresponse.py index 941c3861..2afe1147 100644 --- a/src/openrouter/components/internlistresponse.py +++ b/src/openrouter/components/internlistresponse.py @@ -2,9 +2,10 @@ from __future__ import annotations from .intern import Intern, InternTypedDict -from openrouter.types import BaseModel -from typing import List -from typing_extensions import TypedDict +from openrouter.types import BaseModel, UNSET_SENTINEL +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict class InternListResponseTypedDict(TypedDict): @@ -13,6 +14,8 @@ class InternListResponseTypedDict(TypedDict): data: List[InternTypedDict] has_more: bool r"""True when more interns match the current filters.""" + next_cursor: NotRequired[str] + r"""Opaque cursor, present when `has_more` is true. Pass it as `starting_after` to fetch the next page.""" class InternListResponse(BaseModel): @@ -22,3 +25,22 @@ class InternListResponse(BaseModel): has_more: bool r"""True when more interns match the current filters.""" + + next_cursor: Optional[str] = None + r"""Opaque cursor, present when `has_more` is true. Pass it as `starting_after` to fetch the next page.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["next_cursor"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + + if val != UNSET_SENTINEL: + if val is not None or k not in optional_fields: + m[k] = val + + return m diff --git a/src/openrouter/interns.py b/src/openrouter/interns.py index 5272d44f..705236ee 100644 --- a/src/openrouter/interns.py +++ b/src/openrouter/interns.py @@ -20,6 +20,7 @@ def list_interns( x_open_router_categories: Optional[str] = None, limit: Optional[int] = None, status: Optional[Iterable[operations.Status]] = None, + starting_after: Optional[str] = None, workspace_id: Optional[str] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, @@ -41,6 +42,7 @@ def list_interns( :param limit: Maximum number of interns to return, from 1 through 500. :param status: Comma-separated lifecycle statuses to include. + :param starting_after: The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400. :param workspace_id: Only return interns in this workspace. It must match the API key workspace. :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method @@ -63,6 +65,7 @@ def list_interns( x_open_router_categories=x_open_router_categories, limit=limit, status=utils.unmarshal(status, Optional[List[operations.Status]]), + starting_after=starting_after, workspace_id=workspace_id, ) @@ -154,6 +157,7 @@ async def list_interns_async( x_open_router_categories: Optional[str] = None, limit: Optional[int] = None, status: Optional[Iterable[operations.Status]] = None, + starting_after: Optional[str] = None, workspace_id: Optional[str] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, @@ -175,6 +179,7 @@ async def list_interns_async( :param limit: Maximum number of interns to return, from 1 through 500. :param status: Comma-separated lifecycle statuses to include. + :param starting_after: The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400. :param workspace_id: Only return interns in this workspace. It must match the API key workspace. :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method @@ -197,6 +202,7 @@ async def list_interns_async( x_open_router_categories=x_open_router_categories, limit=limit, status=utils.unmarshal(status, Optional[List[operations.Status]]), + starting_after=starting_after, workspace_id=workspace_id, ) diff --git a/src/openrouter/operations/listinterns.py b/src/openrouter/operations/listinterns.py index 003e2127..f782b7b4 100644 --- a/src/openrouter/operations/listinterns.py +++ b/src/openrouter/operations/listinterns.py @@ -106,6 +106,8 @@ class ListInternsRequestTypedDict(TypedDict): r"""Maximum number of interns to return, from 1 through 500.""" status: NotRequired[List[Status]] r"""Comma-separated lifecycle statuses to include.""" + starting_after: NotRequired[str] + r"""The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400.""" workspace_id: NotRequired[str] r"""Only return interns in this workspace. It must match the API key workspace.""" @@ -151,6 +153,12 @@ class ListInternsRequest(BaseModel): ] = None r"""Comma-separated lifecycle statuses to include.""" + starting_after: Annotated[ + Optional[str], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = None + r"""The opaque `next_cursor` of the previous page. Returns the interns that come after it in the newest-first order. A malformed cursor is a 400.""" + workspace_id: Annotated[ Optional[str], FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), @@ -166,6 +174,7 @@ def serialize_model(self, handler): "X-OpenRouter-Categories", "limit", "status", + "starting_after", "workspace_id", ] ) diff --git a/uv.lock b/uv.lock index 7fbdaf8c..83bda36c 100644 --- a/uv.lock +++ b/uv.lock @@ -213,7 +213,7 @@ wheels = [ [[package]] name = "openrouter" -version = "1.2.5" +version = "1.2.6" source = { editable = "." } dependencies = [ { name = "httpcore" }, From a9bc79c6e57139fa78a8301a86c26a749c92b854 Mon Sep 17 00:00:00 2001 From: "speakeasy-github[bot]" <128539517+speakeasy-github[bot]@users.noreply.github.com> Date: Sat, 19 Sep 2026 21:26:31 +0000 Subject: [PATCH 2/2] empty commit to trigger [run-tests] workflow