diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a696b6a..154a697 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.36" + ".": "0.1.0-alpha.37" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 3f719fa..79ec5d8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 26 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-62d8fccba4eb8dc3a80434e0849eab3352e49fb96a718bb7b6d17ed8e582b716.yml -openapi_spec_hash: 4ff9376cf9634e91731e63fe482ea532 -config_hash: 1ae82c93499b9f0b9ba828b8919f9cb3 +configured_endpoints: 43 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-97b61518d8666ea7cb310af04248e00bcf8dc9753ba3c7e84471df72b3232004.yml +openapi_spec_hash: a3500531973ad999c350b87c21aa3ab8 +config_hash: 026ef000d34bf2f930e7b41e77d2d3ff diff --git a/CHANGELOG.md b/CHANGELOG.md index 16b4106..d02f4cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,342 +1,36 @@ # Changelog -## 0.1.0-alpha.36 (2025-08-27) +## 0.1.0-alpha.37 (2025-12-09) -Full Changelog: [v0.1.0-alpha.35...v0.1.0-alpha.36](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.35...v0.1.0-alpha.36) +Full Changelog: [v0.1.0-alpha.36...v0.1.0-alpha.37](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.36...v0.1.0-alpha.37) ### Features -* **client:** support file upload requests ([c2e1522](https://github.com/sst/opencode-sdk-python/commit/c2e1522ffba596910098e1e58eef7b4d00548e18)) +* **api:** manual updates ([979c43d](https://github.com/sst/opencode-sdk-python/commit/979c43dbc7f72a03ec5c16c02b800cccca95a24e)) +* improve future compat with pydantic v3 ([5d58a79](https://github.com/sst/opencode-sdk-python/commit/5d58a795ce9d8635b311c8eae4d5c719ecf5558a)) ### Bug Fixes -* avoid newer type syntax ([42779eb](https://github.com/sst/opencode-sdk-python/commit/42779eb3d7035b677ef213d6508658dbd56b50bf)) +* **client:** close streams without requiring full consumption ([81dc914](https://github.com/sst/opencode-sdk-python/commit/81dc9145e044aff113827b1bd7bc54f9aeb1db8e)) +* **compat:** compat with `pydantic<2.8.0` when using additional fields ([662246b](https://github.com/sst/opencode-sdk-python/commit/662246b39c07bcac1346f3033f36c4188a80196e)) ### Chores -* **internal:** change ci workflow machines ([14c918e](https://github.com/sst/opencode-sdk-python/commit/14c918ee18edc797d2d8dd1f1d462ffb004a7e89)) -* **internal:** codegen related update ([477ff58](https://github.com/sst/opencode-sdk-python/commit/477ff58aa920fd0c02378ff33c041d061923dee4)) -* **internal:** fix ruff target version ([359b956](https://github.com/sst/opencode-sdk-python/commit/359b95615445c2f675aa3520cb233c19b50dfe31)) -* **internal:** update comment in script ([9ac7cbb](https://github.com/sst/opencode-sdk-python/commit/9ac7cbb6dba85d18d18bd37f95dffc0eea3d2605)) -* **internal:** update pyright exclude list ([5d96f63](https://github.com/sst/opencode-sdk-python/commit/5d96f63f7a9aaebb2e85fcd11b380bbeca3b7310)) -* update @stainless-api/prism-cli to v5.15.0 ([88487ee](https://github.com/sst/opencode-sdk-python/commit/88487ee6dbc8ac60e9d26573c8f08ae3f1389e83)) -* update github action ([fe98742](https://github.com/sst/opencode-sdk-python/commit/fe98742cd53b1e8783f600879164dd16fea610d3)) - -## 0.1.0-alpha.35 (2025-07-29) - -Full Changelog: [v0.1.0-alpha.34...v0.1.0-alpha.35](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.34...v0.1.0-alpha.35) - -### Features - -* **api:** api update ([06ebf15](https://github.com/sst/opencode-sdk-python/commit/06ebf15e7ed0f782dbf51352a71fc5edb948a93c)) - -## 0.1.0-alpha.34 (2025-07-28) - -Full Changelog: [v0.1.0-alpha.33...v0.1.0-alpha.34](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.33...v0.1.0-alpha.34) - -### Features - -* **api:** api update ([0bc9251](https://github.com/sst/opencode-sdk-python/commit/0bc92517109d280c22e22639ee4ffa58d63d837b)) -* **api:** api update ([14ada9d](https://github.com/sst/opencode-sdk-python/commit/14ada9d7d1e93d85f357f417633b691b116c8ad5)) - -## 0.1.0-alpha.33 (2025-07-25) - -Full Changelog: [v0.1.0-alpha.32...v0.1.0-alpha.33](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.32...v0.1.0-alpha.33) - -### Features - -* **api:** api update ([9002768](https://github.com/sst/opencode-sdk-python/commit/9002768edd617a44d4d686dd9e88f41fe6a56f2f)) - - -### Chores - -* **project:** add settings file for vscode ([7fff9af](https://github.com/sst/opencode-sdk-python/commit/7fff9af8fd66865dc933dce74f0385250377af87)) - -## 0.1.0-alpha.32 (2025-07-24) - -Full Changelog: [v0.1.0-alpha.31...v0.1.0-alpha.32](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.31...v0.1.0-alpha.32) - -### Features - -* **api:** api update ([988b38c](https://github.com/sst/opencode-sdk-python/commit/988b38ce1d4b7694083abe26f2198463d4555012)) - -## 0.1.0-alpha.31 (2025-07-24) - -Full Changelog: [v0.1.0-alpha.30...v0.1.0-alpha.31](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.30...v0.1.0-alpha.31) - -### Features - -* **api:** api update ([35553a6](https://github.com/sst/opencode-sdk-python/commit/35553a6e3b3472562cdc38f0399fcd37af1b52e9)) - -## 0.1.0-alpha.30 (2025-07-23) - -Full Changelog: [v0.1.0-alpha.29...v0.1.0-alpha.30](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.29...v0.1.0-alpha.30) - -### Bug Fixes - -* **parsing:** parse extra field types ([6817656](https://github.com/sst/opencode-sdk-python/commit/6817656ba347e8074960af1526763c134d75cf7d)) - -## 0.1.0-alpha.29 (2025-07-22) - -Full Changelog: [v0.1.0-alpha.28...v0.1.0-alpha.29](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.28...v0.1.0-alpha.29) - -### Features - -* **api:** api update ([08130f0](https://github.com/sst/opencode-sdk-python/commit/08130f0c068f4008ffda297c68a68a44dec34d95)) - -## 0.1.0-alpha.28 (2025-07-22) - -Full Changelog: [v0.1.0-alpha.27...v0.1.0-alpha.28](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.27...v0.1.0-alpha.28) - -### Features - -* **api:** api update ([e8022cd](https://github.com/sst/opencode-sdk-python/commit/e8022cd6d313c1c710dc2721f7e962285d48b02e)) - -## 0.1.0-alpha.27 (2025-07-22) - -Full Changelog: [v0.1.0-alpha.26...v0.1.0-alpha.27](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.26...v0.1.0-alpha.27) - -### Features - -* **api:** api update ([50c887c](https://github.com/sst/opencode-sdk-python/commit/50c887c4202f587317afecb8998181c6de78b7b4)) - - -### Bug Fixes - -* **parsing:** ignore empty metadata ([8ee35ae](https://github.com/sst/opencode-sdk-python/commit/8ee35ae762cb0ade81b08cc41a9f496afe9fd484)) - -## 0.1.0-alpha.26 (2025-07-21) - -Full Changelog: [v0.1.0-alpha.25...v0.1.0-alpha.26](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.25...v0.1.0-alpha.26) - -### Features - -* **api:** api update ([827dc0c](https://github.com/sst/opencode-sdk-python/commit/827dc0c780afd217f981cdd31d371fe96327aeec)) - -## 0.1.0-alpha.25 (2025-07-21) - -Full Changelog: [v0.1.0-alpha.24...v0.1.0-alpha.25](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.24...v0.1.0-alpha.25) - -### Features - -* **api:** api update ([a85f832](https://github.com/sst/opencode-sdk-python/commit/a85f832a942075091b9ca3f7e6399ba00239f354)) - -## 0.1.0-alpha.24 (2025-07-21) - -Full Changelog: [v0.1.0-alpha.23...v0.1.0-alpha.24](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.23...v0.1.0-alpha.24) - -### Features - -* **api:** api update ([bd6dd48](https://github.com/sst/opencode-sdk-python/commit/bd6dd48f11b23c77802e0a36af94c1a92c0326c7)) - -## 0.1.0-alpha.23 (2025-07-18) - -Full Changelog: [v0.1.0-alpha.22...v0.1.0-alpha.23](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.22...v0.1.0-alpha.23) - -### Features - -* **api:** api update ([d8c9fc9](https://github.com/sst/opencode-sdk-python/commit/d8c9fc984c48b7dadce8840c5c1e010a508d58b4)) - -## 0.1.0-alpha.22 (2025-07-17) - -Full Changelog: [v0.1.0-alpha.21...v0.1.0-alpha.22](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.21...v0.1.0-alpha.22) - -### Features - -* **api:** api update ([582070a](https://github.com/sst/opencode-sdk-python/commit/582070ae69b0ae1088271038b0fcb818c30c74cf)) - -## 0.1.0-alpha.21 (2025-07-17) - -Full Changelog: [v0.1.0-alpha.20...v0.1.0-alpha.21](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.20...v0.1.0-alpha.21) - -### Features - -* **api:** api update ([71211e8](https://github.com/sst/opencode-sdk-python/commit/71211e888ecd5e848ac4de5ed058e4756025f694)) - -## 0.1.0-alpha.20 (2025-07-17) - -Full Changelog: [v0.1.0-alpha.19...v0.1.0-alpha.20](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.19...v0.1.0-alpha.20) - -### Features - -* **api:** api update ([f48c0d6](https://github.com/sst/opencode-sdk-python/commit/f48c0d6bb1943df3e3758d19b83c70fd1c15e2c2)) - -## 0.1.0-alpha.19 (2025-07-16) - -Full Changelog: [v0.1.0-alpha.18...v0.1.0-alpha.19](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.18...v0.1.0-alpha.19) - -### Features - -* **api:** api update ([07b8550](https://github.com/sst/opencode-sdk-python/commit/07b8550e658373298ac5d64eed102f21d03a29fa)) - -## 0.1.0-alpha.18 (2025-07-16) - -Full Changelog: [v0.1.0-alpha.17...v0.1.0-alpha.18](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.17...v0.1.0-alpha.18) - -### Features - -* **api:** api update ([02c3399](https://github.com/sst/opencode-sdk-python/commit/02c3399fb52fa96d50e6dd9c74f3106d1107308e)) - -## 0.1.0-alpha.17 (2025-07-16) - -Full Changelog: [v0.1.0-alpha.16...v0.1.0-alpha.17](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.16...v0.1.0-alpha.17) - -### Features - -* **api:** api update ([e76b574](https://github.com/sst/opencode-sdk-python/commit/e76b57439c37c0d3514e1497a4d1a78279844bdc)) - -## 0.1.0-alpha.16 (2025-07-15) - -Full Changelog: [v0.1.0-alpha.15...v0.1.0-alpha.16](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.15...v0.1.0-alpha.16) - -### Features - -* **api:** api update ([670fa88](https://github.com/sst/opencode-sdk-python/commit/670fa889512f9000e6fee8c9f5c2b49434224592)) - -## 0.1.0-alpha.15 (2025-07-15) - -Full Changelog: [v0.1.0-alpha.14...v0.1.0-alpha.15](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.14...v0.1.0-alpha.15) - -### Features - -* **api:** api update ([88bbf66](https://github.com/sst/opencode-sdk-python/commit/88bbf66c1f6ec7266fccb7f8e3265bb074afd5e6)) - -## 0.1.0-alpha.14 (2025-07-15) - -Full Changelog: [v0.1.0-alpha.13...v0.1.0-alpha.14](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.13...v0.1.0-alpha.14) - -### Features - -* **api:** api update ([80d8572](https://github.com/sst/opencode-sdk-python/commit/80d85724c6b17b867ac3d19b0741bb88bb604798)) - -## 0.1.0-alpha.13 (2025-07-15) - -Full Changelog: [v0.1.0-alpha.12...v0.1.0-alpha.13](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.12...v0.1.0-alpha.13) - -### Features - -* **api:** api update ([a51d627](https://github.com/sst/opencode-sdk-python/commit/a51d627f3a39324ca769a688b63c95dc8f5eba35)) - -## 0.1.0-alpha.12 (2025-07-12) - -Full Changelog: [v0.1.0-alpha.11...v0.1.0-alpha.12](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.11...v0.1.0-alpha.12) - -### Bug Fixes - -* **client:** don't send Content-Type header on GET requests ([d52fbac](https://github.com/sst/opencode-sdk-python/commit/d52fbac0f4e2ae7f3338272eb7075f1401912fe4)) - - -### Chores - -* **readme:** fix version rendering on pypi ([d7ae516](https://github.com/sst/opencode-sdk-python/commit/d7ae5162cc2346314e69fd7609050d0e97eecf6c)) - -## 0.1.0-alpha.11 (2025-07-09) - -Full Changelog: [v0.1.0-alpha.10...v0.1.0-alpha.11](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.10...v0.1.0-alpha.11) - -### Bug Fixes - -* **parsing:** correctly handle nested discriminated unions ([ff5c4a1](https://github.com/sst/opencode-sdk-python/commit/ff5c4a14337714858bd0c193a453fc28f011b263)) - - -### Chores - -* **internal:** bump pinned h11 dep ([6faa22e](https://github.com/sst/opencode-sdk-python/commit/6faa22e132534a89f10a872ead9ce78fd4ab553c)) -* **package:** mark python 3.13 as supported ([5f2edbe](https://github.com/sst/opencode-sdk-python/commit/5f2edbe52d0450a205d69d57e75ee571cabe4b10)) - -## 0.1.0-alpha.10 (2025-07-06) - -Full Changelog: [v0.1.0-alpha.9...v0.1.0-alpha.10](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.9...v0.1.0-alpha.10) - -### Features - -* **api:** manual updates ([fdab2a9](https://github.com/sst/opencode-sdk-python/commit/fdab2a9ee5b71d90b1c18c00f67e40247efae0e4)) - -## 0.1.0-alpha.9 (2025-07-05) - -Full Changelog: [v0.1.0-alpha.8...v0.1.0-alpha.9](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.8...v0.1.0-alpha.9) - -### Features - -* **api:** manual updates ([27a53d3](https://github.com/sst/opencode-sdk-python/commit/27a53d3f43455c8420c1501f3995c140f0bf777d)) - -## 0.1.0-alpha.8 (2025-07-03) - -Full Changelog: [v0.1.0-alpha.7...v0.1.0-alpha.8](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.7...v0.1.0-alpha.8) - -### Features - -* **api:** api update ([6f7ea7f](https://github.com/sst/opencode-sdk-python/commit/6f7ea7f1f813c31e513fbe33d8653fe3e07f7831)) - -## 0.1.0-alpha.7 (2025-07-02) - -Full Changelog: [v0.1.0-alpha.6...v0.1.0-alpha.7](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.6...v0.1.0-alpha.7) - -### Features - -* **api:** update via SDK Studio ([84628c0](https://github.com/sst/opencode-sdk-python/commit/84628c0bd3cd508832f04db0fd8a6cd5367dddf3)) - - -### Chores - -* **ci:** change upload type ([f3019c9](https://github.com/sst/opencode-sdk-python/commit/f3019c94cb548e436b2d7d884969a90db4649f80)) - -## 0.1.0-alpha.6 (2025-06-30) - -Full Changelog: [v0.1.0-alpha.5...v0.1.0-alpha.6](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.5...v0.1.0-alpha.6) - -### Features - -* **api:** update via SDK Studio ([e1cb382](https://github.com/sst/opencode-sdk-python/commit/e1cb382c5391eb135a31ad98c7301c061191c563)) -* **api:** update via SDK Studio ([0985851](https://github.com/sst/opencode-sdk-python/commit/09858518e9312ca72238efd596cc0313927c26e3)) - -## 0.1.0-alpha.5 (2025-06-30) - -Full Changelog: [v0.1.0-alpha.4...v0.1.0-alpha.5](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.4...v0.1.0-alpha.5) - -### Bug Fixes - -* **ci:** correct conditional ([6a748da](https://github.com/sst/opencode-sdk-python/commit/6a748dadf9df2b27b9c1123dc3ef989213f75090)) - - -### Chores - -* **ci:** only run for pushes and fork pull requests ([493f7d2](https://github.com/sst/opencode-sdk-python/commit/493f7d2131e0e17fc2128dad40b327e708f64366)) - -## 0.1.0-alpha.4 (2025-06-27) - -Full Changelog: [v0.1.0-alpha.3...v0.1.0-alpha.4](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.3...v0.1.0-alpha.4) - -### Features - -* **api:** update via SDK Studio ([6a793f7](https://github.com/sst/opencode-sdk-python/commit/6a793f7fd33a34f19656a3e723b61a32b0068a88)) - -## 0.1.0-alpha.3 (2025-06-27) - -Full Changelog: [v0.1.0-alpha.2...v0.1.0-alpha.3](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.2...v0.1.0-alpha.3) - -### Features - -* **api:** update via SDK Studio ([9ab5a57](https://github.com/sst/opencode-sdk-python/commit/9ab5a570a78b28aa0dfbad5e6302f930f2011fed)) -* **api:** update via SDK Studio ([3e426e4](https://github.com/sst/opencode-sdk-python/commit/3e426e4328bd876b3bc5123e20b9a1b69dd1756d)) - -## 0.1.0-alpha.2 (2025-06-27) - -Full Changelog: [v0.1.0-alpha.1...v0.1.0-alpha.2](https://github.com/sst/opencode-sdk-python/compare/v0.1.0-alpha.1...v0.1.0-alpha.2) - -### Features - -* **api:** update via SDK Studio ([a6cf7c5](https://github.com/sst/opencode-sdk-python/commit/a6cf7c5b2a411503294088428ca7918226eca161)) - -## 0.1.0-alpha.1 (2025-06-27) - -Full Changelog: [v0.0.1-alpha.0...v0.1.0-alpha.1](https://github.com/sst/opencode-sdk-python/compare/v0.0.1-alpha.0...v0.1.0-alpha.1) - -### Features - -* **api:** update via SDK Studio ([6e58c71](https://github.com/sst/opencode-sdk-python/commit/6e58c71f2372aa3d44c0d30e0309011ef22a9e04)) -* **api:** update via SDK Studio ([06a27a0](https://github.com/sst/opencode-sdk-python/commit/06a27a02713a8d7bb141e1db844c0b7466818a1d)) -* **api:** update via SDK Studio ([e77f059](https://github.com/sst/opencode-sdk-python/commit/e77f05977e808723ca9df84c481a42f601ca4fd1)) -* **api:** update via SDK Studio ([ff05a4a](https://github.com/sst/opencode-sdk-python/commit/ff05a4adf063d98b3434af29069ea513243071e0)) +* bump `httpx-aiohttp` version to 0.1.9 ([92cb13d](https://github.com/sst/opencode-sdk-python/commit/92cb13de6acb1716afc4e51d0d865058d7eabbe0)) +* do not install brew dependencies in ./scripts/bootstrap by default ([74f0fa2](https://github.com/sst/opencode-sdk-python/commit/74f0fa24042592fdaa27e85a58ae7fc299a8e6e3)) +* **internal:** codegen related update ([c2181c0](https://github.com/sst/opencode-sdk-python/commit/c2181c02870aa2fa62c10a911314904999c296f1)) +* **internal:** codegen related update ([a59b154](https://github.com/sst/opencode-sdk-python/commit/a59b154a2d44291e60f0b34f2a39a1849657b5a7)) +* **internal:** codegen related update ([ab5c493](https://github.com/sst/opencode-sdk-python/commit/ab5c4936b874a1e66f6719b03044aead20db01a1)) +* **internal:** codegen related update ([875138a](https://github.com/sst/opencode-sdk-python/commit/875138a43a23834c7a1428e2a4d5c371af3e0299)) +* **internal:** codegen related update ([0ccd3fe](https://github.com/sst/opencode-sdk-python/commit/0ccd3fe9df64a7bff4a66426d3610c03321aee37)) +* **internal:** codegen related update ([568efca](https://github.com/sst/opencode-sdk-python/commit/568efcac0c2684cceaf8f24673f36b94b00be0e3)) +* **internal:** codegen related update ([04093bb](https://github.com/sst/opencode-sdk-python/commit/04093bbe319065b7869da83fbaa77e9a29038e9c)) +* **internal:** codegen related update ([33f3521](https://github.com/sst/opencode-sdk-python/commit/33f3521d9b17fe39b2f13f24f6edd29e3ff26ef9)) +* **internal:** detect missing future annotations with ruff ([932d414](https://github.com/sst/opencode-sdk-python/commit/932d414bd019219a28c392790c5f031313a6b936)) +* **internal:** move mypy configurations to `pyproject.toml` file ([273dffa](https://github.com/sst/opencode-sdk-python/commit/273dffa4ac0653ed7bdb5455af4a5f640c48f91c)) +* **internal:** update pydantic dependency ([6ae1aed](https://github.com/sst/opencode-sdk-python/commit/6ae1aed3aab37a20707d677b943bdd8c5ee3d4bf)) +* sync repo ([9b3134a](https://github.com/sst/opencode-sdk-python/commit/9b3134a27e8d3db940bf76a6bd4b78321047208a)) +* **tests:** simplify `get_platform` test ([0a47e60](https://github.com/sst/opencode-sdk-python/commit/0a47e601d1cbcf879694ce130d9f2e1729a728bb)) +* **types:** change optional parameter type from NotGiven to Omit ([ed48f59](https://github.com/sst/opencode-sdk-python/commit/ed48f59b5316e425d3ae13e9c493848b37ffc03e)) diff --git a/README.md b/README.md index 0e9425a..6dc0e2a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyPI version](https://img.shields.io/pypi/v/opencode-ai.svg?label=pypi%20(stable))](https://pypi.org/project/opencode-ai/) -The Opencode Python library provides convenient access to the Opencode REST API from any Python 3.8+ +The Opencode Python library provides convenient access to the Opencode REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -116,6 +116,31 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: + +```python +from opencode_ai import Opencode + +client = Opencode() + +response = client.session.prompt( + id="id", + parts=[ + { + "text": "text", + "type": "text", + } + ], + model={ + "model_id": "modelID", + "provider_id": "providerID", + }, +) +print(response.model) +``` + ## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `opencode_ai.APIConnectionError` is raised. @@ -367,7 +392,7 @@ print(opencode_ai.__version__) ## Requirements -Python 3.8 or higher. +Python 3.9 or higher. ## Contributing diff --git a/api.md b/api.md index a254328..7b123d7 100644 --- a/api.md +++ b/api.md @@ -14,32 +14,44 @@ from opencode_ai.types import EventListResponse Methods: -- client.event.list() -> EventListResponse +- client.event.list(\*\*params) -> EventListResponse + +# Path + +Types: + +```python +from opencode_ai.types import Path +``` + +Methods: + +- client.path.get(\*\*params) -> Path # App Types: ```python -from opencode_ai.types import ( - App, - Mode, - Model, - Provider, - AppInitResponse, - AppLogResponse, - AppModesResponse, - AppProvidersResponse, -) +from opencode_ai.types import Model, Provider, AppLogResponse, AppProvidersResponse ``` Methods: -- client.app.get() -> App -- client.app.init() -> AppInitResponse - client.app.log(\*\*params) -> AppLogResponse -- client.app.modes() -> AppModesResponse -- client.app.providers() -> AppProvidersResponse +- client.app.providers(\*\*params) -> AppProvidersResponse + +# Agent + +Types: + +```python +from opencode_ai.types import Agent, AgentListResponse +``` + +Methods: + +- client.agent.list(\*\*params) -> AgentListResponse # Find @@ -60,25 +72,51 @@ Methods: Types: ```python -from opencode_ai.types import File, FileReadResponse, FileStatusResponse +from opencode_ai.types import File, FileNode, FileListResponse, FileReadResponse, FileStatusResponse ``` Methods: -- client.file.read(\*\*params) -> FileReadResponse -- client.file.status() -> FileStatusResponse +- client.file.list(\*\*params) -> FileListResponse +- client.file.read(\*\*params) -> FileReadResponse +- client.file.status(\*\*params) -> FileStatusResponse # Config Types: ```python -from opencode_ai.types import Config, KeybindsConfig, McpLocalConfig, McpRemoteConfig, ModeConfig +from opencode_ai.types import Config, KeybindsConfig, McpLocalConfig, McpRemoteConfig +``` + +Methods: + +- client.config.get(\*\*params) -> Config + +# Command + +Types: + +```python +from opencode_ai.types import Command, CommandListResponse +``` + +Methods: + +- client.command.list(\*\*params) -> CommandListResponse + +# Project + +Types: + +```python +from opencode_ai.types import Project, ProjectListResponse ``` Methods: -- client.config.get() -> Config +- client.project.list(\*\*params) -> ProjectListResponse +- client.project.current(\*\*params) -> Project # Session @@ -86,6 +124,8 @@ Types: ```python from opencode_ai.types import ( + AgentPart, + AgentPartInput, AssistantMessage, FilePart, FilePartInput, @@ -94,6 +134,7 @@ from opencode_ai.types import ( FileSource, Message, Part, + ReasoningPart, Session, SnapshotPart, StepFinishPart, @@ -110,36 +151,75 @@ from opencode_ai.types import ( SessionListResponse, SessionDeleteResponse, SessionAbortResponse, + SessionChildrenResponse, + SessionCommandResponse, SessionInitResponse, + SessionMessageResponse, SessionMessagesResponse, + SessionPromptResponse, SessionSummarizeResponse, ) ``` Methods: -- client.session.create() -> Session -- client.session.list() -> SessionListResponse -- client.session.delete(id) -> SessionDeleteResponse -- client.session.abort(id) -> SessionAbortResponse -- client.session.chat(id, \*\*params) -> AssistantMessage -- client.session.init(id, \*\*params) -> SessionInitResponse -- client.session.messages(id) -> SessionMessagesResponse -- client.session.revert(id, \*\*params) -> Session -- client.session.share(id) -> Session -- client.session.summarize(id, \*\*params) -> SessionSummarizeResponse -- client.session.unrevert(id) -> Session -- client.session.unshare(id) -> Session +- client.session.create(\*\*params) -> Session +- client.session.update(id, \*\*params) -> Session +- client.session.list(\*\*params) -> SessionListResponse +- client.session.delete(id, \*\*params) -> SessionDeleteResponse +- client.session.abort(id, \*\*params) -> SessionAbortResponse +- client.session.children(id, \*\*params) -> SessionChildrenResponse +- client.session.command(id, \*\*params) -> SessionCommandResponse +- client.session.get(id, \*\*params) -> Session +- client.session.init(id, \*\*params) -> SessionInitResponse +- client.session.message(message_id, \*, id, \*\*params) -> SessionMessageResponse +- client.session.messages(id, \*\*params) -> SessionMessagesResponse +- client.session.prompt(id, \*\*params) -> SessionPromptResponse +- client.session.revert(id, \*\*params) -> Session +- client.session.share(id, \*\*params) -> Session +- client.session.shell(id, \*\*params) -> AssistantMessage +- client.session.summarize(id, \*\*params) -> SessionSummarizeResponse +- client.session.unrevert(id, \*\*params) -> Session +- client.session.unshare(id, \*\*params) -> Session + +## Permissions + +Types: + +```python +from opencode_ai.types.session import Permission, PermissionRespondResponse +``` + +Methods: + +- client.session.permissions.respond(permission_id, \*, id, \*\*params) -> PermissionRespondResponse # Tui Types: ```python -from opencode_ai.types import TuiAppendPromptResponse, TuiOpenHelpResponse +from opencode_ai.types import ( + TuiAppendPromptResponse, + TuiClearPromptResponse, + TuiExecuteCommandResponse, + TuiOpenHelpResponse, + TuiOpenModelsResponse, + TuiOpenSessionsResponse, + TuiOpenThemesResponse, + TuiShowToastResponse, + TuiSubmitPromptResponse, +) ``` Methods: - client.tui.append_prompt(\*\*params) -> TuiAppendPromptResponse -- client.tui.open_help() -> TuiOpenHelpResponse +- client.tui.clear_prompt(\*\*params) -> TuiClearPromptResponse +- client.tui.execute_command(\*\*params) -> TuiExecuteCommandResponse +- client.tui.open_help(\*\*params) -> TuiOpenHelpResponse +- client.tui.open_models(\*\*params) -> TuiOpenModelsResponse +- client.tui.open_sessions(\*\*params) -> TuiOpenSessionsResponse +- client.tui.open_themes(\*\*params) -> TuiOpenThemesResponse +- client.tui.show_toast(\*\*params) -> TuiShowToastResponse +- client.tui.submit_prompt(\*\*params) -> TuiSubmitPromptResponse diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 34af79f..0000000 --- a/mypy.ini +++ /dev/null @@ -1,50 +0,0 @@ -[mypy] -pretty = True -show_error_codes = True - -# Exclude _files.py because mypy isn't smart enough to apply -# the correct type narrowing and as this is an internal module -# it's fine to just use Pyright. -# -# We also exclude our `tests` as mypy doesn't always infer -# types correctly and Pyright will still catch any type errors. -exclude = ^(src/opencode_ai/_files\.py|_dev/.*\.py|tests/.*)$ - -strict_equality = True -implicit_reexport = True -check_untyped_defs = True -no_implicit_optional = True - -warn_return_any = True -warn_unreachable = True -warn_unused_configs = True - -# Turn these options off as it could cause conflicts -# with the Pyright options. -warn_unused_ignores = False -warn_redundant_casts = False - -disallow_any_generics = True -disallow_untyped_defs = True -disallow_untyped_calls = True -disallow_subclassing_any = True -disallow_incomplete_defs = True -disallow_untyped_decorators = True -cache_fine_grained = True - -# By default, mypy reports an error if you assign a value to the result -# of a function call that doesn't return anything. We do this in our test -# cases: -# ``` -# result = ... -# assert result is None -# ``` -# Changing this codegen to make mypy happy would increase complexity -# and would not be worth it. -disable_error_code = func-returns-value,overload-cannot-match - -# https://github.com/python/mypy/issues/12162 -[mypy.overrides] -module = "black.files.*" -ignore_errors = true -ignore_missing_imports = true diff --git a/pyproject.toml b/pyproject.toml index f5c99e3..b42a659 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,30 +1,32 @@ [project] name = "opencode-ai" -version = "0.1.0-alpha.36" +version = "0.1.0-alpha.37" description = "The official Python library for the opencode API" dynamic = ["readme"] license = "MIT" authors = [ { name = "Opencode", email = "support@sst.dev" }, ] + dependencies = [ - "httpx>=0.23.0, <1", - "pydantic>=1.9.0, <3", - "typing-extensions>=4.10, <5", - "anyio>=3.5.0, <5", - "distro>=1.7.0, <2", - "sniffio", + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.10, <5", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", ] -requires-python = ">= 3.8" + +requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", @@ -39,14 +41,14 @@ Homepage = "https://github.com/sst/opencode-sdk-python" Repository = "https://github.com/sst/opencode-sdk-python" [project.optional-dependencies] -aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] [tool.rye] managed = true # version pins are in requirements-dev.lock dev-dependencies = [ "pyright==1.1.399", - "mypy", + "mypy==1.17", "respx", "pytest", "pytest-asyncio", @@ -56,7 +58,6 @@ dev-dependencies = [ "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", "rich>=13.7.1", - "nest_asyncio==1.6.0", "pytest-xdist>=3.6.1", ] @@ -142,7 +143,7 @@ filterwarnings = [ # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.8" +pythonVersion = "3.9" exclude = [ "_dev", @@ -157,6 +158,58 @@ reportOverlappingOverload = false reportImportCycles = false reportPrivateUsage = false +[tool.mypy] +pretty = true +show_error_codes = true + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ['src/opencode_ai/_files.py', '_dev/.*.py', 'tests/.*'] + +strict_equality = true +implicit_reexport = true +check_untyped_defs = true +no_implicit_optional = true + +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = false +warn_redundant_casts = false + +disallow_any_generics = true +disallow_untyped_defs = true +disallow_untyped_calls = true +disallow_subclassing_any = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +cache_fine_grained = true + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = "func-returns-value,overload-cannot-match" + +# https://github.com/python/mypy/issues/12162 +[[tool.mypy.overrides]] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true + + [tool.ruff] line-length = 120 output-format = "grouped" @@ -173,6 +226,8 @@ select = [ "B", # remove unused imports "F401", + # check for missing future annotations + "FA102", # bare except statements "E722", # unused arguments @@ -195,6 +250,8 @@ unfixable = [ "T203", ] +extend-safe-fixes = ["FA102"] + [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" diff --git a/requirements-dev.lock b/requirements-dev.lock index d88557d..059e0e1 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,40 +12,45 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.2 # via httpx-aiohttp # via opencode-ai -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.0 # via httpx # via opencode-ai -argcomplete==3.1.2 +argcomplete==3.6.3 # via nox async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 + # via nox +backports-asyncio-runner==1.2.0 + # via pytest-asyncio +certifi==2025.11.12 # via httpcore # via httpx -colorlog==6.7.0 +colorlog==6.10.1 + # via nox +dependency-groups==1.3.1 # via nox -dirty-equals==0.6.0 -distlib==0.3.7 +dirty-equals==0.11 +distlib==0.4.0 # via virtualenv -distro==1.8.0 +distro==1.9.0 # via opencode-ai -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio # via pytest -execnet==2.1.1 +execnet==2.1.2 # via pytest-xdist -filelock==3.12.4 +filelock==3.19.1 # via virtualenv -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -56,80 +61,89 @@ httpx==0.28.1 # via httpx-aiohttp # via opencode-ai # via respx -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via opencode-ai -idna==3.4 +humanize==4.13.0 + # via nox +idna==3.11 # via anyio # via httpx # via yarl -importlib-metadata==7.0.0 -iniconfig==2.0.0 +importlib-metadata==8.7.0 +iniconfig==2.1.0 # via pytest markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl -mypy==1.14.1 -mypy-extensions==1.0.0 +mypy==1.17.0 +mypy-extensions==1.1.0 # via mypy -nest-asyncio==1.6.0 -nodeenv==1.8.0 +nodeenv==1.9.1 # via pyright -nox==2023.4.22 -packaging==23.2 +nox==2025.11.12 +packaging==25.0 + # via dependency-groups # via nox # via pytest -platformdirs==3.11.0 +pathspec==0.12.1 + # via mypy +platformdirs==4.4.0 # via virtualenv -pluggy==1.5.0 +pluggy==1.6.0 # via pytest -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.12.5 # via opencode-ai -pydantic-core==2.27.1 +pydantic-core==2.41.5 # via pydantic -pygments==2.18.0 +pygments==2.19.2 + # via pytest # via rich pyright==1.1.399 -pytest==8.3.3 +pytest==8.4.2 # via pytest-asyncio # via pytest-xdist -pytest-asyncio==0.24.0 -pytest-xdist==3.7.0 -python-dateutil==2.8.2 +pytest-asyncio==1.2.0 +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 # via time-machine -pytz==2023.3.post1 - # via dirty-equals respx==0.22.0 -rich==13.7.1 -ruff==0.9.4 -setuptools==68.2.2 - # via nodeenv -six==1.16.0 +rich==14.2.0 +ruff==0.14.7 +six==1.17.0 # via python-dateutil -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via opencode-ai -time-machine==2.9.0 -tomli==2.0.2 +time-machine==2.19.0 +tomli==2.3.0 + # via dependency-groups # via mypy + # via nox # via pytest -typing-extensions==4.12.2 +typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via multidict # via mypy # via opencode-ai # via pydantic # via pydantic-core # via pyright -virtualenv==20.24.5 + # via pytest-asyncio + # via typing-inspection + # via virtualenv +typing-inspection==0.4.2 + # via pydantic +virtualenv==20.35.4 # via nox -yarl==1.20.0 +yarl==1.22.0 # via aiohttp -zipp==3.17.0 +zipp==3.23.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 0ab22ae..2a3b675 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,28 +12,28 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.12.8 +aiohttp==3.13.2 # via httpx-aiohttp # via opencode-ai -aiosignal==1.3.2 +aiosignal==1.4.0 # via aiohttp -annotated-types==0.6.0 +annotated-types==0.7.0 # via pydantic -anyio==4.4.0 +anyio==4.12.0 # via httpx # via opencode-ai async-timeout==5.0.1 # via aiohttp -attrs==25.3.0 +attrs==25.4.0 # via aiohttp -certifi==2023.7.22 +certifi==2025.11.12 # via httpcore # via httpx -distro==1.8.0 +distro==1.9.0 # via opencode-ai -exceptiongroup==1.2.2 +exceptiongroup==1.3.1 # via anyio -frozenlist==1.6.2 +frozenlist==1.8.0 # via aiohttp # via aiosignal h11==0.16.0 @@ -43,30 +43,34 @@ httpcore==1.0.9 httpx==0.28.1 # via httpx-aiohttp # via opencode-ai -httpx-aiohttp==0.1.8 +httpx-aiohttp==0.1.9 # via opencode-ai -idna==3.4 +idna==3.11 # via anyio # via httpx # via yarl -multidict==6.4.4 +multidict==6.7.0 # via aiohttp # via yarl -propcache==0.3.1 +propcache==0.4.1 # via aiohttp # via yarl -pydantic==2.10.3 +pydantic==2.12.5 # via opencode-ai -pydantic-core==2.27.1 +pydantic-core==2.41.5 # via pydantic -sniffio==1.3.0 - # via anyio +sniffio==1.3.1 # via opencode-ai -typing-extensions==4.12.2 +typing-extensions==4.15.0 + # via aiosignal # via anyio + # via exceptiongroup # via multidict # via opencode-ai # via pydantic # via pydantic-core -yarl==1.20.0 + # via typing-inspection +typing-inspection==0.4.2 + # via pydantic +yarl==1.22.0 # via aiohttp diff --git a/scripts/bootstrap b/scripts/bootstrap index e84fe62..b430fee 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependencies…" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi diff --git a/src/opencode/lib/.keep b/src/opencode/lib/.keep deleted file mode 100644 index 5e2c99f..0000000 --- a/src/opencode/lib/.keep +++ /dev/null @@ -1,4 +0,0 @@ -File generated from our OpenAPI spec by Stainless. - -This directory can be used to store custom files to expand the SDK. -It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/opencode_ai/__init__.py b/src/opencode_ai/__init__.py index 7d8c13c..7debe41 100644 --- a/src/opencode_ai/__init__.py +++ b/src/opencode_ai/__init__.py @@ -3,7 +3,7 @@ import typing as _t from . import types -from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path from ._client import ( Client, @@ -48,7 +48,9 @@ "ProxiesTypes", "NotGiven", "NOT_GIVEN", + "not_given", "Omit", + "omit", "OpencodeError", "APIError", "APIStatusError", diff --git a/src/opencode_ai/_base_client.py b/src/opencode_ai/_base_client.py index eb05756..c8a1977 100644 --- a/src/opencode_ai/_base_client.py +++ b/src/opencode_ai/_base_client.py @@ -42,7 +42,6 @@ from ._qs import Querystring from ._files import to_httpx_files, async_to_httpx_files from ._types import ( - NOT_GIVEN, Body, Omit, Query, @@ -57,9 +56,10 @@ RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, + not_given, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import PYDANTIC_V2, model_copy, model_dump +from ._compat import PYDANTIC_V1, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -145,9 +145,9 @@ def __init__( def __init__( self, *, - url: URL | NotGiven = NOT_GIVEN, - json: Body | NotGiven = NOT_GIVEN, - params: Query | NotGiven = NOT_GIVEN, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, ) -> None: self.url = url self.json = json @@ -232,7 +232,7 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: - if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model @@ -320,7 +320,7 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: - if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model @@ -595,7 +595,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques # we internally support defining a temporary header to override the # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` # see _response.py for implementation details - override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) if is_given(override_cast_to): options.headers = headers return cast(Type[ResponseT], override_cast_to) @@ -825,7 +825,7 @@ def __init__( version: str, base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1356,7 +1356,7 @@ def __init__( base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, @@ -1818,8 +1818,8 @@ def make_request_options( extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - post_parser: PostParser | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} diff --git a/src/opencode_ai/_client.py b/src/opencode_ai/_client.py index cabe5b7..3a34a0a 100644 --- a/src/opencode_ai/_client.py +++ b/src/opencode_ai/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Union, Mapping +from typing import Any, Mapping from typing_extensions import Self, override import httpx @@ -11,17 +11,17 @@ from . import _exceptions from ._qs import Querystring from ._types import ( - NOT_GIVEN, Omit, Timeout, NotGiven, Transport, ProxiesTypes, RequestOptions, + not_given, ) from ._utils import is_given, get_async_library from ._version import __version__ -from .resources import app, tui, file, find, event, config, session +from .resources import app, tui, file, find, path, agent, event, config, command, project from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError from ._base_client import ( @@ -29,6 +29,7 @@ SyncAPIClient, AsyncAPIClient, ) +from .resources.session import session __all__ = [ "Timeout", @@ -44,10 +45,14 @@ class Opencode(SyncAPIClient): event: event.EventResource + path: path.PathResource app: app.AppResource + agent: agent.AgentResource find: find.FindResource file: file.FileResource config: config.ConfigResource + command: command.CommandResource + project: project.ProjectResource session: session.SessionResource tui: tui.TuiResource with_raw_response: OpencodeWithRawResponse @@ -59,7 +64,7 @@ def __init__( self, *, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -97,10 +102,14 @@ def __init__( self._default_stream_cls = Stream self.event = event.EventResource(self) + self.path = path.PathResource(self) self.app = app.AppResource(self) + self.agent = agent.AgentResource(self) self.find = find.FindResource(self) self.file = file.FileResource(self) self.config = config.ConfigResource(self) + self.command = command.CommandResource(self) + self.project = project.ProjectResource(self) self.session = session.SessionResource(self) self.tui = tui.TuiResource(self) self.with_raw_response = OpencodeWithRawResponse(self) @@ -124,9 +133,9 @@ def copy( self, *, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -205,10 +214,14 @@ def _make_status_error( class AsyncOpencode(AsyncAPIClient): event: event.AsyncEventResource + path: path.AsyncPathResource app: app.AsyncAppResource + agent: agent.AsyncAgentResource find: find.AsyncFindResource file: file.AsyncFileResource config: config.AsyncConfigResource + command: command.AsyncCommandResource + project: project.AsyncProjectResource session: session.AsyncSessionResource tui: tui.AsyncTuiResource with_raw_response: AsyncOpencodeWithRawResponse @@ -220,7 +233,7 @@ def __init__( self, *, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -258,10 +271,14 @@ def __init__( self._default_stream_cls = AsyncStream self.event = event.AsyncEventResource(self) + self.path = path.AsyncPathResource(self) self.app = app.AsyncAppResource(self) + self.agent = agent.AsyncAgentResource(self) self.find = find.AsyncFindResource(self) self.file = file.AsyncFileResource(self) self.config = config.AsyncConfigResource(self) + self.command = command.AsyncCommandResource(self) + self.project = project.AsyncProjectResource(self) self.session = session.AsyncSessionResource(self) self.tui = tui.AsyncTuiResource(self) self.with_raw_response = AsyncOpencodeWithRawResponse(self) @@ -285,9 +302,9 @@ def copy( self, *, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -367,10 +384,14 @@ def _make_status_error( class OpencodeWithRawResponse: def __init__(self, client: Opencode) -> None: self.event = event.EventResourceWithRawResponse(client.event) + self.path = path.PathResourceWithRawResponse(client.path) self.app = app.AppResourceWithRawResponse(client.app) + self.agent = agent.AgentResourceWithRawResponse(client.agent) self.find = find.FindResourceWithRawResponse(client.find) self.file = file.FileResourceWithRawResponse(client.file) self.config = config.ConfigResourceWithRawResponse(client.config) + self.command = command.CommandResourceWithRawResponse(client.command) + self.project = project.ProjectResourceWithRawResponse(client.project) self.session = session.SessionResourceWithRawResponse(client.session) self.tui = tui.TuiResourceWithRawResponse(client.tui) @@ -378,10 +399,14 @@ def __init__(self, client: Opencode) -> None: class AsyncOpencodeWithRawResponse: def __init__(self, client: AsyncOpencode) -> None: self.event = event.AsyncEventResourceWithRawResponse(client.event) + self.path = path.AsyncPathResourceWithRawResponse(client.path) self.app = app.AsyncAppResourceWithRawResponse(client.app) + self.agent = agent.AsyncAgentResourceWithRawResponse(client.agent) self.find = find.AsyncFindResourceWithRawResponse(client.find) self.file = file.AsyncFileResourceWithRawResponse(client.file) self.config = config.AsyncConfigResourceWithRawResponse(client.config) + self.command = command.AsyncCommandResourceWithRawResponse(client.command) + self.project = project.AsyncProjectResourceWithRawResponse(client.project) self.session = session.AsyncSessionResourceWithRawResponse(client.session) self.tui = tui.AsyncTuiResourceWithRawResponse(client.tui) @@ -389,10 +414,14 @@ def __init__(self, client: AsyncOpencode) -> None: class OpencodeWithStreamedResponse: def __init__(self, client: Opencode) -> None: self.event = event.EventResourceWithStreamingResponse(client.event) + self.path = path.PathResourceWithStreamingResponse(client.path) self.app = app.AppResourceWithStreamingResponse(client.app) + self.agent = agent.AgentResourceWithStreamingResponse(client.agent) self.find = find.FindResourceWithStreamingResponse(client.find) self.file = file.FileResourceWithStreamingResponse(client.file) self.config = config.ConfigResourceWithStreamingResponse(client.config) + self.command = command.CommandResourceWithStreamingResponse(client.command) + self.project = project.ProjectResourceWithStreamingResponse(client.project) self.session = session.SessionResourceWithStreamingResponse(client.session) self.tui = tui.TuiResourceWithStreamingResponse(client.tui) @@ -400,10 +429,14 @@ def __init__(self, client: Opencode) -> None: class AsyncOpencodeWithStreamedResponse: def __init__(self, client: AsyncOpencode) -> None: self.event = event.AsyncEventResourceWithStreamingResponse(client.event) + self.path = path.AsyncPathResourceWithStreamingResponse(client.path) self.app = app.AsyncAppResourceWithStreamingResponse(client.app) + self.agent = agent.AsyncAgentResourceWithStreamingResponse(client.agent) self.find = find.AsyncFindResourceWithStreamingResponse(client.find) self.file = file.AsyncFileResourceWithStreamingResponse(client.file) self.config = config.AsyncConfigResourceWithStreamingResponse(client.config) + self.command = command.AsyncCommandResourceWithStreamingResponse(client.command) + self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) self.session = session.AsyncSessionResourceWithStreamingResponse(client.session) self.tui = tui.AsyncTuiResourceWithStreamingResponse(client.tui) diff --git a/src/opencode_ai/_compat.py b/src/opencode_ai/_compat.py index 92d9ee6..bdef67f 100644 --- a/src/opencode_ai/_compat.py +++ b/src/opencode_ai/_compat.py @@ -12,14 +12,13 @@ _T = TypeVar("_T") _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) -# --------------- Pydantic v2 compatibility --------------- +# --------------- Pydantic v2, v3 compatibility --------------- # Pyright incorrectly reports some of our functions as overriding a method when they don't # pyright: reportIncompatibleMethodOverride=false -PYDANTIC_V2 = pydantic.VERSION.startswith("2.") +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") -# v1 re-exports if TYPE_CHECKING: def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 @@ -44,90 +43,92 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 ... else: - if PYDANTIC_V2: - from pydantic.v1.typing import ( + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( get_args as get_args, is_union as is_union, get_origin as get_origin, is_typeddict as is_typeddict, is_literal_type as is_literal_type, ) - from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime else: - from pydantic.typing import ( + from ._utils import ( get_args as get_args, is_union as is_union, get_origin as get_origin, + parse_date as parse_date, is_typeddict as is_typeddict, + parse_datetime as parse_datetime, is_literal_type as is_literal_type, ) - from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # refactored config if TYPE_CHECKING: from pydantic import ConfigDict as ConfigDict else: - if PYDANTIC_V2: - from pydantic import ConfigDict - else: + if PYDANTIC_V1: # TODO: provide an error message here? ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict # renamed methods / properties def parse_obj(model: type[_ModelT], value: object) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(value) - else: + if PYDANTIC_V1: return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) def field_is_required(field: FieldInfo) -> bool: - if PYDANTIC_V2: - return field.is_required() - return field.required # type: ignore + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() def field_get_default(field: FieldInfo) -> Any: value = field.get_default() - if PYDANTIC_V2: - from pydantic_core import PydanticUndefined - - if value == PydanticUndefined: - return None + if PYDANTIC_V1: return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None return value def field_outer_type(field: FieldInfo) -> Any: - if PYDANTIC_V2: - return field.annotation - return field.outer_type_ # type: ignore + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation def get_model_config(model: type[pydantic.BaseModel]) -> Any: - if PYDANTIC_V2: - return model.model_config - return model.__config__ # type: ignore + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: - if PYDANTIC_V2: - return model.model_fields - return model.__fields__ # type: ignore + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: - if PYDANTIC_V2: - return model.model_copy(deep=deep) - return model.copy(deep=deep) # type: ignore + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: - if PYDANTIC_V2: - return model.model_dump_json(indent=indent) - return model.json(indent=indent) # type: ignore + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) def model_dump( @@ -139,14 +140,14 @@ def model_dump( warnings: bool = True, mode: Literal["json", "python"] = "python", ) -> dict[str, Any]: - if PYDANTIC_V2 or hasattr(model, "model_dump"): + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): return model.model_dump( mode=mode, exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 - warnings=warnings if PYDANTIC_V2 else True, + warnings=True if PYDANTIC_V1 else warnings, ) return cast( "dict[str, Any]", @@ -159,9 +160,9 @@ def model_dump( def model_parse(model: type[_ModelT], data: Any) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(data) - return model.parse_obj(data) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) # generic models @@ -170,17 +171,16 @@ def model_parse(model: type[_ModelT], data: Any) -> _ModelT: class GenericModel(pydantic.BaseModel): ... else: - if PYDANTIC_V2: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: # there no longer needs to be a distinction in v2 but # we still have to create our own subclass to avoid # inconsistent MRO ordering errors class GenericModel(pydantic.BaseModel): ... - else: - import pydantic.generics - - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... - # cached properties if TYPE_CHECKING: diff --git a/src/opencode_ai/_models.py b/src/opencode_ai/_models.py index 92f7c10..ca9500b 100644 --- a/src/opencode_ai/_models.py +++ b/src/opencode_ai/_models.py @@ -2,6 +2,7 @@ import os import inspect +import weakref from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( @@ -50,7 +51,7 @@ strip_annotated_type, ) from ._compat import ( - PYDANTIC_V2, + PYDANTIC_V1, ConfigDict, GenericModel as BaseGenericModel, get_args, @@ -81,11 +82,7 @@ class _ConfigProtocol(Protocol): class BaseModel(pydantic.BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) - ) - else: + if PYDANTIC_V1: @property @override @@ -95,6 +92,10 @@ def model_fields_set(self) -> set[str]: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) def to_dict( self, @@ -215,25 +216,25 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] 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] = parsed - else: + if PYDANTIC_V1: _fields_set.add(key) fields_values[key] = parsed + else: + _extra[key] = parsed object.__setattr__(m, "__dict__", fields_values) - if PYDANTIC_V2: - # these properties are copied from Pydantic's `model_construct()` method - object.__setattr__(m, "__pydantic_private__", None) - object.__setattr__(m, "__pydantic_extra__", _extra) - object.__setattr__(m, "__pydantic_fields_set__", _fields_set) - else: + if PYDANTIC_V1: # init_private_attributes() does not exist in v2 m._init_private_attributes() # type: ignore # copied from Pydantic v1's `construct()` method object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) return m @@ -243,7 +244,7 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] # although not in practice model_construct = construct - if not PYDANTIC_V2: + if PYDANTIC_V1: # we define aliases for some of the new pydantic v2 methods so # that we can just document these methods without having to specify # a specific pydantic version as some users may not know which @@ -256,13 +257,15 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + context: Any | None = None, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, + fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -271,16 +274,24 @@ def model_dump( Args: mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. @@ -295,10 +306,14 @@ def model_dump( 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") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -311,15 +326,18 @@ def model_dump_json( self, *, indent: int | None = None, + ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + context: Any | None = None, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, + fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -348,11 +366,17 @@ def model_dump_json( 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") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -363,10 +387,10 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) - if PYDANTIC_V2: - type_ = field.annotation - else: + if PYDANTIC_V1: type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore if type_ is None: raise RuntimeError(f"Unexpected field type is None for {key}") @@ -375,7 +399,7 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: - if not PYDANTIC_V2: + if PYDANTIC_V1: # TODO return None @@ -567,6 +591,9 @@ class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. @@ -609,8 +636,9 @@ def __init__( def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached discriminator_field_name: str | None = None @@ -628,30 +656,30 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, for variant in get_args(union): variant = strip_annotated_type(variant) if is_basemodel_type(variant): - if PYDANTIC_V2: - field = _extract_field_schema_pv2(variant, discriminator_field_name) - if not field: + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field.get("serialization_alias") - - field_schema = field["schema"] + discriminator_alias = field_info.alias - if field_schema["type"] == "literal": - for entry in cast("LiteralSchema", field_schema)["expected"]: + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): if isinstance(entry, str): mapping[entry] = variant else: - field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - if not field_info: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field_info.alias + discriminator_alias = field.get("serialization_alias") - if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): - for entry in get_args(annotation): + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: if isinstance(entry, str): mapping[entry] = variant @@ -663,7 +691,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) - cast(CachedDiscriminatorType, union).__discriminator__ = details + DISCRIMINATOR_CACHE.setdefault(union, details) return details @@ -714,7 +742,7 @@ class GenericModel(BaseGenericModel, BaseModel): pass -if PYDANTIC_V2: +if not PYDANTIC_V1: from pydantic import TypeAdapter as _TypeAdapter _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) @@ -782,12 +810,12 @@ class FinalRequestOptions(pydantic.BaseModel): json_data: Union[Body, None] = None extra_json: Union[AnyMapping, None] = None - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) - else: + if PYDANTIC_V1: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) def get_max_retries(self, max_retries: int) -> int: if isinstance(self.max_retries, NotGiven): @@ -820,9 +848,9 @@ def construct( # type: ignore key: strip_not_given(value) for key, value in values.items() } - if PYDANTIC_V2: - return super().model_construct(_fields_set, **kwargs) - return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) if not TYPE_CHECKING: # type checkers incorrectly complain about this assignment diff --git a/src/opencode_ai/_qs.py b/src/opencode_ai/_qs.py index 274320c..ada6fd3 100644 --- a/src/opencode_ai/_qs.py +++ b/src/opencode_ai/_qs.py @@ -4,7 +4,7 @@ from urllib.parse import parse_qs, urlencode from typing_extensions import Literal, get_args -from ._types import NOT_GIVEN, NotGiven, NotGivenOr +from ._types import NotGiven, not_given from ._utils import flatten _T = TypeVar("_T") @@ -41,8 +41,8 @@ def stringify( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> str: return urlencode( self.stringify_items( @@ -56,8 +56,8 @@ def stringify_items( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> list[tuple[str, str]]: opts = Options( qs=self, @@ -143,8 +143,8 @@ def __init__( self, qs: Querystring = _qs, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> None: self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/opencode_ai/_streaming.py b/src/opencode_ai/_streaming.py index 34499b5..ae72550 100644 --- a/src/opencode_ai/_streaming.py +++ b/src/opencode_ai/_streaming.py @@ -54,12 +54,12 @@ def __stream__(self) -> Iterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # Ensure the entire stream is consumed - for _sse in iterator: - ... + try: + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() def __enter__(self) -> Self: return self @@ -118,12 +118,12 @@ async def __stream__(self) -> AsyncIterator[_T]: process_data = self._client._process_response_data iterator = self._iter_events() - async for sse in iterator: - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - # Ensure the entire stream is consumed - async for _sse in iterator: - ... + try: + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() async def __aenter__(self) -> Self: return self diff --git a/src/opencode_ai/_types.py b/src/opencode_ai/_types.py index e9ba7eb..3a1e658 100644 --- a/src/opencode_ai/_types.py +++ b/src/opencode_ai/_types.py @@ -13,10 +13,21 @@ Mapping, TypeVar, Callable, + Iterator, Optional, Sequence, ) -from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) import httpx import pydantic @@ -106,18 +117,21 @@ class RequestOptions(TypedDict, total=False): # Sentinel class used until PEP 0661 is accepted class NotGiven: """ - A sentinel singleton class used to distinguish omitted keyword arguments - from those passed in with the value None (which may have different behavior). + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + def create(timeout: Timeout | None | NotGiven = not_given): ... - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior ``` """ @@ -129,13 +143,14 @@ def __repr__(self) -> str: return "NOT_GIVEN" -NotGivenOr = Union[_T, NotGiven] +not_given = NotGiven() +# for backwards compatibility: NOT_GIVEN = NotGiven() class Omit: - """In certain situations you need to be able to represent a case where a default value has - to be explicitly removed and `None` is not an appropriate substitute, for example: + """ + To explicitly omit something from being sent in a request, use `omit`. ```py # as the default `Content-Type` header is `application/json` that will be sent @@ -145,8 +160,8 @@ class Omit: # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' client.post(..., headers={"Content-Type": "multipart/form-data"}) - # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={"Content-Type": Omit()}) + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) ``` """ @@ -154,6 +169,9 @@ def __bool__(self) -> Literal[False]: return False +omit = Omit() + + @runtime_checkable class ModelBuilderProtocol(Protocol): @classmethod @@ -217,3 +235,27 @@ class _GenericAlias(Protocol): class HttpxSendArgs(TypedDict, total=False): auth: httpx.Auth follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/src/opencode_ai/_utils/__init__.py b/src/opencode_ai/_utils/__init__.py index d4fda26..dc64e29 100644 --- a/src/opencode_ai/_utils/__init__.py +++ b/src/opencode_ai/_utils/__init__.py @@ -10,7 +10,6 @@ lru_cache as lru_cache, is_mapping as is_mapping, is_tuple_t as is_tuple_t, - parse_date as parse_date, is_iterable as is_iterable, is_sequence as is_sequence, coerce_float as coerce_float, @@ -23,7 +22,6 @@ coerce_boolean as coerce_boolean, coerce_integer as coerce_integer, file_from_path as file_from_path, - parse_datetime as parse_datetime, strip_not_given as strip_not_given, deepcopy_minimal as deepcopy_minimal, get_async_library as get_async_library, @@ -32,12 +30,20 @@ maybe_coerce_boolean as maybe_coerce_boolean, maybe_coerce_integer as maybe_coerce_integer, ) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) from ._typing import ( is_list_type as is_list_type, is_union_type as is_union_type, extract_type_arg as extract_type_arg, is_iterable_type as is_iterable_type, is_required_type as is_required_type, + is_sequence_type as is_sequence_type, is_annotated_type as is_annotated_type, is_type_alias_type as is_type_alias_type, strip_annotated_type as strip_annotated_type, @@ -55,3 +61,4 @@ function_has_argument as function_has_argument, assert_signatures_in_sync as assert_signatures_in_sync, ) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/src/opencode_ai/_utils/_compat.py b/src/opencode_ai/_utils/_compat.py new file mode 100644 index 0000000..dd70323 --- /dev/null +++ b/src/opencode_ai/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/src/opencode_ai/_utils/_datetime_parse.py b/src/opencode_ai/_utils/_datetime_parse.py new file mode 100644 index 0000000..7cb9d9e --- /dev/null +++ b/src/opencode_ai/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/src/opencode_ai/_utils/_sync.py b/src/opencode_ai/_utils/_sync.py index ad7ec71..f6027c1 100644 --- a/src/opencode_ai/_utils/_sync.py +++ b/src/opencode_ai/_utils/_sync.py @@ -1,10 +1,8 @@ from __future__ import annotations -import sys import asyncio import functools -import contextvars -from typing import Any, TypeVar, Callable, Awaitable +from typing import TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio @@ -15,34 +13,11 @@ T_ParamSpec = ParamSpec("T_ParamSpec") -if sys.version_info >= (3, 9): - _asyncio_to_thread = asyncio.to_thread -else: - # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread - # for Python 3.8 support - async def _asyncio_to_thread( - func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs - ) -> Any: - """Asynchronously run function *func* in a separate thread. - - Any *args and **kwargs supplied for this function are directly passed - to *func*. Also, the current :class:`contextvars.Context` is propagated, - allowing context variables from the main thread to be accessed in the - separate thread. - - Returns a coroutine that can be awaited to get the eventual result of *func*. - """ - loop = asyncio.events.get_running_loop() - ctx = contextvars.copy_context() - func_call = functools.partial(ctx.run, func, *args, **kwargs) - return await loop.run_in_executor(None, func_call) - - async def to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": - return await _asyncio_to_thread(func, *args, **kwargs) + return await asyncio.to_thread(func, *args, **kwargs) return await anyio.to_thread.run_sync( functools.partial(func, *args, **kwargs), @@ -53,10 +28,7 @@ async def to_thread( def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same - positional and keyword arguments. For python version 3.9 and above, it uses - asyncio.to_thread to run the function in a separate thread. For python version - 3.8, it uses locally defined copy of the asyncio.to_thread function which was - introduced in python 3.9. + positional and keyword arguments. Usage: diff --git a/src/opencode_ai/_utils/_transform.py b/src/opencode_ai/_utils/_transform.py index b0cc20a..5207549 100644 --- a/src/opencode_ai/_utils/_transform.py +++ b/src/opencode_ai/_utils/_transform.py @@ -16,18 +16,20 @@ lru_cache, is_mapping, is_iterable, + is_sequence, ) from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict from ._typing import ( is_list_type, is_union_type, extract_type_arg, is_iterable_type, is_required_type, + is_sequence_type, is_annotated_type, strip_annotated_type, ) -from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -167,6 +169,8 @@ def _transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation @@ -184,6 +188,8 @@ def _transform_recursive( (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. @@ -262,7 +268,7 @@ def _transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue @@ -329,6 +335,8 @@ async def _async_transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation @@ -346,6 +354,8 @@ async def _async_transform_recursive( (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. @@ -424,7 +434,7 @@ async def _async_transform_typeddict( annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): - # we don't need to include `NotGiven` values here as they'll + # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue diff --git a/src/opencode_ai/_utils/_typing.py b/src/opencode_ai/_utils/_typing.py index 1bac954..193109f 100644 --- a/src/opencode_ai/_utils/_typing.py +++ b/src/opencode_ai/_utils/_typing.py @@ -15,7 +15,7 @@ from ._utils import lru_cache from .._types import InheritsGeneric -from .._compat import is_union as _is_union +from ._compat import is_union as _is_union def is_annotated_type(typ: type) -> bool: @@ -26,6 +26,11 @@ def is_list_type(typ: type) -> bool: return (get_origin(typ) or typ) == list +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + def is_iterable_type(typ: type) -> bool: """If the given type is `typing.Iterable[T]`""" origin = get_origin(typ) or typ diff --git a/src/opencode_ai/_utils/_utils.py b/src/opencode_ai/_utils/_utils.py index ea3cf3f..eec7f4a 100644 --- a/src/opencode_ai/_utils/_utils.py +++ b/src/opencode_ai/_utils/_utils.py @@ -21,8 +21,7 @@ import sniffio -from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import parse_date as parse_date, parse_datetime as parse_datetime +from .._types import Omit, NotGiven, FileTypes, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -64,7 +63,7 @@ def _extract_items( try: key = path[index] except IndexError: - if isinstance(obj, NotGiven): + if not is_given(obj): # no value was provided - we can safely ignore return [] @@ -127,14 +126,14 @@ def _extract_items( return [] -def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) # Type safe methods for narrowing types with TypeVars. # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], # however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in it's place. +# care about the contained types we can safely use `object` in its place. # # There are two separate functions defined, `is_*` and `is_*_t` for different use cases. # `is_*` is for when you're dealing with an unknown input diff --git a/src/opencode_ai/_version.py b/src/opencode_ai/_version.py index 3e57475..565754d 100644 --- a/src/opencode_ai/_version.py +++ b/src/opencode_ai/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "opencode_ai" -__version__ = "0.1.0-alpha.36" # x-release-please-version +__version__ = "0.1.0-alpha.37" # x-release-please-version diff --git a/src/opencode_ai/resources/__init__.py b/src/opencode_ai/resources/__init__.py index ee6a647..c01ce0e 100644 --- a/src/opencode_ai/resources/__init__.py +++ b/src/opencode_ai/resources/__init__.py @@ -32,6 +32,22 @@ FindResourceWithStreamingResponse, AsyncFindResourceWithStreamingResponse, ) +from .path import ( + PathResource, + AsyncPathResource, + PathResourceWithRawResponse, + AsyncPathResourceWithRawResponse, + PathResourceWithStreamingResponse, + AsyncPathResourceWithStreamingResponse, +) +from .agent import ( + AgentResource, + AsyncAgentResource, + AgentResourceWithRawResponse, + AsyncAgentResourceWithRawResponse, + AgentResourceWithStreamingResponse, + AsyncAgentResourceWithStreamingResponse, +) from .event import ( EventResource, AsyncEventResource, @@ -48,6 +64,22 @@ ConfigResourceWithStreamingResponse, AsyncConfigResourceWithStreamingResponse, ) +from .command import ( + CommandResource, + AsyncCommandResource, + CommandResourceWithRawResponse, + AsyncCommandResourceWithRawResponse, + CommandResourceWithStreamingResponse, + AsyncCommandResourceWithStreamingResponse, +) +from .project import ( + ProjectResource, + AsyncProjectResource, + ProjectResourceWithRawResponse, + AsyncProjectResourceWithRawResponse, + ProjectResourceWithStreamingResponse, + AsyncProjectResourceWithStreamingResponse, +) from .session import ( SessionResource, AsyncSessionResource, @@ -64,12 +96,24 @@ "AsyncEventResourceWithRawResponse", "EventResourceWithStreamingResponse", "AsyncEventResourceWithStreamingResponse", + "PathResource", + "AsyncPathResource", + "PathResourceWithRawResponse", + "AsyncPathResourceWithRawResponse", + "PathResourceWithStreamingResponse", + "AsyncPathResourceWithStreamingResponse", "AppResource", "AsyncAppResource", "AppResourceWithRawResponse", "AsyncAppResourceWithRawResponse", "AppResourceWithStreamingResponse", "AsyncAppResourceWithStreamingResponse", + "AgentResource", + "AsyncAgentResource", + "AgentResourceWithRawResponse", + "AsyncAgentResourceWithRawResponse", + "AgentResourceWithStreamingResponse", + "AsyncAgentResourceWithStreamingResponse", "FindResource", "AsyncFindResource", "FindResourceWithRawResponse", @@ -88,6 +132,18 @@ "AsyncConfigResourceWithRawResponse", "ConfigResourceWithStreamingResponse", "AsyncConfigResourceWithStreamingResponse", + "CommandResource", + "AsyncCommandResource", + "CommandResourceWithRawResponse", + "AsyncCommandResourceWithRawResponse", + "CommandResourceWithStreamingResponse", + "AsyncCommandResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", "SessionResource", "AsyncSessionResource", "SessionResourceWithRawResponse", diff --git a/src/opencode_ai/resources/agent.py b/src/opencode_ai/resources/agent.py new file mode 100644 index 0000000..32bcfbf --- /dev/null +++ b/src/opencode_ai/resources/agent.py @@ -0,0 +1,169 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import agent_list_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.agent_list_response import AgentListResponse + +__all__ = ["AgentResource", "AsyncAgentResource"] + + +class AgentResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AgentResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return AgentResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AgentResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return AgentResourceWithStreamingResponse(self) + + def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentListResponse: + """ + List all agents + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/agent", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, agent_list_params.AgentListParams), + ), + cast_to=AgentListResponse, + ) + + +class AsyncAgentResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAgentResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncAgentResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAgentResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return AsyncAgentResourceWithStreamingResponse(self) + + async def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AgentListResponse: + """ + List all agents + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/agent", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, agent_list_params.AgentListParams), + ), + cast_to=AgentListResponse, + ) + + +class AgentResourceWithRawResponse: + def __init__(self, agent: AgentResource) -> None: + self._agent = agent + + self.list = to_raw_response_wrapper( + agent.list, + ) + + +class AsyncAgentResourceWithRawResponse: + def __init__(self, agent: AsyncAgentResource) -> None: + self._agent = agent + + self.list = async_to_raw_response_wrapper( + agent.list, + ) + + +class AgentResourceWithStreamingResponse: + def __init__(self, agent: AgentResource) -> None: + self._agent = agent + + self.list = to_streamed_response_wrapper( + agent.list, + ) + + +class AsyncAgentResourceWithStreamingResponse: + def __init__(self, agent: AsyncAgentResource) -> None: + self._agent = agent + + self.list = async_to_streamed_response_wrapper( + agent.list, + ) diff --git a/src/opencode_ai/resources/app.py b/src/opencode_ai/resources/app.py index e9505ce..2c139b2 100644 --- a/src/opencode_ai/resources/app.py +++ b/src/opencode_ai/resources/app.py @@ -7,8 +7,8 @@ import httpx -from ..types import app_log_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..types import app_log_params, app_providers_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -18,11 +18,8 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..types.app import App from .._base_client import make_request_options from ..types.app_log_response import AppLogResponse -from ..types.app_init_response import AppInitResponse -from ..types.app_modes_response import AppModesResponse from ..types.app_providers_response import AppProvidersResponse __all__ = ["AppResource", "AsyncAppResource"] @@ -48,57 +45,20 @@ def with_streaming_response(self) -> AppResourceWithStreamingResponse: """ return AppResourceWithStreamingResponse(self) - def get( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> App: - """Get app info""" - return self._get( - "/app", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=App, - ) - - def init( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AppInitResponse: - """Initialize the app""" - return self._post( - "/app/init", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AppInitResponse, - ) - def log( self, *, level: Literal["debug", "info", "error", "warn"], message: str, service: str, - extra: Dict[str, object] | NotGiven = NOT_GIVEN, + directory: str | Omit = omit, + extra: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AppLogResponse: """ Write a log entry to the server logs @@ -132,45 +92,46 @@ def log( app_log_params.AppLogParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, app_log_params.AppLogParams), ), cast_to=AppLogResponse, ) - def modes( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AppModesResponse: - """List all modes""" - return self._get( - "/mode", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AppModesResponse, - ) - def providers( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AppProvidersResponse: - """List all providers""" + """ + List all providers + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get( "/config/providers", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, app_providers_params.AppProvidersParams), ), cast_to=AppProvidersResponse, ) @@ -196,57 +157,20 @@ def with_streaming_response(self) -> AsyncAppResourceWithStreamingResponse: """ return AsyncAppResourceWithStreamingResponse(self) - async def get( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> App: - """Get app info""" - return await self._get( - "/app", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=App, - ) - - async def init( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AppInitResponse: - """Initialize the app""" - return await self._post( - "/app/init", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AppInitResponse, - ) - async def log( self, *, level: Literal["debug", "info", "error", "warn"], message: str, service: str, - extra: Dict[str, object] | NotGiven = NOT_GIVEN, + directory: str | Omit = omit, + extra: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AppLogResponse: """ Write a log entry to the server logs @@ -280,45 +204,46 @@ async def log( app_log_params.AppLogParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, app_log_params.AppLogParams), ), cast_to=AppLogResponse, ) - async def modes( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AppModesResponse: - """List all modes""" - return await self._get( - "/mode", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AppModesResponse, - ) - async def providers( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AppProvidersResponse: - """List all providers""" + """ + List all providers + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._get( "/config/providers", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, app_providers_params.AppProvidersParams), ), cast_to=AppProvidersResponse, ) @@ -328,18 +253,9 @@ class AppResourceWithRawResponse: def __init__(self, app: AppResource) -> None: self._app = app - self.get = to_raw_response_wrapper( - app.get, - ) - self.init = to_raw_response_wrapper( - app.init, - ) self.log = to_raw_response_wrapper( app.log, ) - self.modes = to_raw_response_wrapper( - app.modes, - ) self.providers = to_raw_response_wrapper( app.providers, ) @@ -349,18 +265,9 @@ class AsyncAppResourceWithRawResponse: def __init__(self, app: AsyncAppResource) -> None: self._app = app - self.get = async_to_raw_response_wrapper( - app.get, - ) - self.init = async_to_raw_response_wrapper( - app.init, - ) self.log = async_to_raw_response_wrapper( app.log, ) - self.modes = async_to_raw_response_wrapper( - app.modes, - ) self.providers = async_to_raw_response_wrapper( app.providers, ) @@ -370,18 +277,9 @@ class AppResourceWithStreamingResponse: def __init__(self, app: AppResource) -> None: self._app = app - self.get = to_streamed_response_wrapper( - app.get, - ) - self.init = to_streamed_response_wrapper( - app.init, - ) self.log = to_streamed_response_wrapper( app.log, ) - self.modes = to_streamed_response_wrapper( - app.modes, - ) self.providers = to_streamed_response_wrapper( app.providers, ) @@ -391,18 +289,9 @@ class AsyncAppResourceWithStreamingResponse: def __init__(self, app: AsyncAppResource) -> None: self._app = app - self.get = async_to_streamed_response_wrapper( - app.get, - ) - self.init = async_to_streamed_response_wrapper( - app.init, - ) self.log = async_to_streamed_response_wrapper( app.log, ) - self.modes = async_to_streamed_response_wrapper( - app.modes, - ) self.providers = async_to_streamed_response_wrapper( app.providers, ) diff --git a/src/opencode_ai/resources/command.py b/src/opencode_ai/resources/command.py new file mode 100644 index 0000000..e31c6b4 --- /dev/null +++ b/src/opencode_ai/resources/command.py @@ -0,0 +1,169 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import command_list_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.command_list_response import CommandListResponse + +__all__ = ["CommandResource", "AsyncCommandResource"] + + +class CommandResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CommandResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return CommandResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CommandResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return CommandResourceWithStreamingResponse(self) + + def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CommandListResponse: + """ + List all commands + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/command", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, command_list_params.CommandListParams), + ), + cast_to=CommandListResponse, + ) + + +class AsyncCommandResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCommandResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncCommandResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCommandResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return AsyncCommandResourceWithStreamingResponse(self) + + async def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CommandListResponse: + """ + List all commands + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/command", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, command_list_params.CommandListParams), + ), + cast_to=CommandListResponse, + ) + + +class CommandResourceWithRawResponse: + def __init__(self, command: CommandResource) -> None: + self._command = command + + self.list = to_raw_response_wrapper( + command.list, + ) + + +class AsyncCommandResourceWithRawResponse: + def __init__(self, command: AsyncCommandResource) -> None: + self._command = command + + self.list = async_to_raw_response_wrapper( + command.list, + ) + + +class CommandResourceWithStreamingResponse: + def __init__(self, command: CommandResource) -> None: + self._command = command + + self.list = to_streamed_response_wrapper( + command.list, + ) + + +class AsyncCommandResourceWithStreamingResponse: + def __init__(self, command: AsyncCommandResource) -> None: + self._command = command + + self.list = async_to_streamed_response_wrapper( + command.list, + ) diff --git a/src/opencode_ai/resources/config.py b/src/opencode_ai/resources/config.py index 88ff3a3..70c1c59 100644 --- a/src/opencode_ai/resources/config.py +++ b/src/opencode_ai/resources/config.py @@ -4,7 +4,9 @@ import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..types import config_get_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -42,18 +44,34 @@ def with_streaming_response(self) -> ConfigResourceWithStreamingResponse: def get( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Config: - """Get config info""" + """ + Get config info + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get( "/config", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, config_get_params.ConfigGetParams), ), cast_to=Config, ) @@ -82,18 +100,34 @@ def with_streaming_response(self) -> AsyncConfigResourceWithStreamingResponse: async def get( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Config: - """Get config info""" + """ + Get config info + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._get( "/config", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, config_get_params.ConfigGetParams), ), cast_to=Config, ) diff --git a/src/opencode_ai/resources/event.py b/src/opencode_ai/resources/event.py index 3e9baa3..6c33d60 100644 --- a/src/opencode_ai/resources/event.py +++ b/src/opencode_ai/resources/event.py @@ -6,7 +6,9 @@ import httpx -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..types import event_list_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -45,18 +47,35 @@ def with_streaming_response(self) -> EventResourceWithStreamingResponse: def list( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[EventListResponse]: - """Get events""" + """ + Get events + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} return self._get( "/event", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, event_list_params.EventListParams), ), cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system stream=True, @@ -87,18 +106,35 @@ def with_streaming_response(self) -> AsyncEventResourceWithStreamingResponse: async def list( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[EventListResponse]: - """Get events""" + """ + Get events + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {"Accept": "text/event-stream", **(extra_headers or {})} return await self._get( "/event", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, event_list_params.EventListParams), ), cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system stream=True, diff --git a/src/opencode_ai/resources/file.py b/src/opencode_ai/resources/file.py index 464bb93..711df13 100644 --- a/src/opencode_ai/resources/file.py +++ b/src/opencode_ai/resources/file.py @@ -4,8 +4,8 @@ import httpx -from ..types import file_read_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..types import file_list_params, file_read_params, file_status_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -16,6 +16,7 @@ async_to_streamed_response_wrapper, ) from .._base_client import make_request_options +from ..types.file_list_response import FileListResponse from ..types.file_read_response import FileReadResponse from ..types.file_status_response import FileStatusResponse @@ -42,16 +43,59 @@ def with_streaming_response(self) -> FileResourceWithStreamingResponse: """ return FileResourceWithStreamingResponse(self) + def list( + self, + *, + path: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FileListResponse: + """ + List files and directories + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/file", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "path": path, + "directory": directory, + }, + file_list_params.FileListParams, + ), + ), + cast_to=FileListResponse, + ) + def read( self, *, path: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileReadResponse: """ Read a file @@ -66,13 +110,19 @@ def read( timeout: Override the client-level default timeout for this request, in seconds """ return self._get( - "/file", + "/file/content", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"path": path}, file_read_params.FileReadParams), + query=maybe_transform( + { + "path": path, + "directory": directory, + }, + file_read_params.FileReadParams, + ), ), cast_to=FileReadResponse, ) @@ -80,18 +130,34 @@ def read( def status( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileStatusResponse: - """Get file status""" + """ + Get file status + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get( "/file/status", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, file_status_params.FileStatusParams), ), cast_to=FileStatusResponse, ) @@ -117,16 +183,59 @@ def with_streaming_response(self) -> AsyncFileResourceWithStreamingResponse: """ return AsyncFileResourceWithStreamingResponse(self) + async def list( + self, + *, + path: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FileListResponse: + """ + List files and directories + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/file", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "path": path, + "directory": directory, + }, + file_list_params.FileListParams, + ), + ), + cast_to=FileListResponse, + ) + async def read( self, *, path: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileReadResponse: """ Read a file @@ -141,13 +250,19 @@ async def read( timeout: Override the client-level default timeout for this request, in seconds """ return await self._get( - "/file", + "/file/content", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform({"path": path}, file_read_params.FileReadParams), + query=await async_maybe_transform( + { + "path": path, + "directory": directory, + }, + file_read_params.FileReadParams, + ), ), cast_to=FileReadResponse, ) @@ -155,18 +270,34 @@ async def read( async def status( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileStatusResponse: - """Get file status""" + """ + Get file status + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._get( "/file/status", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, file_status_params.FileStatusParams), ), cast_to=FileStatusResponse, ) @@ -176,6 +307,9 @@ class FileResourceWithRawResponse: def __init__(self, file: FileResource) -> None: self._file = file + self.list = to_raw_response_wrapper( + file.list, + ) self.read = to_raw_response_wrapper( file.read, ) @@ -188,6 +322,9 @@ class AsyncFileResourceWithRawResponse: def __init__(self, file: AsyncFileResource) -> None: self._file = file + self.list = async_to_raw_response_wrapper( + file.list, + ) self.read = async_to_raw_response_wrapper( file.read, ) @@ -200,6 +337,9 @@ class FileResourceWithStreamingResponse: def __init__(self, file: FileResource) -> None: self._file = file + self.list = to_streamed_response_wrapper( + file.list, + ) self.read = to_streamed_response_wrapper( file.read, ) @@ -212,6 +352,9 @@ class AsyncFileResourceWithStreamingResponse: def __init__(self, file: AsyncFileResource) -> None: self._file = file + self.list = async_to_streamed_response_wrapper( + file.list, + ) self.read = async_to_streamed_response_wrapper( file.read, ) diff --git a/src/opencode_ai/resources/find.py b/src/opencode_ai/resources/find.py index 1558e7c..7374a04 100644 --- a/src/opencode_ai/resources/find.py +++ b/src/opencode_ai/resources/find.py @@ -5,7 +5,7 @@ import httpx from ..types import find_text_params, find_files_params, find_symbols_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -47,12 +47,13 @@ def files( self, *, query: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FindFilesResponse: """ Find files @@ -73,7 +74,13 @@ def files( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"query": query}, find_files_params.FindFilesParams), + query=maybe_transform( + { + "query": query, + "directory": directory, + }, + find_files_params.FindFilesParams, + ), ), cast_to=FindFilesResponse, ) @@ -82,12 +89,13 @@ def symbols( self, *, query: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FindSymbolsResponse: """ Find workspace symbols @@ -108,7 +116,13 @@ def symbols( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"query": query}, find_symbols_params.FindSymbolsParams), + query=maybe_transform( + { + "query": query, + "directory": directory, + }, + find_symbols_params.FindSymbolsParams, + ), ), cast_to=FindSymbolsResponse, ) @@ -117,12 +131,13 @@ def text( self, *, pattern: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FindTextResponse: """ Find text in files @@ -143,7 +158,13 @@ def text( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"pattern": pattern}, find_text_params.FindTextParams), + query=maybe_transform( + { + "pattern": pattern, + "directory": directory, + }, + find_text_params.FindTextParams, + ), ), cast_to=FindTextResponse, ) @@ -173,12 +194,13 @@ async def files( self, *, query: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FindFilesResponse: """ Find files @@ -199,7 +221,13 @@ async def files( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform({"query": query}, find_files_params.FindFilesParams), + query=await async_maybe_transform( + { + "query": query, + "directory": directory, + }, + find_files_params.FindFilesParams, + ), ), cast_to=FindFilesResponse, ) @@ -208,12 +236,13 @@ async def symbols( self, *, query: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FindSymbolsResponse: """ Find workspace symbols @@ -234,7 +263,13 @@ async def symbols( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform({"query": query}, find_symbols_params.FindSymbolsParams), + query=await async_maybe_transform( + { + "query": query, + "directory": directory, + }, + find_symbols_params.FindSymbolsParams, + ), ), cast_to=FindSymbolsResponse, ) @@ -243,12 +278,13 @@ async def text( self, *, pattern: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FindTextResponse: """ Find text in files @@ -269,7 +305,13 @@ async def text( extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform({"pattern": pattern}, find_text_params.FindTextParams), + query=await async_maybe_transform( + { + "pattern": pattern, + "directory": directory, + }, + find_text_params.FindTextParams, + ), ), cast_to=FindTextResponse, ) diff --git a/src/opencode_ai/resources/path.py b/src/opencode_ai/resources/path.py new file mode 100644 index 0000000..6afee36 --- /dev/null +++ b/src/opencode_ai/resources/path.py @@ -0,0 +1,169 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import path_get_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..types.path import Path +from .._base_client import make_request_options + +__all__ = ["PathResource", "AsyncPathResource"] + + +class PathResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PathResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return PathResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PathResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return PathResourceWithStreamingResponse(self) + + def get( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Path: + """ + Get the current path + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/path", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, path_get_params.PathGetParams), + ), + cast_to=Path, + ) + + +class AsyncPathResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPathResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncPathResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPathResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return AsyncPathResourceWithStreamingResponse(self) + + async def get( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Path: + """ + Get the current path + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/path", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, path_get_params.PathGetParams), + ), + cast_to=Path, + ) + + +class PathResourceWithRawResponse: + def __init__(self, path: PathResource) -> None: + self._path = path + + self.get = to_raw_response_wrapper( + path.get, + ) + + +class AsyncPathResourceWithRawResponse: + def __init__(self, path: AsyncPathResource) -> None: + self._path = path + + self.get = async_to_raw_response_wrapper( + path.get, + ) + + +class PathResourceWithStreamingResponse: + def __init__(self, path: PathResource) -> None: + self._path = path + + self.get = to_streamed_response_wrapper( + path.get, + ) + + +class AsyncPathResourceWithStreamingResponse: + def __init__(self, path: AsyncPathResource) -> None: + self._path = path + + self.get = async_to_streamed_response_wrapper( + path.get, + ) diff --git a/src/opencode_ai/resources/project.py b/src/opencode_ai/resources/project.py new file mode 100644 index 0000000..e0b94b5 --- /dev/null +++ b/src/opencode_ai/resources/project.py @@ -0,0 +1,254 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import project_list_params, project_current_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.project import Project +from ..types.project_list_response import ProjectListResponse + +__all__ = ["ProjectResource", "AsyncProjectResource"] + + +class ProjectResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ProjectResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return ProjectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return ProjectResourceWithStreamingResponse(self) + + def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectListResponse: + """ + List all projects + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/project", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, project_list_params.ProjectListParams), + ), + cast_to=ProjectListResponse, + ) + + def current( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Get the current project + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/project/current", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, project_current_params.ProjectCurrentParams), + ), + cast_to=Project, + ) + + +class AsyncProjectResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncProjectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return AsyncProjectResourceWithStreamingResponse(self) + + async def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectListResponse: + """ + List all projects + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/project", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, project_list_params.ProjectListParams), + ), + cast_to=ProjectListResponse, + ) + + async def current( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Project: + """ + Get the current project + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/project/current", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, project_current_params.ProjectCurrentParams + ), + ), + cast_to=Project, + ) + + +class ProjectResourceWithRawResponse: + def __init__(self, project: ProjectResource) -> None: + self._project = project + + self.list = to_raw_response_wrapper( + project.list, + ) + self.current = to_raw_response_wrapper( + project.current, + ) + + +class AsyncProjectResourceWithRawResponse: + def __init__(self, project: AsyncProjectResource) -> None: + self._project = project + + self.list = async_to_raw_response_wrapper( + project.list, + ) + self.current = async_to_raw_response_wrapper( + project.current, + ) + + +class ProjectResourceWithStreamingResponse: + def __init__(self, project: ProjectResource) -> None: + self._project = project + + self.list = to_streamed_response_wrapper( + project.list, + ) + self.current = to_streamed_response_wrapper( + project.current, + ) + + +class AsyncProjectResourceWithStreamingResponse: + def __init__(self, project: AsyncProjectResource) -> None: + self._project = project + + self.list = async_to_streamed_response_wrapper( + project.list, + ) + self.current = async_to_streamed_response_wrapper( + project.current, + ) diff --git a/src/opencode_ai/resources/session.py b/src/opencode_ai/resources/session.py deleted file mode 100644 index 7e50413..0000000 --- a/src/opencode_ai/resources/session.py +++ /dev/null @@ -1,1088 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Iterable - -import httpx - -from ..types import session_chat_params, session_init_params, session_revert_params, session_summarize_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.session import Session -from ..types.assistant_message import AssistantMessage -from ..types.session_init_response import SessionInitResponse -from ..types.session_list_response import SessionListResponse -from ..types.session_abort_response import SessionAbortResponse -from ..types.session_delete_response import SessionDeleteResponse -from ..types.session_messages_response import SessionMessagesResponse -from ..types.session_summarize_response import SessionSummarizeResponse - -__all__ = ["SessionResource", "AsyncSessionResource"] - - -class SessionResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SessionResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers - """ - return SessionResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SessionResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response - """ - return SessionResourceWithStreamingResponse(self) - - def create( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """Create a new session""" - return self._post( - "/session", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionListResponse: - """List all sessions""" - return self._get( - "/session", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionListResponse, - ) - - def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionDeleteResponse: - """ - Delete a session and all its data - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._delete( - f"/session/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionDeleteResponse, - ) - - def abort( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionAbortResponse: - """ - Abort a session - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( - f"/session/{id}/abort", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionAbortResponse, - ) - - def chat( - self, - id: str, - *, - model_id: str, - parts: Iterable[session_chat_params.Part], - provider_id: str, - message_id: str | NotGiven = NOT_GIVEN, - mode: str | NotGiven = NOT_GIVEN, - system: str | NotGiven = NOT_GIVEN, - tools: Dict[str, bool] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AssistantMessage: - """ - Create and send a new message to a session - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( - f"/session/{id}/message", - body=maybe_transform( - { - "model_id": model_id, - "parts": parts, - "provider_id": provider_id, - "message_id": message_id, - "mode": mode, - "system": system, - "tools": tools, - }, - session_chat_params.SessionChatParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AssistantMessage, - ) - - def init( - self, - id: str, - *, - message_id: str, - model_id: str, - provider_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionInitResponse: - """ - Analyze the app and create an AGENTS.md file - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( - f"/session/{id}/init", - body=maybe_transform( - { - "message_id": message_id, - "model_id": model_id, - "provider_id": provider_id, - }, - session_init_params.SessionInitParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionInitResponse, - ) - - def messages( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionMessagesResponse: - """ - List messages for a session - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/session/{id}/message", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionMessagesResponse, - ) - - def revert( - self, - id: str, - *, - message_id: str, - part_id: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Revert a message - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( - f"/session/{id}/revert", - body=maybe_transform( - { - "message_id": message_id, - "part_id": part_id, - }, - session_revert_params.SessionRevertParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - def share( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Share a session - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( - f"/session/{id}/share", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - def summarize( - self, - id: str, - *, - model_id: str, - provider_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionSummarizeResponse: - """ - Summarize the session - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( - f"/session/{id}/summarize", - body=maybe_transform( - { - "model_id": model_id, - "provider_id": provider_id, - }, - session_summarize_params.SessionSummarizeParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionSummarizeResponse, - ) - - def unrevert( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Restore all reverted messages - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( - f"/session/{id}/unrevert", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - def unshare( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Unshare the session - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._delete( - f"/session/{id}/share", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - -class AsyncSessionResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSessionResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncSessionResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSessionResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response - """ - return AsyncSessionResourceWithStreamingResponse(self) - - async def create( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """Create a new session""" - return await self._post( - "/session", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - async def list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionListResponse: - """List all sessions""" - return await self._get( - "/session", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionListResponse, - ) - - async def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionDeleteResponse: - """ - Delete a session and all its data - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._delete( - f"/session/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionDeleteResponse, - ) - - async def abort( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionAbortResponse: - """ - Abort a session - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( - f"/session/{id}/abort", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionAbortResponse, - ) - - async def chat( - self, - id: str, - *, - model_id: str, - parts: Iterable[session_chat_params.Part], - provider_id: str, - message_id: str | NotGiven = NOT_GIVEN, - mode: str | NotGiven = NOT_GIVEN, - system: str | NotGiven = NOT_GIVEN, - tools: Dict[str, bool] | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AssistantMessage: - """ - Create and send a new message to a session - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( - f"/session/{id}/message", - body=await async_maybe_transform( - { - "model_id": model_id, - "parts": parts, - "provider_id": provider_id, - "message_id": message_id, - "mode": mode, - "system": system, - "tools": tools, - }, - session_chat_params.SessionChatParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AssistantMessage, - ) - - async def init( - self, - id: str, - *, - message_id: str, - model_id: str, - provider_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionInitResponse: - """ - Analyze the app and create an AGENTS.md file - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( - f"/session/{id}/init", - body=await async_maybe_transform( - { - "message_id": message_id, - "model_id": model_id, - "provider_id": provider_id, - }, - session_init_params.SessionInitParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionInitResponse, - ) - - async def messages( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionMessagesResponse: - """ - List messages for a session - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/session/{id}/message", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionMessagesResponse, - ) - - async def revert( - self, - id: str, - *, - message_id: str, - part_id: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Revert a message - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( - f"/session/{id}/revert", - body=await async_maybe_transform( - { - "message_id": message_id, - "part_id": part_id, - }, - session_revert_params.SessionRevertParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - async def share( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Share a session - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( - f"/session/{id}/share", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - async def summarize( - self, - id: str, - *, - model_id: str, - provider_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SessionSummarizeResponse: - """ - Summarize the session - - Args: - id: Session ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( - f"/session/{id}/summarize", - body=await async_maybe_transform( - { - "model_id": model_id, - "provider_id": provider_id, - }, - session_summarize_params.SessionSummarizeParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SessionSummarizeResponse, - ) - - async def unrevert( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Restore all reverted messages - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( - f"/session/{id}/unrevert", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - async def unshare( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> Session: - """ - Unshare the session - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._delete( - f"/session/{id}/share", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Session, - ) - - -class SessionResourceWithRawResponse: - def __init__(self, session: SessionResource) -> None: - self._session = session - - self.create = to_raw_response_wrapper( - session.create, - ) - self.list = to_raw_response_wrapper( - session.list, - ) - self.delete = to_raw_response_wrapper( - session.delete, - ) - self.abort = to_raw_response_wrapper( - session.abort, - ) - self.chat = to_raw_response_wrapper( - session.chat, - ) - self.init = to_raw_response_wrapper( - session.init, - ) - self.messages = to_raw_response_wrapper( - session.messages, - ) - self.revert = to_raw_response_wrapper( - session.revert, - ) - self.share = to_raw_response_wrapper( - session.share, - ) - self.summarize = to_raw_response_wrapper( - session.summarize, - ) - self.unrevert = to_raw_response_wrapper( - session.unrevert, - ) - self.unshare = to_raw_response_wrapper( - session.unshare, - ) - - -class AsyncSessionResourceWithRawResponse: - def __init__(self, session: AsyncSessionResource) -> None: - self._session = session - - self.create = async_to_raw_response_wrapper( - session.create, - ) - self.list = async_to_raw_response_wrapper( - session.list, - ) - self.delete = async_to_raw_response_wrapper( - session.delete, - ) - self.abort = async_to_raw_response_wrapper( - session.abort, - ) - self.chat = async_to_raw_response_wrapper( - session.chat, - ) - self.init = async_to_raw_response_wrapper( - session.init, - ) - self.messages = async_to_raw_response_wrapper( - session.messages, - ) - self.revert = async_to_raw_response_wrapper( - session.revert, - ) - self.share = async_to_raw_response_wrapper( - session.share, - ) - self.summarize = async_to_raw_response_wrapper( - session.summarize, - ) - self.unrevert = async_to_raw_response_wrapper( - session.unrevert, - ) - self.unshare = async_to_raw_response_wrapper( - session.unshare, - ) - - -class SessionResourceWithStreamingResponse: - def __init__(self, session: SessionResource) -> None: - self._session = session - - self.create = to_streamed_response_wrapper( - session.create, - ) - self.list = to_streamed_response_wrapper( - session.list, - ) - self.delete = to_streamed_response_wrapper( - session.delete, - ) - self.abort = to_streamed_response_wrapper( - session.abort, - ) - self.chat = to_streamed_response_wrapper( - session.chat, - ) - self.init = to_streamed_response_wrapper( - session.init, - ) - self.messages = to_streamed_response_wrapper( - session.messages, - ) - self.revert = to_streamed_response_wrapper( - session.revert, - ) - self.share = to_streamed_response_wrapper( - session.share, - ) - self.summarize = to_streamed_response_wrapper( - session.summarize, - ) - self.unrevert = to_streamed_response_wrapper( - session.unrevert, - ) - self.unshare = to_streamed_response_wrapper( - session.unshare, - ) - - -class AsyncSessionResourceWithStreamingResponse: - def __init__(self, session: AsyncSessionResource) -> None: - self._session = session - - self.create = async_to_streamed_response_wrapper( - session.create, - ) - self.list = async_to_streamed_response_wrapper( - session.list, - ) - self.delete = async_to_streamed_response_wrapper( - session.delete, - ) - self.abort = async_to_streamed_response_wrapper( - session.abort, - ) - self.chat = async_to_streamed_response_wrapper( - session.chat, - ) - self.init = async_to_streamed_response_wrapper( - session.init, - ) - self.messages = async_to_streamed_response_wrapper( - session.messages, - ) - self.revert = async_to_streamed_response_wrapper( - session.revert, - ) - self.share = async_to_streamed_response_wrapper( - session.share, - ) - self.summarize = async_to_streamed_response_wrapper( - session.summarize, - ) - self.unrevert = async_to_streamed_response_wrapper( - session.unrevert, - ) - self.unshare = async_to_streamed_response_wrapper( - session.unshare, - ) diff --git a/src/opencode_ai/resources/session/__init__.py b/src/opencode_ai/resources/session/__init__.py new file mode 100644 index 0000000..60a023e --- /dev/null +++ b/src/opencode_ai/resources/session/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .session import ( + SessionResource, + AsyncSessionResource, + SessionResourceWithRawResponse, + AsyncSessionResourceWithRawResponse, + SessionResourceWithStreamingResponse, + AsyncSessionResourceWithStreamingResponse, +) +from .permissions import ( + PermissionsResource, + AsyncPermissionsResource, + PermissionsResourceWithRawResponse, + AsyncPermissionsResourceWithRawResponse, + PermissionsResourceWithStreamingResponse, + AsyncPermissionsResourceWithStreamingResponse, +) + +__all__ = [ + "PermissionsResource", + "AsyncPermissionsResource", + "PermissionsResourceWithRawResponse", + "AsyncPermissionsResourceWithRawResponse", + "PermissionsResourceWithStreamingResponse", + "AsyncPermissionsResourceWithStreamingResponse", + "SessionResource", + "AsyncSessionResource", + "SessionResourceWithRawResponse", + "AsyncSessionResourceWithRawResponse", + "SessionResourceWithStreamingResponse", + "AsyncSessionResourceWithStreamingResponse", +] diff --git a/src/opencode_ai/resources/session/permissions.py b/src/opencode_ai/resources/session/permissions.py new file mode 100644 index 0000000..a4d9c23 --- /dev/null +++ b/src/opencode_ai/resources/session/permissions.py @@ -0,0 +1,189 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.session import permission_respond_params +from ...types.session.permission_respond_response import PermissionRespondResponse + +__all__ = ["PermissionsResource", "AsyncPermissionsResource"] + + +class PermissionsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PermissionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return PermissionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PermissionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return PermissionsResourceWithStreamingResponse(self) + + def respond( + self, + permission_id: str, + *, + id: str, + response: Literal["once", "always", "reject"], + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PermissionRespondResponse: + """ + Respond to a permission request + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not permission_id: + raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") + return self._post( + f"/session/{id}/permissions/{permission_id}", + body=maybe_transform({"response": response}, permission_respond_params.PermissionRespondParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, permission_respond_params.PermissionRespondParams), + ), + cast_to=PermissionRespondResponse, + ) + + +class AsyncPermissionsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPermissionsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncPermissionsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPermissionsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return AsyncPermissionsResourceWithStreamingResponse(self) + + async def respond( + self, + permission_id: str, + *, + id: str, + response: Literal["once", "always", "reject"], + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PermissionRespondResponse: + """ + Respond to a permission request + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not permission_id: + raise ValueError(f"Expected a non-empty value for `permission_id` but received {permission_id!r}") + return await self._post( + f"/session/{id}/permissions/{permission_id}", + body=await async_maybe_transform({"response": response}, permission_respond_params.PermissionRespondParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, permission_respond_params.PermissionRespondParams + ), + ), + cast_to=PermissionRespondResponse, + ) + + +class PermissionsResourceWithRawResponse: + def __init__(self, permissions: PermissionsResource) -> None: + self._permissions = permissions + + self.respond = to_raw_response_wrapper( + permissions.respond, + ) + + +class AsyncPermissionsResourceWithRawResponse: + def __init__(self, permissions: AsyncPermissionsResource) -> None: + self._permissions = permissions + + self.respond = async_to_raw_response_wrapper( + permissions.respond, + ) + + +class PermissionsResourceWithStreamingResponse: + def __init__(self, permissions: PermissionsResource) -> None: + self._permissions = permissions + + self.respond = to_streamed_response_wrapper( + permissions.respond, + ) + + +class AsyncPermissionsResourceWithStreamingResponse: + def __init__(self, permissions: AsyncPermissionsResource) -> None: + self._permissions = permissions + + self.respond = async_to_streamed_response_wrapper( + permissions.respond, + ) diff --git a/src/opencode_ai/resources/session/session.py b/src/opencode_ai/resources/session/session.py new file mode 100644 index 0000000..950acf1 --- /dev/null +++ b/src/opencode_ai/resources/session/session.py @@ -0,0 +1,1937 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Iterable + +import httpx + +from ...types import ( + session_get_params, + session_init_params, + session_list_params, + session_abort_params, + session_share_params, + session_shell_params, + session_create_params, + session_delete_params, + session_prompt_params, + session_revert_params, + session_update_params, + session_command_params, + session_message_params, + session_unshare_params, + session_children_params, + session_messages_params, + session_unrevert_params, + session_summarize_params, +) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .permissions import ( + PermissionsResource, + AsyncPermissionsResource, + PermissionsResourceWithRawResponse, + AsyncPermissionsResourceWithRawResponse, + PermissionsResourceWithStreamingResponse, + AsyncPermissionsResourceWithStreamingResponse, +) +from ..._base_client import make_request_options +from ...types.session.session import Session +from ...types.assistant_message import AssistantMessage +from ...types.session_init_response import SessionInitResponse +from ...types.session_list_response import SessionListResponse +from ...types.session_abort_response import SessionAbortResponse +from ...types.session_delete_response import SessionDeleteResponse +from ...types.session_prompt_response import SessionPromptResponse +from ...types.session_command_response import SessionCommandResponse +from ...types.session_message_response import SessionMessageResponse +from ...types.session_children_response import SessionChildrenResponse +from ...types.session_messages_response import SessionMessagesResponse +from ...types.session_summarize_response import SessionSummarizeResponse + +__all__ = ["SessionResource", "AsyncSessionResource"] + + +class SessionResource(SyncAPIResource): + @cached_property + def permissions(self) -> PermissionsResource: + return PermissionsResource(self._client) + + @cached_property + def with_raw_response(self) -> SessionResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return SessionResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SessionResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return SessionResourceWithStreamingResponse(self) + + def create( + self, + *, + directory: str | Omit = omit, + parent_id: str | Omit = omit, + title: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Create a new session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/session", + body=maybe_transform( + { + "parent_id": parent_id, + "title": title, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_create_params.SessionCreateParams), + ), + cast_to=Session, + ) + + def update( + self, + id: str, + *, + directory: str | Omit = omit, + title: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Update session properties + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._patch( + f"/session/{id}", + body=maybe_transform({"title": title}, session_update_params.SessionUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_update_params.SessionUpdateParams), + ), + cast_to=Session, + ) + + def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionListResponse: + """ + List all sessions + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/session", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_list_params.SessionListParams), + ), + cast_to=SessionListResponse, + ) + + def delete( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionDeleteResponse: + """ + Delete a session and all its data + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/session/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_delete_params.SessionDeleteParams), + ), + cast_to=SessionDeleteResponse, + ) + + def abort( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionAbortResponse: + """ + Abort a session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/abort", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_abort_params.SessionAbortParams), + ), + cast_to=SessionAbortResponse, + ) + + def children( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionChildrenResponse: + """ + Get a session's children + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/session/{id}/children", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_children_params.SessionChildrenParams), + ), + cast_to=SessionChildrenResponse, + ) + + def command( + self, + id: str, + *, + arguments: str, + command: str, + directory: str | Omit = omit, + agent: str | Omit = omit, + message_id: str | Omit = omit, + model: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionCommandResponse: + """ + Send a new command to a session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/command", + body=maybe_transform( + { + "arguments": arguments, + "command": command, + "agent": agent, + "message_id": message_id, + "model": model, + }, + session_command_params.SessionCommandParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_command_params.SessionCommandParams), + ), + cast_to=SessionCommandResponse, + ) + + def get( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Get session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/session/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_get_params.SessionGetParams), + ), + cast_to=Session, + ) + + def init( + self, + id: str, + *, + message_id: str, + model_id: str, + provider_id: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionInitResponse: + """ + Analyze the app and create an AGENTS.md file + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/init", + body=maybe_transform( + { + "message_id": message_id, + "model_id": model_id, + "provider_id": provider_id, + }, + session_init_params.SessionInitParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_init_params.SessionInitParams), + ), + cast_to=SessionInitResponse, + ) + + def message( + self, + message_id: str, + *, + id: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionMessageResponse: + """ + Get a message from a session + + Args: + id: Session ID + + message_id: Message ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + return self._get( + f"/session/{id}/message/{message_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_message_params.SessionMessageParams), + ), + cast_to=SessionMessageResponse, + ) + + def messages( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionMessagesResponse: + """ + List messages for a session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/session/{id}/message", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_messages_params.SessionMessagesParams), + ), + cast_to=SessionMessagesResponse, + ) + + def prompt( + self, + id: str, + *, + parts: Iterable[session_prompt_params.Part], + directory: str | Omit = omit, + agent: str | Omit = omit, + message_id: str | Omit = omit, + model: session_prompt_params.Model | Omit = omit, + system: str | Omit = omit, + tools: Dict[str, bool] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionPromptResponse: + """ + Create and send a new message to a session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/message", + body=maybe_transform( + { + "parts": parts, + "agent": agent, + "message_id": message_id, + "model": model, + "system": system, + "tools": tools, + }, + session_prompt_params.SessionPromptParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_prompt_params.SessionPromptParams), + ), + cast_to=SessionPromptResponse, + ) + + def revert( + self, + id: str, + *, + message_id: str, + directory: str | Omit = omit, + part_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Revert a message + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/revert", + body=maybe_transform( + { + "message_id": message_id, + "part_id": part_id, + }, + session_revert_params.SessionRevertParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_revert_params.SessionRevertParams), + ), + cast_to=Session, + ) + + def share( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Share a session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/share", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_share_params.SessionShareParams), + ), + cast_to=Session, + ) + + def shell( + self, + id: str, + *, + agent: str, + command: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AssistantMessage: + """ + Run a shell command + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/shell", + body=maybe_transform( + { + "agent": agent, + "command": command, + }, + session_shell_params.SessionShellParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_shell_params.SessionShellParams), + ), + cast_to=AssistantMessage, + ) + + def summarize( + self, + id: str, + *, + model_id: str, + provider_id: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionSummarizeResponse: + """ + Summarize the session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/summarize", + body=maybe_transform( + { + "model_id": model_id, + "provider_id": provider_id, + }, + session_summarize_params.SessionSummarizeParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_summarize_params.SessionSummarizeParams), + ), + cast_to=SessionSummarizeResponse, + ) + + def unrevert( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Restore all reverted messages + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/session/{id}/unrevert", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_unrevert_params.SessionUnrevertParams), + ), + cast_to=Session, + ) + + def unshare( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Unshare the session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/session/{id}/share", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, session_unshare_params.SessionUnshareParams), + ), + cast_to=Session, + ) + + +class AsyncSessionResource(AsyncAPIResource): + @cached_property + def permissions(self) -> AsyncPermissionsResource: + return AsyncPermissionsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncSessionResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/sst/opencode-sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncSessionResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSessionResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/sst/opencode-sdk-python#with_streaming_response + """ + return AsyncSessionResourceWithStreamingResponse(self) + + async def create( + self, + *, + directory: str | Omit = omit, + parent_id: str | Omit = omit, + title: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Create a new session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/session", + body=await async_maybe_transform( + { + "parent_id": parent_id, + "title": title, + }, + session_create_params.SessionCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_create_params.SessionCreateParams), + ), + cast_to=Session, + ) + + async def update( + self, + id: str, + *, + directory: str | Omit = omit, + title: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Update session properties + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._patch( + f"/session/{id}", + body=await async_maybe_transform({"title": title}, session_update_params.SessionUpdateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_update_params.SessionUpdateParams), + ), + cast_to=Session, + ) + + async def list( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionListResponse: + """ + List all sessions + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/session", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_list_params.SessionListParams), + ), + cast_to=SessionListResponse, + ) + + async def delete( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionDeleteResponse: + """ + Delete a session and all its data + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/session/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_delete_params.SessionDeleteParams), + ), + cast_to=SessionDeleteResponse, + ) + + async def abort( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionAbortResponse: + """ + Abort a session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/abort", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_abort_params.SessionAbortParams), + ), + cast_to=SessionAbortResponse, + ) + + async def children( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionChildrenResponse: + """ + Get a session's children + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/session/{id}/children", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, session_children_params.SessionChildrenParams + ), + ), + cast_to=SessionChildrenResponse, + ) + + async def command( + self, + id: str, + *, + arguments: str, + command: str, + directory: str | Omit = omit, + agent: str | Omit = omit, + message_id: str | Omit = omit, + model: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionCommandResponse: + """ + Send a new command to a session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/command", + body=await async_maybe_transform( + { + "arguments": arguments, + "command": command, + "agent": agent, + "message_id": message_id, + "model": model, + }, + session_command_params.SessionCommandParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, session_command_params.SessionCommandParams + ), + ), + cast_to=SessionCommandResponse, + ) + + async def get( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Get session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/session/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_get_params.SessionGetParams), + ), + cast_to=Session, + ) + + async def init( + self, + id: str, + *, + message_id: str, + model_id: str, + provider_id: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionInitResponse: + """ + Analyze the app and create an AGENTS.md file + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/init", + body=await async_maybe_transform( + { + "message_id": message_id, + "model_id": model_id, + "provider_id": provider_id, + }, + session_init_params.SessionInitParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_init_params.SessionInitParams), + ), + cast_to=SessionInitResponse, + ) + + async def message( + self, + message_id: str, + *, + id: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionMessageResponse: + """ + Get a message from a session + + Args: + id: Session ID + + message_id: Message ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + if not message_id: + raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") + return await self._get( + f"/session/{id}/message/{message_id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, session_message_params.SessionMessageParams + ), + ), + cast_to=SessionMessageResponse, + ) + + async def messages( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionMessagesResponse: + """ + List messages for a session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/session/{id}/message", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, session_messages_params.SessionMessagesParams + ), + ), + cast_to=SessionMessagesResponse, + ) + + async def prompt( + self, + id: str, + *, + parts: Iterable[session_prompt_params.Part], + directory: str | Omit = omit, + agent: str | Omit = omit, + message_id: str | Omit = omit, + model: session_prompt_params.Model | Omit = omit, + system: str | Omit = omit, + tools: Dict[str, bool] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionPromptResponse: + """ + Create and send a new message to a session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/message", + body=await async_maybe_transform( + { + "parts": parts, + "agent": agent, + "message_id": message_id, + "model": model, + "system": system, + "tools": tools, + }, + session_prompt_params.SessionPromptParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_prompt_params.SessionPromptParams), + ), + cast_to=SessionPromptResponse, + ) + + async def revert( + self, + id: str, + *, + message_id: str, + directory: str | Omit = omit, + part_id: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Revert a message + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/revert", + body=await async_maybe_transform( + { + "message_id": message_id, + "part_id": part_id, + }, + session_revert_params.SessionRevertParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_revert_params.SessionRevertParams), + ), + cast_to=Session, + ) + + async def share( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Share a session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/share", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_share_params.SessionShareParams), + ), + cast_to=Session, + ) + + async def shell( + self, + id: str, + *, + agent: str, + command: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> AssistantMessage: + """ + Run a shell command + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/shell", + body=await async_maybe_transform( + { + "agent": agent, + "command": command, + }, + session_shell_params.SessionShellParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, session_shell_params.SessionShellParams), + ), + cast_to=AssistantMessage, + ) + + async def summarize( + self, + id: str, + *, + model_id: str, + provider_id: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SessionSummarizeResponse: + """ + Summarize the session + + Args: + id: Session ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/summarize", + body=await async_maybe_transform( + { + "model_id": model_id, + "provider_id": provider_id, + }, + session_summarize_params.SessionSummarizeParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, session_summarize_params.SessionSummarizeParams + ), + ), + cast_to=SessionSummarizeResponse, + ) + + async def unrevert( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Restore all reverted messages + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/session/{id}/unrevert", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, session_unrevert_params.SessionUnrevertParams + ), + ), + cast_to=Session, + ) + + async def unshare( + self, + id: str, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> Session: + """ + Unshare the session + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/session/{id}/share", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, session_unshare_params.SessionUnshareParams + ), + ), + cast_to=Session, + ) + + +class SessionResourceWithRawResponse: + def __init__(self, session: SessionResource) -> None: + self._session = session + + self.create = to_raw_response_wrapper( + session.create, + ) + self.update = to_raw_response_wrapper( + session.update, + ) + self.list = to_raw_response_wrapper( + session.list, + ) + self.delete = to_raw_response_wrapper( + session.delete, + ) + self.abort = to_raw_response_wrapper( + session.abort, + ) + self.children = to_raw_response_wrapper( + session.children, + ) + self.command = to_raw_response_wrapper( + session.command, + ) + self.get = to_raw_response_wrapper( + session.get, + ) + self.init = to_raw_response_wrapper( + session.init, + ) + self.message = to_raw_response_wrapper( + session.message, + ) + self.messages = to_raw_response_wrapper( + session.messages, + ) + self.prompt = to_raw_response_wrapper( + session.prompt, + ) + self.revert = to_raw_response_wrapper( + session.revert, + ) + self.share = to_raw_response_wrapper( + session.share, + ) + self.shell = to_raw_response_wrapper( + session.shell, + ) + self.summarize = to_raw_response_wrapper( + session.summarize, + ) + self.unrevert = to_raw_response_wrapper( + session.unrevert, + ) + self.unshare = to_raw_response_wrapper( + session.unshare, + ) + + @cached_property + def permissions(self) -> PermissionsResourceWithRawResponse: + return PermissionsResourceWithRawResponse(self._session.permissions) + + +class AsyncSessionResourceWithRawResponse: + def __init__(self, session: AsyncSessionResource) -> None: + self._session = session + + self.create = async_to_raw_response_wrapper( + session.create, + ) + self.update = async_to_raw_response_wrapper( + session.update, + ) + self.list = async_to_raw_response_wrapper( + session.list, + ) + self.delete = async_to_raw_response_wrapper( + session.delete, + ) + self.abort = async_to_raw_response_wrapper( + session.abort, + ) + self.children = async_to_raw_response_wrapper( + session.children, + ) + self.command = async_to_raw_response_wrapper( + session.command, + ) + self.get = async_to_raw_response_wrapper( + session.get, + ) + self.init = async_to_raw_response_wrapper( + session.init, + ) + self.message = async_to_raw_response_wrapper( + session.message, + ) + self.messages = async_to_raw_response_wrapper( + session.messages, + ) + self.prompt = async_to_raw_response_wrapper( + session.prompt, + ) + self.revert = async_to_raw_response_wrapper( + session.revert, + ) + self.share = async_to_raw_response_wrapper( + session.share, + ) + self.shell = async_to_raw_response_wrapper( + session.shell, + ) + self.summarize = async_to_raw_response_wrapper( + session.summarize, + ) + self.unrevert = async_to_raw_response_wrapper( + session.unrevert, + ) + self.unshare = async_to_raw_response_wrapper( + session.unshare, + ) + + @cached_property + def permissions(self) -> AsyncPermissionsResourceWithRawResponse: + return AsyncPermissionsResourceWithRawResponse(self._session.permissions) + + +class SessionResourceWithStreamingResponse: + def __init__(self, session: SessionResource) -> None: + self._session = session + + self.create = to_streamed_response_wrapper( + session.create, + ) + self.update = to_streamed_response_wrapper( + session.update, + ) + self.list = to_streamed_response_wrapper( + session.list, + ) + self.delete = to_streamed_response_wrapper( + session.delete, + ) + self.abort = to_streamed_response_wrapper( + session.abort, + ) + self.children = to_streamed_response_wrapper( + session.children, + ) + self.command = to_streamed_response_wrapper( + session.command, + ) + self.get = to_streamed_response_wrapper( + session.get, + ) + self.init = to_streamed_response_wrapper( + session.init, + ) + self.message = to_streamed_response_wrapper( + session.message, + ) + self.messages = to_streamed_response_wrapper( + session.messages, + ) + self.prompt = to_streamed_response_wrapper( + session.prompt, + ) + self.revert = to_streamed_response_wrapper( + session.revert, + ) + self.share = to_streamed_response_wrapper( + session.share, + ) + self.shell = to_streamed_response_wrapper( + session.shell, + ) + self.summarize = to_streamed_response_wrapper( + session.summarize, + ) + self.unrevert = to_streamed_response_wrapper( + session.unrevert, + ) + self.unshare = to_streamed_response_wrapper( + session.unshare, + ) + + @cached_property + def permissions(self) -> PermissionsResourceWithStreamingResponse: + return PermissionsResourceWithStreamingResponse(self._session.permissions) + + +class AsyncSessionResourceWithStreamingResponse: + def __init__(self, session: AsyncSessionResource) -> None: + self._session = session + + self.create = async_to_streamed_response_wrapper( + session.create, + ) + self.update = async_to_streamed_response_wrapper( + session.update, + ) + self.list = async_to_streamed_response_wrapper( + session.list, + ) + self.delete = async_to_streamed_response_wrapper( + session.delete, + ) + self.abort = async_to_streamed_response_wrapper( + session.abort, + ) + self.children = async_to_streamed_response_wrapper( + session.children, + ) + self.command = async_to_streamed_response_wrapper( + session.command, + ) + self.get = async_to_streamed_response_wrapper( + session.get, + ) + self.init = async_to_streamed_response_wrapper( + session.init, + ) + self.message = async_to_streamed_response_wrapper( + session.message, + ) + self.messages = async_to_streamed_response_wrapper( + session.messages, + ) + self.prompt = async_to_streamed_response_wrapper( + session.prompt, + ) + self.revert = async_to_streamed_response_wrapper( + session.revert, + ) + self.share = async_to_streamed_response_wrapper( + session.share, + ) + self.shell = async_to_streamed_response_wrapper( + session.shell, + ) + self.summarize = async_to_streamed_response_wrapper( + session.summarize, + ) + self.unrevert = async_to_streamed_response_wrapper( + session.unrevert, + ) + self.unshare = async_to_streamed_response_wrapper( + session.unshare, + ) + + @cached_property + def permissions(self) -> AsyncPermissionsResourceWithStreamingResponse: + return AsyncPermissionsResourceWithStreamingResponse(self._session.permissions) diff --git a/src/opencode_ai/resources/tui.py b/src/opencode_ai/resources/tui.py index 7194e7a..11ffd2d 100644 --- a/src/opencode_ai/resources/tui.py +++ b/src/opencode_ai/resources/tui.py @@ -2,10 +2,22 @@ from __future__ import annotations +from typing_extensions import Literal + import httpx -from ..types import tui_append_prompt_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..types import ( + tui_open_help_params, + tui_show_toast_params, + tui_open_models_params, + tui_open_themes_params, + tui_clear_prompt_params, + tui_append_prompt_params, + tui_open_sessions_params, + tui_submit_prompt_params, + tui_execute_command_params, +) +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -17,7 +29,14 @@ ) from .._base_client import make_request_options from ..types.tui_open_help_response import TuiOpenHelpResponse +from ..types.tui_show_toast_response import TuiShowToastResponse +from ..types.tui_open_models_response import TuiOpenModelsResponse +from ..types.tui_open_themes_response import TuiOpenThemesResponse +from ..types.tui_clear_prompt_response import TuiClearPromptResponse from ..types.tui_append_prompt_response import TuiAppendPromptResponse +from ..types.tui_open_sessions_response import TuiOpenSessionsResponse +from ..types.tui_submit_prompt_response import TuiSubmitPromptResponse +from ..types.tui_execute_command_response import TuiExecuteCommandResponse __all__ = ["TuiResource", "AsyncTuiResource"] @@ -46,12 +65,13 @@ def append_prompt( self, *, text: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TuiAppendPromptResponse: """ Append prompt to the TUI @@ -69,30 +89,309 @@ def append_prompt( "/tui/append-prompt", body=maybe_transform({"text": text}, tui_append_prompt_params.TuiAppendPromptParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_append_prompt_params.TuiAppendPromptParams), ), cast_to=TuiAppendPromptResponse, ) + def clear_prompt( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiClearPromptResponse: + """ + Clear the prompt + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/tui/clear-prompt", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_clear_prompt_params.TuiClearPromptParams), + ), + cast_to=TuiClearPromptResponse, + ) + + def execute_command( + self, + *, + command: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiExecuteCommandResponse: + """Execute a TUI command (e.g. + + agent_cycle) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/tui/execute-command", + body=maybe_transform({"command": command}, tui_execute_command_params.TuiExecuteCommandParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_execute_command_params.TuiExecuteCommandParams), + ), + cast_to=TuiExecuteCommandResponse, + ) + def open_help( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TuiOpenHelpResponse: - """Open the help dialog""" + """ + Open the help dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._post( "/tui/open-help", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_open_help_params.TuiOpenHelpParams), ), cast_to=TuiOpenHelpResponse, ) + def open_models( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiOpenModelsResponse: + """ + Open the model dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/tui/open-models", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_open_models_params.TuiOpenModelsParams), + ), + cast_to=TuiOpenModelsResponse, + ) + + def open_sessions( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiOpenSessionsResponse: + """ + Open the session dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/tui/open-sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_open_sessions_params.TuiOpenSessionsParams), + ), + cast_to=TuiOpenSessionsResponse, + ) + + def open_themes( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiOpenThemesResponse: + """ + Open the theme dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/tui/open-themes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_open_themes_params.TuiOpenThemesParams), + ), + cast_to=TuiOpenThemesResponse, + ) + + def show_toast( + self, + *, + message: str, + variant: Literal["info", "success", "warning", "error"], + directory: str | Omit = omit, + title: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiShowToastResponse: + """ + Show a toast notification in the TUI + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/tui/show-toast", + body=maybe_transform( + { + "message": message, + "variant": variant, + "title": title, + }, + tui_show_toast_params.TuiShowToastParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_show_toast_params.TuiShowToastParams), + ), + cast_to=TuiShowToastResponse, + ) + + def submit_prompt( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiSubmitPromptResponse: + """ + Submit the prompt + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/tui/submit-prompt", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"directory": directory}, tui_submit_prompt_params.TuiSubmitPromptParams), + ), + cast_to=TuiSubmitPromptResponse, + ) + class AsyncTuiResource(AsyncAPIResource): @cached_property @@ -118,12 +417,13 @@ async def append_prompt( self, *, text: str, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TuiAppendPromptResponse: """ Append prompt to the TUI @@ -141,30 +441,319 @@ async def append_prompt( "/tui/append-prompt", body=await async_maybe_transform({"text": text}, tui_append_prompt_params.TuiAppendPromptParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, tui_append_prompt_params.TuiAppendPromptParams + ), ), cast_to=TuiAppendPromptResponse, ) + async def clear_prompt( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiClearPromptResponse: + """ + Clear the prompt + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/tui/clear-prompt", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, tui_clear_prompt_params.TuiClearPromptParams + ), + ), + cast_to=TuiClearPromptResponse, + ) + + async def execute_command( + self, + *, + command: str, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiExecuteCommandResponse: + """Execute a TUI command (e.g. + + agent_cycle) + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/tui/execute-command", + body=await async_maybe_transform({"command": command}, tui_execute_command_params.TuiExecuteCommandParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, tui_execute_command_params.TuiExecuteCommandParams + ), + ), + cast_to=TuiExecuteCommandResponse, + ) + async def open_help( self, *, + directory: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TuiOpenHelpResponse: - """Open the help dialog""" + """ + Open the help dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._post( "/tui/open-help", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, tui_open_help_params.TuiOpenHelpParams), ), cast_to=TuiOpenHelpResponse, ) + async def open_models( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiOpenModelsResponse: + """ + Open the model dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/tui/open-models", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, tui_open_models_params.TuiOpenModelsParams), + ), + cast_to=TuiOpenModelsResponse, + ) + + async def open_sessions( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiOpenSessionsResponse: + """ + Open the session dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/tui/open-sessions", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, tui_open_sessions_params.TuiOpenSessionsParams + ), + ), + cast_to=TuiOpenSessionsResponse, + ) + + async def open_themes( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiOpenThemesResponse: + """ + Open the theme dialog + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/tui/open-themes", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, tui_open_themes_params.TuiOpenThemesParams), + ), + cast_to=TuiOpenThemesResponse, + ) + + async def show_toast( + self, + *, + message: str, + variant: Literal["info", "success", "warning", "error"], + directory: str | Omit = omit, + title: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiShowToastResponse: + """ + Show a toast notification in the TUI + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/tui/show-toast", + body=await async_maybe_transform( + { + "message": message, + "variant": variant, + "title": title, + }, + tui_show_toast_params.TuiShowToastParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"directory": directory}, tui_show_toast_params.TuiShowToastParams), + ), + cast_to=TuiShowToastResponse, + ) + + async def submit_prompt( + self, + *, + directory: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TuiSubmitPromptResponse: + """ + Submit the prompt + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/tui/submit-prompt", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"directory": directory}, tui_submit_prompt_params.TuiSubmitPromptParams + ), + ), + cast_to=TuiSubmitPromptResponse, + ) + class TuiResourceWithRawResponse: def __init__(self, tui: TuiResource) -> None: @@ -173,9 +762,30 @@ def __init__(self, tui: TuiResource) -> None: self.append_prompt = to_raw_response_wrapper( tui.append_prompt, ) + self.clear_prompt = to_raw_response_wrapper( + tui.clear_prompt, + ) + self.execute_command = to_raw_response_wrapper( + tui.execute_command, + ) self.open_help = to_raw_response_wrapper( tui.open_help, ) + self.open_models = to_raw_response_wrapper( + tui.open_models, + ) + self.open_sessions = to_raw_response_wrapper( + tui.open_sessions, + ) + self.open_themes = to_raw_response_wrapper( + tui.open_themes, + ) + self.show_toast = to_raw_response_wrapper( + tui.show_toast, + ) + self.submit_prompt = to_raw_response_wrapper( + tui.submit_prompt, + ) class AsyncTuiResourceWithRawResponse: @@ -185,9 +795,30 @@ def __init__(self, tui: AsyncTuiResource) -> None: self.append_prompt = async_to_raw_response_wrapper( tui.append_prompt, ) + self.clear_prompt = async_to_raw_response_wrapper( + tui.clear_prompt, + ) + self.execute_command = async_to_raw_response_wrapper( + tui.execute_command, + ) self.open_help = async_to_raw_response_wrapper( tui.open_help, ) + self.open_models = async_to_raw_response_wrapper( + tui.open_models, + ) + self.open_sessions = async_to_raw_response_wrapper( + tui.open_sessions, + ) + self.open_themes = async_to_raw_response_wrapper( + tui.open_themes, + ) + self.show_toast = async_to_raw_response_wrapper( + tui.show_toast, + ) + self.submit_prompt = async_to_raw_response_wrapper( + tui.submit_prompt, + ) class TuiResourceWithStreamingResponse: @@ -197,9 +828,30 @@ def __init__(self, tui: TuiResource) -> None: self.append_prompt = to_streamed_response_wrapper( tui.append_prompt, ) + self.clear_prompt = to_streamed_response_wrapper( + tui.clear_prompt, + ) + self.execute_command = to_streamed_response_wrapper( + tui.execute_command, + ) self.open_help = to_streamed_response_wrapper( tui.open_help, ) + self.open_models = to_streamed_response_wrapper( + tui.open_models, + ) + self.open_sessions = to_streamed_response_wrapper( + tui.open_sessions, + ) + self.open_themes = to_streamed_response_wrapper( + tui.open_themes, + ) + self.show_toast = to_streamed_response_wrapper( + tui.show_toast, + ) + self.submit_prompt = to_streamed_response_wrapper( + tui.submit_prompt, + ) class AsyncTuiResourceWithStreamingResponse: @@ -209,6 +861,27 @@ def __init__(self, tui: AsyncTuiResource) -> None: self.append_prompt = async_to_streamed_response_wrapper( tui.append_prompt, ) + self.clear_prompt = async_to_streamed_response_wrapper( + tui.clear_prompt, + ) + self.execute_command = async_to_streamed_response_wrapper( + tui.execute_command, + ) self.open_help = async_to_streamed_response_wrapper( tui.open_help, ) + self.open_models = async_to_streamed_response_wrapper( + tui.open_models, + ) + self.open_sessions = async_to_streamed_response_wrapper( + tui.open_sessions, + ) + self.open_themes = async_to_streamed_response_wrapper( + tui.open_themes, + ) + self.show_toast = async_to_streamed_response_wrapper( + tui.show_toast, + ) + self.submit_prompt = async_to_streamed_response_wrapper( + tui.submit_prompt, + ) diff --git a/src/opencode_ai/types/__init__.py b/src/opencode_ai/types/__init__.py index 1d0663b..83a7701 100644 --- a/src/opencode_ai/types/__init__.py +++ b/src/opencode_ai/types/__init__.py @@ -2,10 +2,10 @@ from __future__ import annotations -from .app import App as App from .file import File as File -from .mode import Mode as Mode from .part import Part as Part +from .path import Path as Path +from .agent import Agent as Agent from .model import Model as Model from .config import Config as Config from .shared import ( @@ -14,60 +14,110 @@ MessageAbortedError as MessageAbortedError, ) from .symbol import Symbol as Symbol +from .command import Command as Command from .message import Message as Message +from .project import Project as Project from .session import Session as Session from .provider import Provider as Provider +from .file_node import FileNode as FileNode from .file_part import FilePart as FilePart from .text_part import TextPart as TextPart from .tool_part import ToolPart as ToolPart +from .agent_part import AgentPart as AgentPart from .file_source import FileSource as FileSource -from .mode_config import ModeConfig as ModeConfig from .user_message import UserMessage as UserMessage from .snapshot_part import SnapshotPart as SnapshotPart from .symbol_source import SymbolSource as SymbolSource from .app_log_params import AppLogParams as AppLogParams +from .reasoning_part import ReasoningPart as ReasoningPart from .keybinds_config import KeybindsConfig as KeybindsConfig +from .path_get_params import PathGetParams as PathGetParams from .step_start_part import StepStartPart as StepStartPart from .app_log_response import AppLogResponse as AppLogResponse +from .file_list_params import FileListParams as FileListParams from .file_part_source import FilePartSource as FilePartSource from .file_read_params import FileReadParams as FileReadParams from .find_text_params import FindTextParams as FindTextParams from .mcp_local_config import McpLocalConfig as McpLocalConfig from .step_finish_part import StepFinishPart as StepFinishPart from .tool_state_error import ToolStateError as ToolStateError -from .app_init_response import AppInitResponse as AppInitResponse +from .agent_list_params import AgentListParams as AgentListParams from .assistant_message import AssistantMessage as AssistantMessage +from .config_get_params import ConfigGetParams as ConfigGetParams +from .event_list_params import EventListParams as EventListParams from .file_source_param import FileSourceParam as FileSourceParam from .find_files_params import FindFilesParams as FindFilesParams from .mcp_remote_config import McpRemoteConfig as McpRemoteConfig -from .app_modes_response import AppModesResponse as AppModesResponse +from .file_list_response import FileListResponse as FileListResponse from .file_read_response import FileReadResponse as FileReadResponse +from .file_status_params import FileStatusParams as FileStatusParams from .find_text_response import FindTextResponse as FindTextResponse +from .session_get_params import SessionGetParams as SessionGetParams from .tool_state_pending import ToolStatePending as ToolStatePending from .tool_state_running import ToolStateRunning as ToolStateRunning +from .agent_list_response import AgentListResponse as AgentListResponse +from .command_list_params import CommandListParams as CommandListParams from .event_list_response import EventListResponse as EventListResponse from .find_files_response import FindFilesResponse as FindFilesResponse from .find_symbols_params import FindSymbolsParams as FindSymbolsParams -from .session_chat_params import SessionChatParams as SessionChatParams +from .project_list_params import ProjectListParams as ProjectListParams from .session_init_params import SessionInitParams as SessionInitParams +from .session_list_params import SessionListParams as SessionListParams from .symbol_source_param import SymbolSourceParam as SymbolSourceParam +from .app_providers_params import AppProvidersParams as AppProvidersParams from .file_status_response import FileStatusResponse as FileStatusResponse +from .session_abort_params import SessionAbortParams as SessionAbortParams +from .session_share_params import SessionShareParams as SessionShareParams +from .session_shell_params import SessionShellParams as SessionShellParams from .tool_state_completed import ToolStateCompleted as ToolStateCompleted +from .tui_open_help_params import TuiOpenHelpParams as TuiOpenHelpParams +from .command_list_response import CommandListResponse as CommandListResponse from .file_part_input_param import FilePartInputParam as FilePartInputParam from .file_part_source_text import FilePartSourceText as FilePartSourceText from .find_symbols_response import FindSymbolsResponse as FindSymbolsResponse +from .project_list_response import ProjectListResponse as ProjectListResponse +from .session_create_params import SessionCreateParams as SessionCreateParams +from .session_delete_params import SessionDeleteParams as SessionDeleteParams from .session_init_response import SessionInitResponse as SessionInitResponse from .session_list_response import SessionListResponse as SessionListResponse +from .session_prompt_params import SessionPromptParams as SessionPromptParams from .session_revert_params import SessionRevertParams as SessionRevertParams +from .session_update_params import SessionUpdateParams as SessionUpdateParams from .text_part_input_param import TextPartInputParam as TextPartInputParam +from .tui_show_toast_params import TuiShowToastParams as TuiShowToastParams +from .agent_part_input_param import AgentPartInputParam as AgentPartInputParam from .app_providers_response import AppProvidersResponse as AppProvidersResponse from .file_part_source_param import FilePartSourceParam as FilePartSourceParam +from .project_current_params import ProjectCurrentParams as ProjectCurrentParams from .session_abort_response import SessionAbortResponse as SessionAbortResponse +from .session_command_params import SessionCommandParams as SessionCommandParams +from .session_message_params import SessionMessageParams as SessionMessageParams +from .session_unshare_params import SessionUnshareParams as SessionUnshareParams from .tui_open_help_response import TuiOpenHelpResponse as TuiOpenHelpResponse +from .tui_open_models_params import TuiOpenModelsParams as TuiOpenModelsParams +from .tui_open_themes_params import TuiOpenThemesParams as TuiOpenThemesParams +from .session_children_params import SessionChildrenParams as SessionChildrenParams from .session_delete_response import SessionDeleteResponse as SessionDeleteResponse +from .session_messages_params import SessionMessagesParams as SessionMessagesParams +from .session_prompt_response import SessionPromptResponse as SessionPromptResponse +from .session_unrevert_params import SessionUnrevertParams as SessionUnrevertParams +from .tui_clear_prompt_params import TuiClearPromptParams as TuiClearPromptParams +from .tui_show_toast_response import TuiShowToastResponse as TuiShowToastResponse +from .session_command_response import SessionCommandResponse as SessionCommandResponse +from .session_message_response import SessionMessageResponse as SessionMessageResponse from .session_summarize_params import SessionSummarizeParams as SessionSummarizeParams from .tui_append_prompt_params import TuiAppendPromptParams as TuiAppendPromptParams +from .tui_open_models_response import TuiOpenModelsResponse as TuiOpenModelsResponse +from .tui_open_sessions_params import TuiOpenSessionsParams as TuiOpenSessionsParams +from .tui_open_themes_response import TuiOpenThemesResponse as TuiOpenThemesResponse +from .tui_submit_prompt_params import TuiSubmitPromptParams as TuiSubmitPromptParams +from .session_children_response import SessionChildrenResponse as SessionChildrenResponse from .session_messages_response import SessionMessagesResponse as SessionMessagesResponse +from .tui_clear_prompt_response import TuiClearPromptResponse as TuiClearPromptResponse from .session_summarize_response import SessionSummarizeResponse as SessionSummarizeResponse from .tui_append_prompt_response import TuiAppendPromptResponse as TuiAppendPromptResponse +from .tui_execute_command_params import TuiExecuteCommandParams as TuiExecuteCommandParams +from .tui_open_sessions_response import TuiOpenSessionsResponse as TuiOpenSessionsResponse +from .tui_submit_prompt_response import TuiSubmitPromptResponse as TuiSubmitPromptResponse from .file_part_source_text_param import FilePartSourceTextParam as FilePartSourceTextParam +from .tui_execute_command_response import TuiExecuteCommandResponse as TuiExecuteCommandResponse diff --git a/src/opencode_ai/types/agent.py b/src/opencode_ai/types/agent.py new file mode 100644 index 0000000..34f5c9f --- /dev/null +++ b/src/opencode_ai/types/agent.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["Agent", "Permission", "Model"] + + +class Permission(BaseModel): + bash: Dict[str, Literal["ask", "allow", "deny"]] + + edit: Literal["ask", "allow", "deny"] + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class Model(BaseModel): + api_model_id: str = FieldInfo(alias="modelID") + + provider_id: str = FieldInfo(alias="providerID") + + +class Agent(BaseModel): + built_in: bool = FieldInfo(alias="builtIn") + + mode: Literal["subagent", "primary", "all"] + + name: str + + options: Dict[str, object] + + permission: Permission + + tools: Dict[str, bool] + + description: Optional[str] = None + + model: Optional[Model] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + top_p: Optional[float] = FieldInfo(alias="topP", default=None) diff --git a/src/opencode_ai/types/agent_list_params.py b/src/opencode_ai/types/agent_list_params.py new file mode 100644 index 0000000..a5ee594 --- /dev/null +++ b/src/opencode_ai/types/agent_list_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AgentListParams"] + + +class AgentListParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/app_modes_response.py b/src/opencode_ai/types/agent_list_response.py similarity index 60% rename from src/opencode_ai/types/app_modes_response.py rename to src/opencode_ai/types/agent_list_response.py index 8d76f89..f33d201 100644 --- a/src/opencode_ai/types/app_modes_response.py +++ b/src/opencode_ai/types/agent_list_response.py @@ -3,8 +3,8 @@ from typing import List from typing_extensions import TypeAlias -from .mode import Mode +from .agent import Agent -__all__ = ["AppModesResponse"] +__all__ = ["AgentListResponse"] -AppModesResponse: TypeAlias = List[Mode] +AgentListResponse: TypeAlias = List[Agent] diff --git a/src/opencode_ai/types/agent_part.py b/src/opencode_ai/types/agent_part.py new file mode 100644 index 0000000..3b0fd98 --- /dev/null +++ b/src/opencode_ai/types/agent_part.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["AgentPart", "Source"] + + +class Source(BaseModel): + end: int + + start: int + + value: str + + +class AgentPart(BaseModel): + id: str + + message_id: str = FieldInfo(alias="messageID") + + name: str + + session_id: str = FieldInfo(alias="sessionID") + + type: Literal["agent"] + + source: Optional[Source] = None diff --git a/src/opencode_ai/types/agent_part_input_param.py b/src/opencode_ai/types/agent_part_input_param.py new file mode 100644 index 0000000..a500028 --- /dev/null +++ b/src/opencode_ai/types/agent_part_input_param.py @@ -0,0 +1,25 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AgentPartInputParam", "Source"] + + +class Source(TypedDict, total=False): + end: Required[int] + + start: Required[int] + + value: Required[str] + + +class AgentPartInputParam(TypedDict, total=False): + name: Required[str] + + type: Required[Literal["agent"]] + + id: str + + source: Source diff --git a/src/opencode_ai/types/app_log_params.py b/src/opencode_ai/types/app_log_params.py index 8b24c11..cb5a8ee 100644 --- a/src/opencode_ai/types/app_log_params.py +++ b/src/opencode_ai/types/app_log_params.py @@ -18,5 +18,7 @@ class AppLogParams(TypedDict, total=False): service: Required[str] """Service name for the log entry""" + directory: str + extra: Dict[str, object] """Additional metadata for the log entry""" diff --git a/src/opencode_ai/types/app_providers_params.py b/src/opencode_ai/types/app_providers_params.py new file mode 100644 index 0000000..75e4783 --- /dev/null +++ b/src/opencode_ai/types/app_providers_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["AppProvidersParams"] + + +class AppProvidersParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/command.py b/src/opencode_ai/types/command.py new file mode 100644 index 0000000..116ab0b --- /dev/null +++ b/src/opencode_ai/types/command.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["Command"] + + +class Command(BaseModel): + name: str + + template: str + + agent: Optional[str] = None + + description: Optional[str] = None + + model: Optional[str] = None diff --git a/src/opencode_ai/types/command_list_params.py b/src/opencode_ai/types/command_list_params.py new file mode 100644 index 0000000..96da71f --- /dev/null +++ b/src/opencode_ai/types/command_list_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CommandListParams"] + + +class CommandListParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/command_list_response.py b/src/opencode_ai/types/command_list_response.py new file mode 100644 index 0000000..2b89a1e --- /dev/null +++ b/src/opencode_ai/types/command_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .command import Command + +__all__ = ["CommandListResponse"] + +CommandListResponse: TypeAlias = List[Command] diff --git a/src/opencode_ai/types/config.py b/src/opencode_ai/types/config.py index 76f9348..c9bcec8 100644 --- a/src/opencode_ai/types/config.py +++ b/src/opencode_ai/types/config.py @@ -7,7 +7,6 @@ from .._utils import PropertyInfo from .._models import BaseModel -from .mode_config import ModeConfig from .keybinds_config import KeybindsConfig from .mcp_local_config import McpLocalConfig from .mcp_remote_config import McpRemoteConfig @@ -15,39 +14,235 @@ __all__ = [ "Config", "Agent", + "AgentBuild", + "AgentBuildPermission", "AgentGeneral", + "AgentGeneralPermission", + "AgentPlan", + "AgentPlanPermission", "AgentAgentItem", + "AgentAgentItemPermission", + "Command", "Experimental", "ExperimentalHook", "ExperimentalHookFileEdited", "ExperimentalHookSessionCompleted", + "Formatter", + "Lsp", + "LspDisabled", + "LspUnionMember1", "Mcp", "Mode", + "ModeBuild", + "ModeBuildPermission", + "ModePlan", + "ModePlanPermission", + "ModeModeItem", + "ModeModeItemPermission", + "Permission", "Provider", "ProviderModels", "ProviderModelsCost", "ProviderModelsLimit", "ProviderOptions", + "Tui", ] -class AgentGeneral(ModeConfig): - description: str +class AgentBuildPermission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + edit: Optional[Literal["ask", "allow", "deny"]] = None -class AgentAgentItem(ModeConfig): - description: str + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class AgentBuild(BaseModel): + description: Optional[str] = None + """Description of when to use the agent""" + + disable: Optional[bool] = None + + mode: Optional[Literal["subagent", "primary", "all"]] = None + + model: Optional[str] = None + + permission: Optional[AgentBuildPermission] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + tools: Optional[Dict[str, bool]] = None + + top_p: Optional[float] = None + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class AgentGeneralPermission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + + edit: Optional[Literal["ask", "allow", "deny"]] = None + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class AgentGeneral(BaseModel): + description: Optional[str] = None + """Description of when to use the agent""" + + disable: Optional[bool] = None + + mode: Optional[Literal["subagent", "primary", "all"]] = None + + model: Optional[str] = None + + permission: Optional[AgentGeneralPermission] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + tools: Optional[Dict[str, bool]] = None + + top_p: Optional[float] = None + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class AgentPlanPermission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + + edit: Optional[Literal["ask", "allow", "deny"]] = None + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class AgentPlan(BaseModel): + description: Optional[str] = None + """Description of when to use the agent""" + + disable: Optional[bool] = None + + mode: Optional[Literal["subagent", "primary", "all"]] = None + + model: Optional[str] = None + + permission: Optional[AgentPlanPermission] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + tools: Optional[Dict[str, bool]] = None + + top_p: Optional[float] = None + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class AgentAgentItemPermission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + + edit: Optional[Literal["ask", "allow", "deny"]] = None + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class AgentAgentItem(BaseModel): + description: Optional[str] = None + """Description of when to use the agent""" + + disable: Optional[bool] = None + + mode: Optional[Literal["subagent", "primary", "all"]] = None + + model: Optional[str] = None + + permission: Optional[AgentAgentItemPermission] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + tools: Optional[Dict[str, bool]] = None + + top_p: Optional[float] = None + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] class Agent(BaseModel): + """Agent configuration, see https://opencode.ai/docs/agent""" + + build: Optional[AgentBuild] = None + general: Optional[AgentGeneral] = None - __pydantic_extra__: Dict[str, AgentAgentItem] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + plan: Optional[AgentPlan] = None + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, AgentAgentItem] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + # Stub to indicate that arbitrary properties are accepted. # To access properties that are not valid identifiers you can use `getattr`, e.g. # `getattr(obj, '$type')` def __getattr__(self, attr: str) -> AgentAgentItem: ... + else: + __pydantic_extra__: Dict[str, AgentAgentItem] + + +class Command(BaseModel): + template: str + + agent: Optional[str] = None + + description: Optional[str] = None + + model: Optional[str] = None class ExperimentalHookFileEdited(BaseModel): @@ -72,20 +267,186 @@ class Experimental(BaseModel): hook: Optional[ExperimentalHook] = None +class Formatter(BaseModel): + command: Optional[List[str]] = None + + disabled: Optional[bool] = None + + environment: Optional[Dict[str, str]] = None + + extensions: Optional[List[str]] = None + + +class LspDisabled(BaseModel): + disabled: Literal[True] + + +class LspUnionMember1(BaseModel): + command: List[str] + + disabled: Optional[bool] = None + + env: Optional[Dict[str, str]] = None + + extensions: Optional[List[str]] = None + + initialization: Optional[Dict[str, object]] = None + + +Lsp: TypeAlias = Union[LspDisabled, LspUnionMember1] + Mcp: TypeAlias = Annotated[Union[McpLocalConfig, McpRemoteConfig], PropertyInfo(discriminator="type")] +class ModeBuildPermission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + + edit: Optional[Literal["ask", "allow", "deny"]] = None + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class ModeBuild(BaseModel): + description: Optional[str] = None + """Description of when to use the agent""" + + disable: Optional[bool] = None + + mode: Optional[Literal["subagent", "primary", "all"]] = None + + model: Optional[str] = None + + permission: Optional[ModeBuildPermission] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + tools: Optional[Dict[str, bool]] = None + + top_p: Optional[float] = None + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class ModePlanPermission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + + edit: Optional[Literal["ask", "allow", "deny"]] = None + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class ModePlan(BaseModel): + description: Optional[str] = None + """Description of when to use the agent""" + + disable: Optional[bool] = None + + mode: Optional[Literal["subagent", "primary", "all"]] = None + + model: Optional[str] = None + + permission: Optional[ModePlanPermission] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + tools: Optional[Dict[str, bool]] = None + + top_p: Optional[float] = None + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + +class ModeModeItemPermission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + + edit: Optional[Literal["ask", "allow", "deny"]] = None + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None + + +class ModeModeItem(BaseModel): + description: Optional[str] = None + """Description of when to use the agent""" + + disable: Optional[bool] = None + + mode: Optional[Literal["subagent", "primary", "all"]] = None + + model: Optional[str] = None + + permission: Optional[ModeModeItemPermission] = None + + prompt: Optional[str] = None + + temperature: Optional[float] = None + + tools: Optional[Dict[str, bool]] = None + + top_p: Optional[float] = None + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] + + class Mode(BaseModel): - build: Optional[ModeConfig] = None + """@deprecated Use `agent` field instead.""" + + build: Optional[ModeBuild] = None - plan: Optional[ModeConfig] = None + plan: Optional[ModePlan] = None - __pydantic_extra__: Dict[str, ModeConfig] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, ModeModeItem] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + # Stub to indicate that arbitrary properties are accepted. # To access properties that are not valid identifiers you can use `getattr`, e.g. # `getattr(obj, '$type')` - def __getattr__(self, attr: str) -> ModeConfig: ... + def __getattr__(self, attr: str) -> ModeModeItem: ... + else: + __pydantic_extra__: Dict[str, ModeModeItem] + + +class Permission(BaseModel): + bash: Union[Literal["ask", "allow", "deny"], Dict[str, Literal["ask", "allow", "deny"]], None] = None + + edit: Optional[Literal["ask", "allow", "deny"]] = None + + webfetch: Optional[Literal["ask", "allow", "deny"]] = None class ProviderModelsCost(BaseModel): @@ -131,23 +492,34 @@ class ProviderOptions(BaseModel): base_url: Optional[str] = FieldInfo(alias="baseURL", default=None) - __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + timeout: Union[int, bool, None] = None + """Timeout in milliseconds for requests to this provider. + + Default is 300000 (5 minutes). Set to false to disable timeout. + """ + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + # Stub to indicate that arbitrary properties are accepted. # To access properties that are not valid identifiers you can use `getattr`, e.g. # `getattr(obj, '$type')` def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] class Provider(BaseModel): - models: Dict[str, ProviderModels] - id: Optional[str] = None api: Optional[str] = None env: Optional[List[str]] = None + models: Optional[Dict[str, ProviderModels]] = None + name: Optional[str] = None npm: Optional[str] = None @@ -155,12 +527,19 @@ class Provider(BaseModel): options: Optional[ProviderOptions] = None +class Tui(BaseModel): + """TUI specific settings""" + + scroll_speed: float + """TUI scroll speed""" + + class Config(BaseModel): schema_: Optional[str] = FieldInfo(alias="$schema", default=None) """JSON schema reference for configuration validation""" agent: Optional[Agent] = None - """Modes configuration, see https://opencode.ai/docs/modes""" + """Agent configuration, see https://opencode.ai/docs/agent""" autoshare: Optional[bool] = None """@deprecated Use 'share' field instead. @@ -171,11 +550,16 @@ class Config(BaseModel): autoupdate: Optional[bool] = None """Automatically update to the latest version""" + command: Optional[Dict[str, Command]] = None + """Command configuration, see https://opencode.ai/docs/commands""" + disabled_providers: Optional[List[str]] = None """Disable providers that are loaded automatically""" experimental: Optional[Experimental] = None + formatter: Optional[Dict[str, Formatter]] = None + instructions: Optional[List[str]] = None """Additional instruction files or patterns to include""" @@ -185,15 +569,21 @@ class Config(BaseModel): layout: Optional[Literal["auto", "stretch"]] = None """@deprecated Always uses stretch layout.""" + lsp: Optional[Dict[str, Lsp]] = None + mcp: Optional[Dict[str, Mcp]] = None """MCP (Model Context Protocol) server configurations""" mode: Optional[Mode] = None - """Modes configuration, see https://opencode.ai/docs/modes""" + """@deprecated Use `agent` field instead.""" model: Optional[str] = None """Model to use in the format of provider/model, eg anthropic/claude-2""" + permission: Optional[Permission] = None + + plugin: Optional[List[str]] = None + provider: Optional[Dict[str, Provider]] = None """Custom provider configurations and model overrides""" @@ -205,12 +595,19 @@ class Config(BaseModel): small_model: Optional[str] = None """ - Small model to use for tasks like summarization and title generation in the - format of provider/model + Small model to use for tasks like title generation in the format of + provider/model """ + snapshot: Optional[bool] = None + theme: Optional[str] = None """Theme name to use for the interface""" + tools: Optional[Dict[str, bool]] = None + + tui: Optional[Tui] = None + """TUI specific settings""" + username: Optional[str] = None """Custom username to display in conversations instead of system username""" diff --git a/src/opencode_ai/types/config_get_params.py b/src/opencode_ai/types/config_get_params.py new file mode 100644 index 0000000..338d65c --- /dev/null +++ b/src/opencode_ai/types/config_get_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ConfigGetParams"] + + +class ConfigGetParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/event_list_params.py b/src/opencode_ai/types/event_list_params.py new file mode 100644 index 0000000..988815e --- /dev/null +++ b/src/opencode_ai/types/event_list_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["EventListParams"] + + +class EventListParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/event_list_response.py b/src/opencode_ai/types/event_list_response.py index fa52d22..2df9d35 100644 --- a/src/opencode_ai/types/event_list_response.py +++ b/src/opencode_ai/types/event_list_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, Union, Optional +from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from pydantic import Field as FieldInfo @@ -8,8 +8,9 @@ from .part import Part from .._utils import PropertyInfo from .message import Message -from .session import Session from .._models import BaseModel +from .session.session import Session +from .session.permission import Permission from .shared.unknown_error import UnknownError from .shared.provider_auth_error import ProviderAuthError from .shared.message_aborted_error import MessageAbortedError @@ -28,11 +29,9 @@ "EventMessagePartUpdatedProperties", "EventMessagePartRemoved", "EventMessagePartRemovedProperties", - "EventStorageWrite", - "EventStorageWriteProperties", "EventPermissionUpdated", - "EventPermissionUpdatedProperties", - "EventPermissionUpdatedPropertiesTime", + "EventPermissionReplied", + "EventPermissionRepliedProperties", "EventFileEdited", "EventFileEditedProperties", "EventSessionUpdated", @@ -46,10 +45,6 @@ "EventSessionErrorPropertiesError", "EventSessionErrorPropertiesErrorMessageOutputLengthError", "EventServerConnected", - "EventFileWatcherUpdated", - "EventFileWatcherUpdatedProperties", - "EventIdeInstalled", - "EventIdeInstalledProperties", ] @@ -121,38 +116,24 @@ class EventMessagePartRemoved(BaseModel): type: Literal["message.part.removed"] -class EventStorageWriteProperties(BaseModel): - key: str - - content: Optional[object] = None - - -class EventStorageWrite(BaseModel): - properties: EventStorageWriteProperties - - type: Literal["storage.write"] - +class EventPermissionUpdated(BaseModel): + properties: Permission -class EventPermissionUpdatedPropertiesTime(BaseModel): - created: float + type: Literal["permission.updated"] -class EventPermissionUpdatedProperties(BaseModel): - id: str +class EventPermissionRepliedProperties(BaseModel): + permission_id: str = FieldInfo(alias="permissionID") - metadata: Dict[str, object] + response: str session_id: str = FieldInfo(alias="sessionID") - time: EventPermissionUpdatedPropertiesTime - - title: str - -class EventPermissionUpdated(BaseModel): - properties: EventPermissionUpdatedProperties +class EventPermissionReplied(BaseModel): + properties: EventPermissionRepliedProperties - type: Literal["permission.updated"] + type: Literal["permission.replied"] class EventFileEditedProperties(BaseModel): @@ -227,28 +208,6 @@ class EventServerConnected(BaseModel): type: Literal["server.connected"] -class EventFileWatcherUpdatedProperties(BaseModel): - event: Literal["rename", "change"] - - file: str - - -class EventFileWatcherUpdated(BaseModel): - properties: EventFileWatcherUpdatedProperties - - type: Literal["file.watcher.updated"] - - -class EventIdeInstalledProperties(BaseModel): - ide: str - - -class EventIdeInstalled(BaseModel): - properties: EventIdeInstalledProperties - - type: Literal["ide.installed"] - - EventListResponse: TypeAlias = Annotated[ Union[ EventInstallationUpdated, @@ -257,16 +216,14 @@ class EventIdeInstalled(BaseModel): EventMessageRemoved, EventMessagePartUpdated, EventMessagePartRemoved, - EventStorageWrite, EventPermissionUpdated, + EventPermissionReplied, EventFileEdited, EventSessionUpdated, EventSessionDeleted, EventSessionIdle, EventSessionError, EventServerConnected, - EventFileWatcherUpdated, - EventIdeInstalled, ], PropertyInfo(discriminator="type"), ] diff --git a/src/opencode_ai/types/file_list_params.py b/src/opencode_ai/types/file_list_params.py new file mode 100644 index 0000000..7fc1cbf --- /dev/null +++ b/src/opencode_ai/types/file_list_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["FileListParams"] + + +class FileListParams(TypedDict, total=False): + path: Required[str] + + directory: str diff --git a/src/opencode_ai/types/file_list_response.py b/src/opencode_ai/types/file_list_response.py new file mode 100644 index 0000000..30e17d8 --- /dev/null +++ b/src/opencode_ai/types/file_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .file_node import FileNode + +__all__ = ["FileListResponse"] + +FileListResponse: TypeAlias = List[FileNode] diff --git a/src/opencode_ai/types/file_node.py b/src/opencode_ai/types/file_node.py new file mode 100644 index 0000000..56b8ded --- /dev/null +++ b/src/opencode_ai/types/file_node.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from .._models import BaseModel + +__all__ = ["FileNode"] + + +class FileNode(BaseModel): + ignored: bool + + name: str + + path: str + + type: Literal["file", "directory"] diff --git a/src/opencode_ai/types/file_read_params.py b/src/opencode_ai/types/file_read_params.py index b251a07..087534e 100644 --- a/src/opencode_ai/types/file_read_params.py +++ b/src/opencode_ai/types/file_read_params.py @@ -9,3 +9,5 @@ class FileReadParams(TypedDict, total=False): path: Required[str] + + directory: str diff --git a/src/opencode_ai/types/file_status_params.py b/src/opencode_ai/types/file_status_params.py new file mode 100644 index 0000000..d9d0922 --- /dev/null +++ b/src/opencode_ai/types/file_status_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["FileStatusParams"] + + +class FileStatusParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/find_files_params.py b/src/opencode_ai/types/find_files_params.py index c2b3093..5b23a52 100644 --- a/src/opencode_ai/types/find_files_params.py +++ b/src/opencode_ai/types/find_files_params.py @@ -9,3 +9,5 @@ class FindFilesParams(TypedDict, total=False): query: Required[str] + + directory: str diff --git a/src/opencode_ai/types/find_symbols_params.py b/src/opencode_ai/types/find_symbols_params.py index 60f9e9e..4cd4e34 100644 --- a/src/opencode_ai/types/find_symbols_params.py +++ b/src/opencode_ai/types/find_symbols_params.py @@ -9,3 +9,5 @@ class FindSymbolsParams(TypedDict, total=False): query: Required[str] + + directory: str diff --git a/src/opencode_ai/types/find_text_params.py b/src/opencode_ai/types/find_text_params.py index f35ea4f..c1c31df 100644 --- a/src/opencode_ai/types/find_text_params.py +++ b/src/opencode_ai/types/find_text_params.py @@ -9,3 +9,5 @@ class FindTextParams(TypedDict, total=False): pattern: Required[str] + + directory: str diff --git a/src/opencode_ai/types/keybinds_config.py b/src/opencode_ai/types/keybinds_config.py index c7cb57c..452fa2b 100644 --- a/src/opencode_ai/types/keybinds_config.py +++ b/src/opencode_ai/types/keybinds_config.py @@ -8,6 +8,15 @@ class KeybindsConfig(BaseModel): + agent_cycle: str + """Next agent""" + + agent_cycle_reverse: str + """Previous agent""" + + agent_list: str + """List agents""" + app_exit: str """Exit the application""" @@ -18,16 +27,16 @@ class KeybindsConfig(BaseModel): """Open external editor""" file_close: str - """Close file""" + """@deprecated Close file""" file_diff_toggle: str - """Split/unified diff""" + """@deprecated Split/unified diff""" file_list: str - """List files""" + """@deprecated Currently not available. List files""" file_search: str - """Search file""" + """@deprecated Search file""" input_clear: str """Clear input field""" @@ -60,10 +69,10 @@ class KeybindsConfig(BaseModel): """Navigate to last message""" messages_layout_toggle: str - """Toggle layout""" + """@deprecated Toggle layout""" messages_next: str - """Navigate to next message""" + """@deprecated Navigate to next message""" messages_page_down: str """Scroll messages down by one page""" @@ -72,7 +81,7 @@ class KeybindsConfig(BaseModel): """Scroll messages up by one page""" messages_previous: str - """Navigate to previous message""" + """@deprecated Navigate to previous message""" messages_redo: str """Redo message""" @@ -83,12 +92,24 @@ class KeybindsConfig(BaseModel): messages_undo: str """Undo message""" + api_model_cycle_recent: str = FieldInfo(alias="model_cycle_recent") + """Next recent model""" + + api_model_cycle_recent_reverse: str = FieldInfo(alias="model_cycle_recent_reverse") + """Previous recent model""" + api_model_list: str = FieldInfo(alias="model_list") """List available models""" project_init: str """Create/update AGENTS.md""" + session_child_cycle: str + """Cycle to next child session""" + + session_child_cycle_reverse: str + """Cycle to previous child session""" + session_compact: str """Compact the session""" @@ -107,17 +128,29 @@ class KeybindsConfig(BaseModel): session_share: str """Share current session""" + session_timeline: str + """Show session timeline""" + session_unshare: str """Unshare current session""" + switch_agent: str + """@deprecated use agent_cycle. Next agent""" + + switch_agent_reverse: str + """@deprecated use agent_cycle_reverse. Previous agent""" + switch_mode: str - """Next mode""" + """@deprecated use agent_cycle. Next mode""" switch_mode_reverse: str - """Previous Mode""" + """@deprecated use agent_cycle_reverse. Previous mode""" theme_list: str """List available themes""" + thinking_blocks: str + """Toggle thinking blocks""" + tool_details: str """Toggle tool details""" diff --git a/src/opencode_ai/types/mode.py b/src/opencode_ai/types/mode.py deleted file mode 100644 index 041b7f3..0000000 --- a/src/opencode_ai/types/mode.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["Mode", "Model"] - - -class Model(BaseModel): - api_model_id: str = FieldInfo(alias="modelID") - - provider_id: str = FieldInfo(alias="providerID") - - -class Mode(BaseModel): - name: str - - tools: Dict[str, bool] - - model: Optional[Model] = None - - prompt: Optional[str] = None - - temperature: Optional[float] = None diff --git a/src/opencode_ai/types/mode_config.py b/src/opencode_ai/types/mode_config.py deleted file mode 100644 index d7561c2..0000000 --- a/src/opencode_ai/types/mode_config.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional - -from .._models import BaseModel - -__all__ = ["ModeConfig"] - - -class ModeConfig(BaseModel): - disable: Optional[bool] = None - - model: Optional[str] = None - - prompt: Optional[str] = None - - temperature: Optional[float] = None - - tools: Optional[Dict[str, bool]] = None diff --git a/src/opencode_ai/types/part.py b/src/opencode_ai/types/part.py index 5a74aa5..820c1e1 100644 --- a/src/opencode_ai/types/part.py +++ b/src/opencode_ai/types/part.py @@ -10,7 +10,9 @@ from .file_part import FilePart from .text_part import TextPart from .tool_part import ToolPart +from .agent_part import AgentPart from .snapshot_part import SnapshotPart +from .reasoning_part import ReasoningPart from .step_start_part import StepStartPart from .step_finish_part import StepFinishPart @@ -32,6 +34,8 @@ class PatchPart(BaseModel): Part: TypeAlias = Annotated[ - Union[TextPart, FilePart, ToolPart, StepStartPart, StepFinishPart, SnapshotPart, PatchPart], + Union[ + TextPart, ReasoningPart, FilePart, ToolPart, StepStartPart, StepFinishPart, SnapshotPart, PatchPart, AgentPart + ], PropertyInfo(discriminator="type"), ] diff --git a/src/opencode_ai/types/path.py b/src/opencode_ai/types/path.py new file mode 100644 index 0000000..8885465 --- /dev/null +++ b/src/opencode_ai/types/path.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["Path"] + + +class Path(BaseModel): + config: str + + directory: str + + state: str + + worktree: str diff --git a/src/opencode_ai/types/path_get_params.py b/src/opencode_ai/types/path_get_params.py new file mode 100644 index 0000000..49bd72f --- /dev/null +++ b/src/opencode_ai/types/path_get_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["PathGetParams"] + + +class PathGetParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/app.py b/src/opencode_ai/types/project.py similarity index 53% rename from src/opencode_ai/types/app.py rename to src/opencode_ai/types/project.py index d60c600..f19b103 100644 --- a/src/opencode_ai/types/app.py +++ b/src/opencode_ai/types/project.py @@ -1,33 +1,24 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional +from typing_extensions import Literal from .._models import BaseModel -__all__ = ["App", "Path", "Time"] - - -class Path(BaseModel): - config: str - - cwd: str - - data: str - - root: str - - state: str +__all__ = ["Project", "Time"] class Time(BaseModel): + created: float + initialized: Optional[float] = None -class App(BaseModel): - git: bool +class Project(BaseModel): + id: str - hostname: str + time: Time - path: Path + worktree: str - time: Time + vcs: Optional[Literal["git"]] = None diff --git a/src/opencode_ai/types/project_current_params.py b/src/opencode_ai/types/project_current_params.py new file mode 100644 index 0000000..6b9a645 --- /dev/null +++ b/src/opencode_ai/types/project_current_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectCurrentParams"] + + +class ProjectCurrentParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/project_list_params.py b/src/opencode_ai/types/project_list_params.py new file mode 100644 index 0000000..c60aaca --- /dev/null +++ b/src/opencode_ai/types/project_list_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectListParams"] + + +class ProjectListParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/project_list_response.py b/src/opencode_ai/types/project_list_response.py new file mode 100644 index 0000000..2d05a23 --- /dev/null +++ b/src/opencode_ai/types/project_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .project import Project + +__all__ = ["ProjectListResponse"] + +ProjectListResponse: TypeAlias = List[Project] diff --git a/src/opencode_ai/types/reasoning_part.py b/src/opencode_ai/types/reasoning_part.py new file mode 100644 index 0000000..71e0052 --- /dev/null +++ b/src/opencode_ai/types/reasoning_part.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ReasoningPart", "Time"] + + +class Time(BaseModel): + start: float + + end: Optional[float] = None + + +class ReasoningPart(BaseModel): + id: str + + message_id: str = FieldInfo(alias="messageID") + + session_id: str = FieldInfo(alias="sessionID") + + text: str + + time: Time + + type: Literal["reasoning"] + + metadata: Optional[Dict[str, object]] = None diff --git a/src/opencode_ai/types/session/__init__.py b/src/opencode_ai/types/session/__init__.py new file mode 100644 index 0000000..b360854 --- /dev/null +++ b/src/opencode_ai/types/session/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .session import Session as Session +from .permission import Permission as Permission +from .permission_respond_params import PermissionRespondParams as PermissionRespondParams +from .permission_respond_response import PermissionRespondResponse as PermissionRespondResponse diff --git a/src/opencode_ai/types/session/permission.py b/src/opencode_ai/types/session/permission.py new file mode 100644 index 0000000..88fc6cb --- /dev/null +++ b/src/opencode_ai/types/session/permission.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["Permission", "Time"] + + +class Time(BaseModel): + created: float + + +class Permission(BaseModel): + id: str + + message_id: str = FieldInfo(alias="messageID") + + metadata: Dict[str, object] + + session_id: str = FieldInfo(alias="sessionID") + + time: Time + + title: str + + type: str + + call_id: Optional[str] = FieldInfo(alias="callID", default=None) + + pattern: Optional[str] = None diff --git a/src/opencode_ai/types/session/permission_respond_params.py b/src/opencode_ai/types/session/permission_respond_params.py new file mode 100644 index 0000000..8698254 --- /dev/null +++ b/src/opencode_ai/types/session/permission_respond_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["PermissionRespondParams"] + + +class PermissionRespondParams(TypedDict, total=False): + id: Required[str] + + response: Required[Literal["once", "always", "reject"]] + + directory: str diff --git a/src/opencode_ai/types/session/permission_respond_response.py b/src/opencode_ai/types/session/permission_respond_response.py new file mode 100644 index 0000000..0cc1480 --- /dev/null +++ b/src/opencode_ai/types/session/permission_respond_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["PermissionRespondResponse"] + +PermissionRespondResponse: TypeAlias = bool diff --git a/src/opencode_ai/types/session.py b/src/opencode_ai/types/session/session.py similarity index 87% rename from src/opencode_ai/types/session.py rename to src/opencode_ai/types/session/session.py index a0ed929..af9fcd3 100644 --- a/src/opencode_ai/types/session.py +++ b/src/opencode_ai/types/session/session.py @@ -4,7 +4,7 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel __all__ = ["Session", "Time", "Revert", "Share"] @@ -32,6 +32,10 @@ class Share(BaseModel): class Session(BaseModel): id: str + directory: str + + project_id: str = FieldInfo(alias="projectID") + time: Time title: str diff --git a/src/opencode_ai/types/session_abort_params.py b/src/opencode_ai/types/session_abort_params.py new file mode 100644 index 0000000..15a923b --- /dev/null +++ b/src/opencode_ai/types/session_abort_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionAbortParams"] + + +class SessionAbortParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_children_params.py b/src/opencode_ai/types/session_children_params.py new file mode 100644 index 0000000..49004c4 --- /dev/null +++ b/src/opencode_ai/types/session_children_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionChildrenParams"] + + +class SessionChildrenParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_children_response.py b/src/opencode_ai/types/session_children_response.py new file mode 100644 index 0000000..975bc30 --- /dev/null +++ b/src/opencode_ai/types/session_children_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .session.session import Session + +__all__ = ["SessionChildrenResponse"] + +SessionChildrenResponse: TypeAlias = List[Session] diff --git a/src/opencode_ai/types/session_command_params.py b/src/opencode_ai/types/session_command_params.py new file mode 100644 index 0000000..0ae5a37 --- /dev/null +++ b/src/opencode_ai/types/session_command_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["SessionCommandParams"] + + +class SessionCommandParams(TypedDict, total=False): + arguments: Required[str] + + command: Required[str] + + directory: str + + agent: str + + message_id: Annotated[str, PropertyInfo(alias="messageID")] + + model: str diff --git a/src/opencode_ai/types/session_command_response.py b/src/opencode_ai/types/session_command_response.py new file mode 100644 index 0000000..c697bbd --- /dev/null +++ b/src/opencode_ai/types/session_command_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .part import Part +from .._models import BaseModel +from .assistant_message import AssistantMessage + +__all__ = ["SessionCommandResponse"] + + +class SessionCommandResponse(BaseModel): + info: AssistantMessage + + parts: List[Part] diff --git a/src/opencode_ai/types/session_create_params.py b/src/opencode_ai/types/session_create_params.py new file mode 100644 index 0000000..d41fa79 --- /dev/null +++ b/src/opencode_ai/types/session_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["SessionCreateParams"] + + +class SessionCreateParams(TypedDict, total=False): + directory: str + + parent_id: Annotated[str, PropertyInfo(alias="parentID")] + + title: str diff --git a/src/opencode_ai/types/session_delete_params.py b/src/opencode_ai/types/session_delete_params.py new file mode 100644 index 0000000..3549cea --- /dev/null +++ b/src/opencode_ai/types/session_delete_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionDeleteParams"] + + +class SessionDeleteParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_get_params.py b/src/opencode_ai/types/session_get_params.py new file mode 100644 index 0000000..f77e9b0 --- /dev/null +++ b/src/opencode_ai/types/session_get_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionGetParams"] + + +class SessionGetParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_init_params.py b/src/opencode_ai/types/session_init_params.py index 8d6d0d8..cf370b8 100644 --- a/src/opencode_ai/types/session_init_params.py +++ b/src/opencode_ai/types/session_init_params.py @@ -15,3 +15,5 @@ class SessionInitParams(TypedDict, total=False): model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]] provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]] + + directory: str diff --git a/src/opencode_ai/types/session_list_params.py b/src/opencode_ai/types/session_list_params.py new file mode 100644 index 0000000..21d63f1 --- /dev/null +++ b/src/opencode_ai/types/session_list_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionListParams"] + + +class SessionListParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_list_response.py b/src/opencode_ai/types/session_list_response.py index ca162dd..5f221fa 100644 --- a/src/opencode_ai/types/session_list_response.py +++ b/src/opencode_ai/types/session_list_response.py @@ -3,7 +3,7 @@ from typing import List from typing_extensions import TypeAlias -from .session import Session +from .session.session import Session __all__ = ["SessionListResponse"] diff --git a/src/opencode_ai/types/session_message_params.py b/src/opencode_ai/types/session_message_params.py new file mode 100644 index 0000000..aa00f7c --- /dev/null +++ b/src/opencode_ai/types/session_message_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SessionMessageParams"] + + +class SessionMessageParams(TypedDict, total=False): + id: Required[str] + """Session ID""" + + directory: str diff --git a/src/opencode_ai/types/session_message_response.py b/src/opencode_ai/types/session_message_response.py new file mode 100644 index 0000000..a64b088 --- /dev/null +++ b/src/opencode_ai/types/session_message_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .part import Part +from .message import Message +from .._models import BaseModel + +__all__ = ["SessionMessageResponse"] + + +class SessionMessageResponse(BaseModel): + info: Message + + parts: List[Part] diff --git a/src/opencode_ai/types/session_messages_params.py b/src/opencode_ai/types/session_messages_params.py new file mode 100644 index 0000000..6831706 --- /dev/null +++ b/src/opencode_ai/types/session_messages_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionMessagesParams"] + + +class SessionMessagesParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_chat_params.py b/src/opencode_ai/types/session_prompt_params.py similarity index 67% rename from src/opencode_ai/types/session_chat_params.py rename to src/opencode_ai/types/session_prompt_params.py index e827282..78fad20 100644 --- a/src/opencode_ai/types/session_chat_params.py +++ b/src/opencode_ai/types/session_prompt_params.py @@ -8,24 +8,31 @@ from .._utils import PropertyInfo from .file_part_input_param import FilePartInputParam from .text_part_input_param import TextPartInputParam +from .agent_part_input_param import AgentPartInputParam -__all__ = ["SessionChatParams", "Part"] +__all__ = ["SessionPromptParams", "Part", "Model"] -class SessionChatParams(TypedDict, total=False): - model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]] - +class SessionPromptParams(TypedDict, total=False): parts: Required[Iterable[Part]] - provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]] + directory: str + + agent: str message_id: Annotated[str, PropertyInfo(alias="messageID")] - mode: str + model: Model system: str tools: Dict[str, bool] -Part: TypeAlias = Union[TextPartInputParam, FilePartInputParam] +Part: TypeAlias = Union[TextPartInputParam, FilePartInputParam, AgentPartInputParam] + + +class Model(TypedDict, total=False): + model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]] + + provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]] diff --git a/src/opencode_ai/types/session_prompt_response.py b/src/opencode_ai/types/session_prompt_response.py new file mode 100644 index 0000000..2502def --- /dev/null +++ b/src/opencode_ai/types/session_prompt_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .part import Part +from .._models import BaseModel +from .assistant_message import AssistantMessage + +__all__ = ["SessionPromptResponse"] + + +class SessionPromptResponse(BaseModel): + info: AssistantMessage + + parts: List[Part] diff --git a/src/opencode_ai/types/session_revert_params.py b/src/opencode_ai/types/session_revert_params.py index e5361ee..b56a5b4 100644 --- a/src/opencode_ai/types/session_revert_params.py +++ b/src/opencode_ai/types/session_revert_params.py @@ -12,4 +12,6 @@ class SessionRevertParams(TypedDict, total=False): message_id: Required[Annotated[str, PropertyInfo(alias="messageID")]] + directory: str + part_id: Annotated[str, PropertyInfo(alias="partID")] diff --git a/src/opencode_ai/types/session_share_params.py b/src/opencode_ai/types/session_share_params.py new file mode 100644 index 0000000..9c1dbb6 --- /dev/null +++ b/src/opencode_ai/types/session_share_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionShareParams"] + + +class SessionShareParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_shell_params.py b/src/opencode_ai/types/session_shell_params.py new file mode 100644 index 0000000..7e45f59 --- /dev/null +++ b/src/opencode_ai/types/session_shell_params.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["SessionShellParams"] + + +class SessionShellParams(TypedDict, total=False): + agent: Required[str] + + command: Required[str] + + directory: str diff --git a/src/opencode_ai/types/session_summarize_params.py b/src/opencode_ai/types/session_summarize_params.py index 46e3aa2..f976e9f 100644 --- a/src/opencode_ai/types/session_summarize_params.py +++ b/src/opencode_ai/types/session_summarize_params.py @@ -13,3 +13,5 @@ class SessionSummarizeParams(TypedDict, total=False): model_id: Required[Annotated[str, PropertyInfo(alias="modelID")]] provider_id: Required[Annotated[str, PropertyInfo(alias="providerID")]] + + directory: str diff --git a/src/opencode_ai/types/session_unrevert_params.py b/src/opencode_ai/types/session_unrevert_params.py new file mode 100644 index 0000000..24cbedd --- /dev/null +++ b/src/opencode_ai/types/session_unrevert_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionUnrevertParams"] + + +class SessionUnrevertParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_unshare_params.py b/src/opencode_ai/types/session_unshare_params.py new file mode 100644 index 0000000..bbbd1be --- /dev/null +++ b/src/opencode_ai/types/session_unshare_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionUnshareParams"] + + +class SessionUnshareParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/session_update_params.py b/src/opencode_ai/types/session_update_params.py new file mode 100644 index 0000000..c1169af --- /dev/null +++ b/src/opencode_ai/types/session_update_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SessionUpdateParams"] + + +class SessionUpdateParams(TypedDict, total=False): + directory: str + + title: str diff --git a/src/opencode_ai/types/tool_state_error.py b/src/opencode_ai/types/tool_state_error.py index 141a4cd..8f6bf5a 100644 --- a/src/opencode_ai/types/tool_state_error.py +++ b/src/opencode_ai/types/tool_state_error.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict +from typing import Dict, Optional from typing_extensions import Literal from .._models import BaseModel @@ -22,3 +22,5 @@ class ToolStateError(BaseModel): status: Literal["error"] time: Time + + metadata: Optional[Dict[str, object]] = None diff --git a/src/opencode_ai/types/tui_append_prompt_params.py b/src/opencode_ai/types/tui_append_prompt_params.py index 431f731..48a0e32 100644 --- a/src/opencode_ai/types/tui_append_prompt_params.py +++ b/src/opencode_ai/types/tui_append_prompt_params.py @@ -9,3 +9,5 @@ class TuiAppendPromptParams(TypedDict, total=False): text: Required[str] + + directory: str diff --git a/src/opencode_ai/types/tui_clear_prompt_params.py b/src/opencode_ai/types/tui_clear_prompt_params.py new file mode 100644 index 0000000..5f1a7f3 --- /dev/null +++ b/src/opencode_ai/types/tui_clear_prompt_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TuiClearPromptParams"] + + +class TuiClearPromptParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/tui_clear_prompt_response.py b/src/opencode_ai/types/tui_clear_prompt_response.py new file mode 100644 index 0000000..3b96c7a --- /dev/null +++ b/src/opencode_ai/types/tui_clear_prompt_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["TuiClearPromptResponse"] + +TuiClearPromptResponse: TypeAlias = bool diff --git a/src/opencode_ai/types/tui_execute_command_params.py b/src/opencode_ai/types/tui_execute_command_params.py new file mode 100644 index 0000000..a73953f --- /dev/null +++ b/src/opencode_ai/types/tui_execute_command_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["TuiExecuteCommandParams"] + + +class TuiExecuteCommandParams(TypedDict, total=False): + command: Required[str] + + directory: str diff --git a/src/opencode_ai/types/tui_execute_command_response.py b/src/opencode_ai/types/tui_execute_command_response.py new file mode 100644 index 0000000..5ceb1dd --- /dev/null +++ b/src/opencode_ai/types/tui_execute_command_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["TuiExecuteCommandResponse"] + +TuiExecuteCommandResponse: TypeAlias = bool diff --git a/src/opencode_ai/types/tui_open_help_params.py b/src/opencode_ai/types/tui_open_help_params.py new file mode 100644 index 0000000..7d613af --- /dev/null +++ b/src/opencode_ai/types/tui_open_help_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TuiOpenHelpParams"] + + +class TuiOpenHelpParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/tui_open_models_params.py b/src/opencode_ai/types/tui_open_models_params.py new file mode 100644 index 0000000..f514e95 --- /dev/null +++ b/src/opencode_ai/types/tui_open_models_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TuiOpenModelsParams"] + + +class TuiOpenModelsParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/tui_open_models_response.py b/src/opencode_ai/types/tui_open_models_response.py new file mode 100644 index 0000000..073a388 --- /dev/null +++ b/src/opencode_ai/types/tui_open_models_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["TuiOpenModelsResponse"] + +TuiOpenModelsResponse: TypeAlias = bool diff --git a/src/opencode_ai/types/tui_open_sessions_params.py b/src/opencode_ai/types/tui_open_sessions_params.py new file mode 100644 index 0000000..284cd70 --- /dev/null +++ b/src/opencode_ai/types/tui_open_sessions_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TuiOpenSessionsParams"] + + +class TuiOpenSessionsParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/tui_open_sessions_response.py b/src/opencode_ai/types/tui_open_sessions_response.py new file mode 100644 index 0000000..eab352a --- /dev/null +++ b/src/opencode_ai/types/tui_open_sessions_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["TuiOpenSessionsResponse"] + +TuiOpenSessionsResponse: TypeAlias = bool diff --git a/src/opencode_ai/types/tui_open_themes_params.py b/src/opencode_ai/types/tui_open_themes_params.py new file mode 100644 index 0000000..8adc30d --- /dev/null +++ b/src/opencode_ai/types/tui_open_themes_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TuiOpenThemesParams"] + + +class TuiOpenThemesParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/tui_open_themes_response.py b/src/opencode_ai/types/tui_open_themes_response.py new file mode 100644 index 0000000..8623270 --- /dev/null +++ b/src/opencode_ai/types/tui_open_themes_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["TuiOpenThemesResponse"] + +TuiOpenThemesResponse: TypeAlias = bool diff --git a/src/opencode_ai/types/tui_show_toast_params.py b/src/opencode_ai/types/tui_show_toast_params.py new file mode 100644 index 0000000..bbc7e15 --- /dev/null +++ b/src/opencode_ai/types/tui_show_toast_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["TuiShowToastParams"] + + +class TuiShowToastParams(TypedDict, total=False): + message: Required[str] + + variant: Required[Literal["info", "success", "warning", "error"]] + + directory: str + + title: str diff --git a/src/opencode_ai/types/app_init_response.py b/src/opencode_ai/types/tui_show_toast_response.py similarity index 63% rename from src/opencode_ai/types/app_init_response.py rename to src/opencode_ai/types/tui_show_toast_response.py index 08e3d67..e66d1de 100644 --- a/src/opencode_ai/types/app_init_response.py +++ b/src/opencode_ai/types/tui_show_toast_response.py @@ -2,6 +2,6 @@ from typing_extensions import TypeAlias -__all__ = ["AppInitResponse"] +__all__ = ["TuiShowToastResponse"] -AppInitResponse: TypeAlias = bool +TuiShowToastResponse: TypeAlias = bool diff --git a/src/opencode_ai/types/tui_submit_prompt_params.py b/src/opencode_ai/types/tui_submit_prompt_params.py new file mode 100644 index 0000000..4331fda --- /dev/null +++ b/src/opencode_ai/types/tui_submit_prompt_params.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TuiSubmitPromptParams"] + + +class TuiSubmitPromptParams(TypedDict, total=False): + directory: str diff --git a/src/opencode_ai/types/tui_submit_prompt_response.py b/src/opencode_ai/types/tui_submit_prompt_response.py new file mode 100644 index 0000000..55cf124 --- /dev/null +++ b/src/opencode_ai/types/tui_submit_prompt_response.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import TypeAlias + +__all__ = ["TuiSubmitPromptResponse"] + +TuiSubmitPromptResponse: TypeAlias = bool diff --git a/tests/api_resources/session/__init__.py b/tests/api_resources/session/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/session/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/session/test_permissions.py b/tests/api_resources/session/test_permissions.py new file mode 100644 index 0000000..97a1c50 --- /dev/null +++ b/tests/api_resources/session/test_permissions.py @@ -0,0 +1,160 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from opencode_ai import Opencode, AsyncOpencode +from tests.utils import assert_matches_type +from opencode_ai.types.session import PermissionRespondResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPermissions: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_respond(self, client: Opencode) -> None: + permission = client.session.permissions.respond( + permission_id="permissionID", + id="id", + response="once", + ) + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_respond_with_all_params(self, client: Opencode) -> None: + permission = client.session.permissions.respond( + permission_id="permissionID", + id="id", + response="once", + directory="directory", + ) + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_respond(self, client: Opencode) -> None: + response = client.session.permissions.with_raw_response.respond( + permission_id="permissionID", + id="id", + response="once", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = response.parse() + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_respond(self, client: Opencode) -> None: + with client.session.permissions.with_streaming_response.respond( + permission_id="permissionID", + id="id", + response="once", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = response.parse() + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_respond(self, client: Opencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.session.permissions.with_raw_response.respond( + permission_id="permissionID", + id="", + response="once", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `permission_id` but received ''"): + client.session.permissions.with_raw_response.respond( + permission_id="", + id="id", + response="once", + ) + + +class TestAsyncPermissions: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_respond(self, async_client: AsyncOpencode) -> None: + permission = await async_client.session.permissions.respond( + permission_id="permissionID", + id="id", + response="once", + ) + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_respond_with_all_params(self, async_client: AsyncOpencode) -> None: + permission = await async_client.session.permissions.respond( + permission_id="permissionID", + id="id", + response="once", + directory="directory", + ) + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_respond(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.permissions.with_raw_response.respond( + permission_id="permissionID", + id="id", + response="once", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + permission = await response.parse() + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_respond(self, async_client: AsyncOpencode) -> None: + async with async_client.session.permissions.with_streaming_response.respond( + permission_id="permissionID", + id="id", + response="once", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + permission = await response.parse() + assert_matches_type(PermissionRespondResponse, permission, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_respond(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.permissions.with_raw_response.respond( + permission_id="permissionID", + id="", + response="once", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `permission_id` but received ''"): + await async_client.session.permissions.with_raw_response.respond( + permission_id="", + id="id", + response="once", + ) diff --git a/tests/api_resources/test_agent.py b/tests/api_resources/test_agent.py new file mode 100644 index 0000000..0cafe73 --- /dev/null +++ b/tests/api_resources/test_agent.py @@ -0,0 +1,96 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from opencode_ai import Opencode, AsyncOpencode +from tests.utils import assert_matches_type +from opencode_ai.types import AgentListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAgent: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Opencode) -> None: + agent = client.agent.list() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Opencode) -> None: + agent = client.agent.list( + directory="directory", + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Opencode) -> None: + response = client.agent.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Opencode) -> None: + with client.agent.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncAgent: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncOpencode) -> None: + agent = await async_client.agent.list() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None: + agent = await async_client.agent.list( + directory="directory", + ) + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: + response = await async_client.agent.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + agent = await response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None: + async with async_client.agent.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + agent = await response.parse() + assert_matches_type(AgentListResponse, agent, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_app.py b/tests/api_resources/test_app.py index e0f0670..8a9bf1e 100644 --- a/tests/api_resources/test_app.py +++ b/tests/api_resources/test_app.py @@ -9,13 +9,7 @@ from opencode_ai import Opencode, AsyncOpencode from tests.utils import assert_matches_type -from opencode_ai.types import ( - App, - AppLogResponse, - AppInitResponse, - AppModesResponse, - AppProvidersResponse, -) +from opencode_ai.types import AppLogResponse, AppProvidersResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -23,62 +17,6 @@ class TestApp: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_get(self, client: Opencode) -> None: - app = client.app.get() - assert_matches_type(App, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_get(self, client: Opencode) -> None: - response = client.app.with_raw_response.get() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - app = response.parse() - assert_matches_type(App, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_get(self, client: Opencode) -> None: - with client.app.with_streaming_response.get() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - app = response.parse() - assert_matches_type(App, app, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_init(self, client: Opencode) -> None: - app = client.app.init() - assert_matches_type(AppInitResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_init(self, client: Opencode) -> None: - response = client.app.with_raw_response.init() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - app = response.parse() - assert_matches_type(AppInitResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_init(self, client: Opencode) -> None: - with client.app.with_streaming_response.init() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - app = response.parse() - assert_matches_type(AppInitResponse, app, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_log(self, client: Opencode) -> None: @@ -96,6 +34,7 @@ def test_method_log_with_all_params(self, client: Opencode) -> None: level="debug", message="message", service="service", + directory="directory", extra={"foo": "bar"}, ) assert_matches_type(AppLogResponse, app, path=["response"]) @@ -132,36 +71,16 @@ def test_streaming_response_log(self, client: Opencode) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_modes(self, client: Opencode) -> None: - app = client.app.modes() - assert_matches_type(AppModesResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_modes(self, client: Opencode) -> None: - response = client.app.with_raw_response.modes() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - app = response.parse() - assert_matches_type(AppModesResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_modes(self, client: Opencode) -> None: - with client.app.with_streaming_response.modes() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - app = response.parse() - assert_matches_type(AppModesResponse, app, path=["response"]) - - assert cast(Any, response.is_closed) is True + def test_method_providers(self, client: Opencode) -> None: + app = client.app.providers() + assert_matches_type(AppProvidersResponse, app, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_providers(self, client: Opencode) -> None: - app = client.app.providers() + def test_method_providers_with_all_params(self, client: Opencode) -> None: + app = client.app.providers( + directory="directory", + ) assert_matches_type(AppProvidersResponse, app, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @@ -192,62 +111,6 @@ class TestAsyncApp: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_get(self, async_client: AsyncOpencode) -> None: - app = await async_client.app.get() - assert_matches_type(App, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_get(self, async_client: AsyncOpencode) -> None: - response = await async_client.app.with_raw_response.get() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - app = await response.parse() - assert_matches_type(App, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_get(self, async_client: AsyncOpencode) -> None: - async with async_client.app.with_streaming_response.get() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - app = await response.parse() - assert_matches_type(App, app, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_init(self, async_client: AsyncOpencode) -> None: - app = await async_client.app.init() - assert_matches_type(AppInitResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_init(self, async_client: AsyncOpencode) -> None: - response = await async_client.app.with_raw_response.init() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - app = await response.parse() - assert_matches_type(AppInitResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_init(self, async_client: AsyncOpencode) -> None: - async with async_client.app.with_streaming_response.init() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - app = await response.parse() - assert_matches_type(AppInitResponse, app, path=["response"]) - - assert cast(Any, response.is_closed) is True - @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_log(self, async_client: AsyncOpencode) -> None: @@ -265,6 +128,7 @@ async def test_method_log_with_all_params(self, async_client: AsyncOpencode) -> level="debug", message="message", service="service", + directory="directory", extra={"foo": "bar"}, ) assert_matches_type(AppLogResponse, app, path=["response"]) @@ -301,36 +165,16 @@ async def test_streaming_response_log(self, async_client: AsyncOpencode) -> None @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_modes(self, async_client: AsyncOpencode) -> None: - app = await async_client.app.modes() - assert_matches_type(AppModesResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_modes(self, async_client: AsyncOpencode) -> None: - response = await async_client.app.with_raw_response.modes() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - app = await response.parse() - assert_matches_type(AppModesResponse, app, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_modes(self, async_client: AsyncOpencode) -> None: - async with async_client.app.with_streaming_response.modes() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - app = await response.parse() - assert_matches_type(AppModesResponse, app, path=["response"]) - - assert cast(Any, response.is_closed) is True + async def test_method_providers(self, async_client: AsyncOpencode) -> None: + app = await async_client.app.providers() + assert_matches_type(AppProvidersResponse, app, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_providers(self, async_client: AsyncOpencode) -> None: - app = await async_client.app.providers() + async def test_method_providers_with_all_params(self, async_client: AsyncOpencode) -> None: + app = await async_client.app.providers( + directory="directory", + ) assert_matches_type(AppProvidersResponse, app, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") diff --git a/tests/api_resources/test_command.py b/tests/api_resources/test_command.py new file mode 100644 index 0000000..c966fef --- /dev/null +++ b/tests/api_resources/test_command.py @@ -0,0 +1,96 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from opencode_ai import Opencode, AsyncOpencode +from tests.utils import assert_matches_type +from opencode_ai.types import CommandListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCommand: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Opencode) -> None: + command = client.command.list() + assert_matches_type(CommandListResponse, command, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Opencode) -> None: + command = client.command.list( + directory="directory", + ) + assert_matches_type(CommandListResponse, command, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Opencode) -> None: + response = client.command.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + command = response.parse() + assert_matches_type(CommandListResponse, command, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Opencode) -> None: + with client.command.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + command = response.parse() + assert_matches_type(CommandListResponse, command, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncCommand: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncOpencode) -> None: + command = await async_client.command.list() + assert_matches_type(CommandListResponse, command, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None: + command = await async_client.command.list( + directory="directory", + ) + assert_matches_type(CommandListResponse, command, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: + response = await async_client.command.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + command = await response.parse() + assert_matches_type(CommandListResponse, command, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None: + async with async_client.command.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + command = await response.parse() + assert_matches_type(CommandListResponse, command, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_config.py b/tests/api_resources/test_config.py index 86e4c8f..6c6f053 100644 --- a/tests/api_resources/test_config.py +++ b/tests/api_resources/test_config.py @@ -23,6 +23,14 @@ def test_method_get(self, client: Opencode) -> None: config = client.config.get() assert_matches_type(Config, config, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get_with_all_params(self, client: Opencode) -> None: + config = client.config.get( + directory="directory", + ) + assert_matches_type(Config, config, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_get(self, client: Opencode) -> None: @@ -57,6 +65,14 @@ async def test_method_get(self, async_client: AsyncOpencode) -> None: config = await async_client.config.get() assert_matches_type(Config, config, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get_with_all_params(self, async_client: AsyncOpencode) -> None: + config = await async_client.config.get( + directory="directory", + ) + assert_matches_type(Config, config, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_get(self, async_client: AsyncOpencode) -> None: diff --git a/tests/api_resources/test_event.py b/tests/api_resources/test_event.py index 95de55b..6955fbe 100644 --- a/tests/api_resources/test_event.py +++ b/tests/api_resources/test_event.py @@ -15,13 +15,21 @@ class TestEvent: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip(reason="Prism tests are disabled") + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize def test_method_list(self, client: Opencode) -> None: event_stream = client.event.list() event_stream.response.close() - @pytest.mark.skip(reason="Prism tests are disabled") + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + def test_method_list_with_all_params(self, client: Opencode) -> None: + event_stream = client.event.list( + directory="directory", + ) + event_stream.response.close() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize def test_raw_response_list(self, client: Opencode) -> None: response = client.event.with_raw_response.list() @@ -30,7 +38,7 @@ def test_raw_response_list(self, client: Opencode) -> None: stream = response.parse() stream.close() - @pytest.mark.skip(reason="Prism tests are disabled") + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize def test_streaming_response_list(self, client: Opencode) -> None: with client.event.with_streaming_response.list() as response: @@ -48,13 +56,21 @@ class TestAsyncEvent: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @pytest.mark.skip(reason="Prism tests are disabled") + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize async def test_method_list(self, async_client: AsyncOpencode) -> None: event_stream = await async_client.event.list() await event_stream.response.aclose() - @pytest.mark.skip(reason="Prism tests are disabled") + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None: + event_stream = await async_client.event.list( + directory="directory", + ) + await event_stream.response.aclose() + + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: response = await async_client.event.with_raw_response.list() @@ -63,7 +79,7 @@ async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: stream = await response.parse() await stream.close() - @pytest.mark.skip(reason="Prism tests are disabled") + @pytest.mark.skip(reason="Prism doesn't support text/event-stream responses") @parametrize async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None: async with async_client.event.with_streaming_response.list() as response: diff --git a/tests/api_resources/test_file.py b/tests/api_resources/test_file.py index cd09854..5641905 100644 --- a/tests/api_resources/test_file.py +++ b/tests/api_resources/test_file.py @@ -9,7 +9,11 @@ from opencode_ai import Opencode, AsyncOpencode from tests.utils import assert_matches_type -from opencode_ai.types import FileReadResponse, FileStatusResponse +from opencode_ai.types import ( + FileListResponse, + FileReadResponse, + FileStatusResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -17,6 +21,49 @@ class TestFile: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Opencode) -> None: + file = client.file.list( + path="path", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Opencode) -> None: + file = client.file.list( + path="path", + directory="directory", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Opencode) -> None: + response = client.file.with_raw_response.list( + path="path", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Opencode) -> None: + with client.file.with_streaming_response.list( + path="path", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_read(self, client: Opencode) -> None: @@ -25,6 +72,15 @@ def test_method_read(self, client: Opencode) -> None: ) assert_matches_type(FileReadResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_read_with_all_params(self, client: Opencode) -> None: + file = client.file.read( + path="path", + directory="directory", + ) + assert_matches_type(FileReadResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_read(self, client: Opencode) -> None: @@ -57,6 +113,14 @@ def test_method_status(self, client: Opencode) -> None: file = client.file.status() assert_matches_type(FileStatusResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_status_with_all_params(self, client: Opencode) -> None: + file = client.file.status( + directory="directory", + ) + assert_matches_type(FileStatusResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_status(self, client: Opencode) -> None: @@ -85,6 +149,49 @@ class TestAsyncFile: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncOpencode) -> None: + file = await async_client.file.list( + path="path", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None: + file = await async_client.file.list( + path="path", + directory="directory", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: + response = await async_client.file.with_raw_response.list( + path="path", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None: + async with async_client.file.with_streaming_response.list( + path="path", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_read(self, async_client: AsyncOpencode) -> None: @@ -93,6 +200,15 @@ async def test_method_read(self, async_client: AsyncOpencode) -> None: ) assert_matches_type(FileReadResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_read_with_all_params(self, async_client: AsyncOpencode) -> None: + file = await async_client.file.read( + path="path", + directory="directory", + ) + assert_matches_type(FileReadResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_read(self, async_client: AsyncOpencode) -> None: @@ -125,6 +241,14 @@ async def test_method_status(self, async_client: AsyncOpencode) -> None: file = await async_client.file.status() assert_matches_type(FileStatusResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_status_with_all_params(self, async_client: AsyncOpencode) -> None: + file = await async_client.file.status( + directory="directory", + ) + assert_matches_type(FileStatusResponse, file, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_status(self, async_client: AsyncOpencode) -> None: diff --git a/tests/api_resources/test_find.py b/tests/api_resources/test_find.py index cd54e28..40b1e5d 100644 --- a/tests/api_resources/test_find.py +++ b/tests/api_resources/test_find.py @@ -29,6 +29,15 @@ def test_method_files(self, client: Opencode) -> None: ) assert_matches_type(FindFilesResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_files_with_all_params(self, client: Opencode) -> None: + find = client.find.files( + query="query", + directory="directory", + ) + assert_matches_type(FindFilesResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_files(self, client: Opencode) -> None: @@ -63,6 +72,15 @@ def test_method_symbols(self, client: Opencode) -> None: ) assert_matches_type(FindSymbolsResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_symbols_with_all_params(self, client: Opencode) -> None: + find = client.find.symbols( + query="query", + directory="directory", + ) + assert_matches_type(FindSymbolsResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_symbols(self, client: Opencode) -> None: @@ -97,6 +115,15 @@ def test_method_text(self, client: Opencode) -> None: ) assert_matches_type(FindTextResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_text_with_all_params(self, client: Opencode) -> None: + find = client.find.text( + pattern="pattern", + directory="directory", + ) + assert_matches_type(FindTextResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_text(self, client: Opencode) -> None: @@ -137,6 +164,15 @@ async def test_method_files(self, async_client: AsyncOpencode) -> None: ) assert_matches_type(FindFilesResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_files_with_all_params(self, async_client: AsyncOpencode) -> None: + find = await async_client.find.files( + query="query", + directory="directory", + ) + assert_matches_type(FindFilesResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_files(self, async_client: AsyncOpencode) -> None: @@ -171,6 +207,15 @@ async def test_method_symbols(self, async_client: AsyncOpencode) -> None: ) assert_matches_type(FindSymbolsResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_symbols_with_all_params(self, async_client: AsyncOpencode) -> None: + find = await async_client.find.symbols( + query="query", + directory="directory", + ) + assert_matches_type(FindSymbolsResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_symbols(self, async_client: AsyncOpencode) -> None: @@ -205,6 +250,15 @@ async def test_method_text(self, async_client: AsyncOpencode) -> None: ) assert_matches_type(FindTextResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_text_with_all_params(self, async_client: AsyncOpencode) -> None: + find = await async_client.find.text( + pattern="pattern", + directory="directory", + ) + assert_matches_type(FindTextResponse, find, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_text(self, async_client: AsyncOpencode) -> None: diff --git a/tests/api_resources/test_path.py b/tests/api_resources/test_path.py new file mode 100644 index 0000000..c612834 --- /dev/null +++ b/tests/api_resources/test_path.py @@ -0,0 +1,96 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from opencode_ai import Opencode, AsyncOpencode +from tests.utils import assert_matches_type +from opencode_ai.types import Path + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPath: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get(self, client: Opencode) -> None: + path = client.path.get() + assert_matches_type(Path, path, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_get_with_all_params(self, client: Opencode) -> None: + path = client.path.get( + directory="directory", + ) + assert_matches_type(Path, path, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_get(self, client: Opencode) -> None: + response = client.path.with_raw_response.get() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + path = response.parse() + assert_matches_type(Path, path, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_get(self, client: Opencode) -> None: + with client.path.with_streaming_response.get() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + path = response.parse() + assert_matches_type(Path, path, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncPath: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get(self, async_client: AsyncOpencode) -> None: + path = await async_client.path.get() + assert_matches_type(Path, path, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get_with_all_params(self, async_client: AsyncOpencode) -> None: + path = await async_client.path.get( + directory="directory", + ) + assert_matches_type(Path, path, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_get(self, async_client: AsyncOpencode) -> None: + response = await async_client.path.with_raw_response.get() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + path = await response.parse() + assert_matches_type(Path, path, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_get(self, async_client: AsyncOpencode) -> None: + async with async_client.path.with_streaming_response.get() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + path = await response.parse() + assert_matches_type(Path, path, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_project.py new file mode 100644 index 0000000..b0ea3f4 --- /dev/null +++ b/tests/api_resources/test_project.py @@ -0,0 +1,168 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from opencode_ai import Opencode, AsyncOpencode +from tests.utils import assert_matches_type +from opencode_ai.types import Project, ProjectListResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestProject: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list(self, client: Opencode) -> None: + project = client.project.list() + assert_matches_type(ProjectListResponse, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Opencode) -> None: + project = client.project.list( + directory="directory", + ) + assert_matches_type(ProjectListResponse, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_list(self, client: Opencode) -> None: + response = client.project.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectListResponse, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_list(self, client: Opencode) -> None: + with client.project.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_current(self, client: Opencode) -> None: + project = client.project.current() + assert_matches_type(Project, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_current_with_all_params(self, client: Opencode) -> None: + project = client.project.current( + directory="directory", + ) + assert_matches_type(Project, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_current(self, client: Opencode) -> None: + response = client.project.with_raw_response.current() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_current(self, client: Opencode) -> None: + with client.project.with_streaming_response.current() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncProject: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncOpencode) -> None: + project = await async_client.project.list() + assert_matches_type(ProjectListResponse, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None: + project = await async_client.project.list( + directory="directory", + ) + assert_matches_type(ProjectListResponse, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: + response = await async_client.project.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectListResponse, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None: + async with async_client.project.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_current(self, async_client: AsyncOpencode) -> None: + project = await async_client.project.current() + assert_matches_type(Project, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_current_with_all_params(self, async_client: AsyncOpencode) -> None: + project = await async_client.project.current( + directory="directory", + ) + assert_matches_type(Project, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_current(self, async_client: AsyncOpencode) -> None: + response = await async_client.project.with_raw_response.current() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(Project, project, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_current(self, async_client: AsyncOpencode) -> None: + async with async_client.project.with_streaming_response.current() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(Project, project, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_session.py b/tests/api_resources/test_session.py index 15b39d7..555bb75 100644 --- a/tests/api_resources/test_session.py +++ b/tests/api_resources/test_session.py @@ -10,15 +10,19 @@ from opencode_ai import Opencode, AsyncOpencode from tests.utils import assert_matches_type from opencode_ai.types import ( - Session, AssistantMessage, SessionInitResponse, SessionListResponse, SessionAbortResponse, SessionDeleteResponse, + SessionPromptResponse, + SessionCommandResponse, + SessionMessageResponse, + SessionChildrenResponse, SessionMessagesResponse, SessionSummarizeResponse, ) +from opencode_ai.types.session import Session base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -32,6 +36,16 @@ def test_method_create(self, client: Opencode) -> None: session = client.session.create() assert_matches_type(Session, session, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: Opencode) -> None: + session = client.session.create( + directory="directory", + parent_id="parentID", + title="title", + ) + assert_matches_type(Session, session, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: Opencode) -> None: @@ -54,12 +68,72 @@ def test_streaming_response_create(self, client: Opencode) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update(self, client: Opencode) -> None: + session = client.session.update( + id="id", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: Opencode) -> None: + session = client.session.update( + id="id", + directory="directory", + title="title", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_update(self, client: Opencode) -> None: + response = client.session.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_update(self, client: Opencode) -> None: + with client.session.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(Session, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_update(self, client: Opencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.session.with_raw_response.update( + id="", + ) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: Opencode) -> None: session = client.session.list() assert_matches_type(SessionListResponse, session, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_list_with_all_params(self, client: Opencode) -> None: + session = client.session.list( + directory="directory", + ) + assert_matches_type(SessionListResponse, session, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: Opencode) -> None: @@ -86,7 +160,16 @@ def test_streaming_response_list(self, client: Opencode) -> None: @parametrize def test_method_delete(self, client: Opencode) -> None: session = client.session.delete( - "id", + id="id", + ) + assert_matches_type(SessionDeleteResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_delete_with_all_params(self, client: Opencode) -> None: + session = client.session.delete( + id="id", + directory="directory", ) assert_matches_type(SessionDeleteResponse, session, path=["response"]) @@ -94,7 +177,7 @@ def test_method_delete(self, client: Opencode) -> None: @parametrize def test_raw_response_delete(self, client: Opencode) -> None: response = client.session.with_raw_response.delete( - "id", + id="id", ) assert response.is_closed is True @@ -106,7 +189,7 @@ def test_raw_response_delete(self, client: Opencode) -> None: @parametrize def test_streaming_response_delete(self, client: Opencode) -> None: with client.session.with_streaming_response.delete( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -121,14 +204,23 @@ def test_streaming_response_delete(self, client: Opencode) -> None: def test_path_params_delete(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.session.with_raw_response.delete( - "", + id="", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_abort(self, client: Opencode) -> None: session = client.session.abort( - "id", + id="id", + ) + assert_matches_type(SessionAbortResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_abort_with_all_params(self, client: Opencode) -> None: + session = client.session.abort( + id="id", + directory="directory", ) assert_matches_type(SessionAbortResponse, session, path=["response"]) @@ -136,7 +228,7 @@ def test_method_abort(self, client: Opencode) -> None: @parametrize def test_raw_response_abort(self, client: Opencode) -> None: response = client.session.with_raw_response.abort( - "id", + id="id", ) assert response.is_closed is True @@ -148,7 +240,7 @@ def test_raw_response_abort(self, client: Opencode) -> None: @parametrize def test_streaming_response_abort(self, client: Opencode) -> None: with client.session.with_streaming_response.abort( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -163,408 +255,475 @@ def test_streaming_response_abort(self, client: Opencode) -> None: def test_path_params_abort(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.session.with_raw_response.abort( - "", + id="", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_chat(self, client: Opencode) -> None: - session = client.session.chat( + def test_method_children(self, client: Opencode) -> None: + session = client.session.children( id="id", - model_id="modelID", - parts=[ - { - "text": "text", - "type": "text", - } - ], - provider_id="providerID", ) - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionChildrenResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_chat_with_all_params(self, client: Opencode) -> None: - session = client.session.chat( + def test_method_children_with_all_params(self, client: Opencode) -> None: + session = client.session.children( id="id", - model_id="modelID", - parts=[ - { - "text": "text", - "type": "text", - "id": "id", - "synthetic": True, - "time": { - "start": 0, - "end": 0, - }, - } - ], - provider_id="providerID", - message_id="msg", - mode="mode", - system="system", - tools={"foo": True}, + directory="directory", ) - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionChildrenResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_chat(self, client: Opencode) -> None: - response = client.session.with_raw_response.chat( + def test_raw_response_children(self, client: Opencode) -> None: + response = client.session.with_raw_response.children( id="id", - model_id="modelID", - parts=[ - { - "text": "text", - "type": "text", - } - ], - provider_id="providerID", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionChildrenResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_chat(self, client: Opencode) -> None: - with client.session.with_streaming_response.chat( + def test_streaming_response_children(self, client: Opencode) -> None: + with client.session.with_streaming_response.children( id="id", - model_id="modelID", - parts=[ - { - "text": "text", - "type": "text", - } - ], - provider_id="providerID", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionChildrenResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_chat(self, client: Opencode) -> None: + def test_path_params_children(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.chat( + client.session.with_raw_response.children( id="", - model_id="modelID", - parts=[ - { - "text": "text", - "type": "text", - } - ], - provider_id="providerID", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_init(self, client: Opencode) -> None: - session = client.session.init( + def test_method_command(self, client: Opencode) -> None: + session = client.session.command( id="id", - message_id="messageID", - model_id="modelID", - provider_id="providerID", + arguments="arguments", + command="command", ) - assert_matches_type(SessionInitResponse, session, path=["response"]) + assert_matches_type(SessionCommandResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_init(self, client: Opencode) -> None: - response = client.session.with_raw_response.init( + def test_method_command_with_all_params(self, client: Opencode) -> None: + session = client.session.command( id="id", - message_id="messageID", - model_id="modelID", - provider_id="providerID", + arguments="arguments", + command="command", + directory="directory", + agent="agent", + message_id="msg", + model="model", + ) + assert_matches_type(SessionCommandResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_command(self, client: Opencode) -> None: + response = client.session.with_raw_response.command( + id="id", + arguments="arguments", + command="command", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(SessionInitResponse, session, path=["response"]) + assert_matches_type(SessionCommandResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_init(self, client: Opencode) -> None: - with client.session.with_streaming_response.init( + def test_streaming_response_command(self, client: Opencode) -> None: + with client.session.with_streaming_response.command( id="id", - message_id="messageID", - model_id="modelID", - provider_id="providerID", + arguments="arguments", + command="command", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(SessionInitResponse, session, path=["response"]) + assert_matches_type(SessionCommandResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_init(self, client: Opencode) -> None: + def test_path_params_command(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.init( + client.session.with_raw_response.command( id="", - message_id="messageID", - model_id="modelID", - provider_id="providerID", + arguments="arguments", + command="command", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_messages(self, client: Opencode) -> None: - session = client.session.messages( - "id", + def test_method_get(self, client: Opencode) -> None: + session = client.session.get( + id="id", ) - assert_matches_type(SessionMessagesResponse, session, path=["response"]) + assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_messages(self, client: Opencode) -> None: - response = client.session.with_raw_response.messages( - "id", + def test_method_get_with_all_params(self, client: Opencode) -> None: + session = client.session.get( + id="id", + directory="directory", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_get(self, client: Opencode) -> None: + response = client.session.with_raw_response.get( + id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(SessionMessagesResponse, session, path=["response"]) + assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_messages(self, client: Opencode) -> None: - with client.session.with_streaming_response.messages( - "id", + def test_streaming_response_get(self, client: Opencode) -> None: + with client.session.with_streaming_response.get( + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(SessionMessagesResponse, session, path=["response"]) + assert_matches_type(Session, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_messages(self, client: Opencode) -> None: + def test_path_params_get(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.messages( - "", + client.session.with_raw_response.get( + id="", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_revert(self, client: Opencode) -> None: - session = client.session.revert( + def test_method_init(self, client: Opencode) -> None: + session = client.session.init( id="id", - message_id="msg", + message_id="messageID", + model_id="modelID", + provider_id="providerID", ) - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionInitResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_revert_with_all_params(self, client: Opencode) -> None: - session = client.session.revert( + def test_method_init_with_all_params(self, client: Opencode) -> None: + session = client.session.init( id="id", - message_id="msg", - part_id="prt", + message_id="messageID", + model_id="modelID", + provider_id="providerID", + directory="directory", ) - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionInitResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_revert(self, client: Opencode) -> None: - response = client.session.with_raw_response.revert( + def test_raw_response_init(self, client: Opencode) -> None: + response = client.session.with_raw_response.init( id="id", - message_id="msg", + message_id="messageID", + model_id="modelID", + provider_id="providerID", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionInitResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_revert(self, client: Opencode) -> None: - with client.session.with_streaming_response.revert( + def test_streaming_response_init(self, client: Opencode) -> None: + with client.session.with_streaming_response.init( id="id", - message_id="msg", + message_id="messageID", + model_id="modelID", + provider_id="providerID", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionInitResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_revert(self, client: Opencode) -> None: + def test_path_params_init(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.revert( + client.session.with_raw_response.init( id="", - message_id="msg", + message_id="messageID", + model_id="modelID", + provider_id="providerID", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_share(self, client: Opencode) -> None: - session = client.session.share( - "id", + def test_method_message(self, client: Opencode) -> None: + session = client.session.message( + message_id="messageID", + id="id", ) - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionMessageResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_share(self, client: Opencode) -> None: - response = client.session.with_raw_response.share( - "id", + def test_method_message_with_all_params(self, client: Opencode) -> None: + session = client.session.message( + message_id="messageID", + id="id", + directory="directory", + ) + assert_matches_type(SessionMessageResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_message(self, client: Opencode) -> None: + response = client.session.with_raw_response.message( + message_id="messageID", + id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionMessageResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_share(self, client: Opencode) -> None: - with client.session.with_streaming_response.share( - "id", + def test_streaming_response_message(self, client: Opencode) -> None: + with client.session.with_streaming_response.message( + message_id="messageID", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionMessageResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_share(self, client: Opencode) -> None: + def test_path_params_message(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.share( - "", + client.session.with_raw_response.message( + message_id="messageID", + id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + client.session.with_raw_response.message( + message_id="", + id="id", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_summarize(self, client: Opencode) -> None: - session = client.session.summarize( - id="id", - model_id="modelID", - provider_id="providerID", + def test_method_messages(self, client: Opencode) -> None: + session = client.session.messages( + id="id", ) - assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_summarize(self, client: Opencode) -> None: - response = client.session.with_raw_response.summarize( + def test_method_messages_with_all_params(self, client: Opencode) -> None: + session = client.session.messages( + id="id", + directory="directory", + ) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_messages(self, client: Opencode) -> None: + response = client.session.with_raw_response.messages( id="id", - model_id="modelID", - provider_id="providerID", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_summarize(self, client: Opencode) -> None: - with client.session.with_streaming_response.summarize( + def test_streaming_response_messages(self, client: Opencode) -> None: + with client.session.with_streaming_response.messages( id="id", - model_id="modelID", - provider_id="providerID", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_summarize(self, client: Opencode) -> None: + def test_path_params_messages(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.summarize( + client.session.with_raw_response.messages( id="", - model_id="modelID", - provider_id="providerID", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_unrevert(self, client: Opencode) -> None: - session = client.session.unrevert( - "id", + def test_method_prompt(self, client: Opencode) -> None: + session = client.session.prompt( + id="id", + parts=[ + { + "text": "text", + "type": "text", + } + ], ) - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionPromptResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_unrevert(self, client: Opencode) -> None: - response = client.session.with_raw_response.unrevert( - "id", + def test_method_prompt_with_all_params(self, client: Opencode) -> None: + session = client.session.prompt( + id="id", + parts=[ + { + "text": "text", + "type": "text", + "id": "id", + "synthetic": True, + "time": { + "start": 0, + "end": 0, + }, + } + ], + directory="directory", + agent="agent", + message_id="msg", + model={ + "model_id": "modelID", + "provider_id": "providerID", + }, + system="system", + tools={"foo": True}, + ) + assert_matches_type(SessionPromptResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_prompt(self, client: Opencode) -> None: + response = client.session.with_raw_response.prompt( + id="id", + parts=[ + { + "text": "text", + "type": "text", + } + ], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionPromptResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_unrevert(self, client: Opencode) -> None: - with client.session.with_streaming_response.unrevert( - "id", + def test_streaming_response_prompt(self, client: Opencode) -> None: + with client.session.with_streaming_response.prompt( + id="id", + parts=[ + { + "text": "text", + "type": "text", + } + ], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(SessionPromptResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_unrevert(self, client: Opencode) -> None: + def test_path_params_prompt(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.unrevert( - "", + client.session.with_raw_response.prompt( + id="", + parts=[ + { + "text": "text", + "type": "text", + } + ], ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_method_unshare(self, client: Opencode) -> None: - session = client.session.unshare( - "id", + def test_method_revert(self, client: Opencode) -> None: + session = client.session.revert( + id="id", + message_id="msg", ) assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_raw_response_unshare(self, client: Opencode) -> None: - response = client.session.with_raw_response.unshare( - "id", + def test_method_revert_with_all_params(self, client: Opencode) -> None: + session = client.session.revert( + id="id", + message_id="msg", + directory="directory", + part_id="prt", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_revert(self, client: Opencode) -> None: + response = client.session.with_raw_response.revert( + id="id", + message_id="msg", ) assert response.is_closed is True @@ -574,9 +733,10 @@ def test_raw_response_unshare(self, client: Opencode) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_streaming_response_unshare(self, client: Opencode) -> None: - with client.session.with_streaming_response.unshare( - "id", + def test_streaming_response_revert(self, client: Opencode) -> None: + with client.session.with_streaming_response.revert( + id="id", + message_id="msg", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -588,180 +748,886 @@ def test_streaming_response_unshare(self, client: Opencode) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - def test_path_params_unshare(self, client: Opencode) -> None: + def test_path_params_revert(self, client: Opencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.session.with_raw_response.unshare( - "", + client.session.with_raw_response.revert( + id="", + message_id="msg", ) - -class TestAsyncSession: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_share(self, client: Opencode) -> None: + session = client.session.share( + id="id", + ) + assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_create(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.create() + def test_method_share_with_all_params(self, client: Opencode) -> None: + session = client.session.share( + id="id", + directory="directory", + ) assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_create(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.create() + def test_raw_response_share(self, client: Opencode) -> None: + response = client.session.with_raw_response.share( + id="id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - session = await response.parse() + session = response.parse() assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_create(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.create() as response: + def test_streaming_response_share(self, client: Opencode) -> None: + with client.session.with_streaming_response.share( + id="id", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - session = await response.parse() + session = response.parse() assert_matches_type(Session, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_list(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.list() - assert_matches_type(SessionListResponse, session, path=["response"]) + def test_path_params_share(self, client: Opencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.session.with_raw_response.share( + id="", + ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.list() + def test_method_shell(self, client: Opencode) -> None: + session = client.session.shell( + id="id", + agent="agent", + command="command", + ) + assert_matches_type(AssistantMessage, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_shell_with_all_params(self, client: Opencode) -> None: + session = client.session.shell( + id="id", + agent="agent", + command="command", + directory="directory", + ) + assert_matches_type(AssistantMessage, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_shell(self, client: Opencode) -> None: + response = client.session.with_raw_response.shell( + id="id", + agent="agent", + command="command", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - session = await response.parse() - assert_matches_type(SessionListResponse, session, path=["response"]) + session = response.parse() + assert_matches_type(AssistantMessage, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_shell(self, client: Opencode) -> None: + with client.session.with_streaming_response.shell( + id="id", + agent="agent", + command="command", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(AssistantMessage, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_shell(self, client: Opencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.session.with_raw_response.shell( + id="", + agent="agent", + command="command", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_summarize(self, client: Opencode) -> None: + session = client.session.summarize( + id="id", + model_id="modelID", + provider_id="providerID", + ) + assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_summarize_with_all_params(self, client: Opencode) -> None: + session = client.session.summarize( + id="id", + model_id="modelID", + provider_id="providerID", + directory="directory", + ) + assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_summarize(self, client: Opencode) -> None: + response = client.session.with_raw_response.summarize( + id="id", + model_id="modelID", + provider_id="providerID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_summarize(self, client: Opencode) -> None: + with client.session.with_streaming_response.summarize( + id="id", + model_id="modelID", + provider_id="providerID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_summarize(self, client: Opencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.session.with_raw_response.summarize( + id="", + model_id="modelID", + provider_id="providerID", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_unrevert(self, client: Opencode) -> None: + session = client.session.unrevert( + id="id", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_unrevert_with_all_params(self, client: Opencode) -> None: + session = client.session.unrevert( + id="id", + directory="directory", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_unrevert(self, client: Opencode) -> None: + response = client.session.with_raw_response.unrevert( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_unrevert(self, client: Opencode) -> None: + with client.session.with_streaming_response.unrevert( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(Session, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_unrevert(self, client: Opencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.session.with_raw_response.unrevert( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_unshare(self, client: Opencode) -> None: + session = client.session.unshare( + id="id", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_unshare_with_all_params(self, client: Opencode) -> None: + session = client.session.unshare( + id="id", + directory="directory", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_unshare(self, client: Opencode) -> None: + response = client.session.with_raw_response.unshare( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = response.parse() + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_unshare(self, client: Opencode) -> None: + with client.session.with_streaming_response.unshare( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = response.parse() + assert_matches_type(Session, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_path_params_unshare(self, client: Opencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.session.with_raw_response.unshare( + id="", + ) + + +class TestAsyncSession: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.create() + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.create( + directory="directory", + parent_id="parentID", + title="title", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(Session, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.update( + id="id", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.update( + id="id", + directory="directory", + title="title", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(Session, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.list() + assert_matches_type(SessionListResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.list( + directory="directory", + ) + assert_matches_type(SessionListResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(SessionListResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(SessionListResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.delete( + id="id", + ) + assert_matches_type(SessionDeleteResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_delete_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.delete( + id="id", + directory="directory", + ) + assert_matches_type(SessionDeleteResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.delete( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(SessionDeleteResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.delete( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(SessionDeleteResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.with_raw_response.delete( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_abort(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.abort( + id="id", + ) + assert_matches_type(SessionAbortResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_abort_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.abort( + id="id", + directory="directory", + ) + assert_matches_type(SessionAbortResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_abort(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.abort( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(SessionAbortResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_abort(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.abort( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(SessionAbortResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_abort(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.with_raw_response.abort( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_children(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.children( + id="id", + ) + assert_matches_type(SessionChildrenResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_children_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.children( + id="id", + directory="directory", + ) + assert_matches_type(SessionChildrenResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_children(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.children( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(SessionChildrenResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_children(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.children( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(SessionChildrenResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_children(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.with_raw_response.children( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_command(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.command( + id="id", + arguments="arguments", + command="command", + ) + assert_matches_type(SessionCommandResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_command_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.command( + id="id", + arguments="arguments", + command="command", + directory="directory", + agent="agent", + message_id="msg", + model="model", + ) + assert_matches_type(SessionCommandResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_command(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.command( + id="id", + arguments="arguments", + command="command", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(SessionCommandResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_command(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.command( + id="id", + arguments="arguments", + command="command", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(SessionCommandResponse, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_command(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.with_raw_response.command( + id="", + arguments="arguments", + command="command", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.get( + id="id", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_get_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.get( + id="id", + directory="directory", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_get(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.get( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_get(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.get( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + session = await response.parse() + assert_matches_type(Session, session, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_path_params_get(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.with_raw_response.get( + id="", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_init(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.init( + id="id", + message_id="messageID", + model_id="modelID", + provider_id="providerID", + ) + assert_matches_type(SessionInitResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_init_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.init( + id="id", + message_id="messageID", + model_id="modelID", + provider_id="providerID", + directory="directory", + ) + assert_matches_type(SessionInitResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_init(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.init( + id="id", + message_id="messageID", + model_id="modelID", + provider_id="providerID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + session = await response.parse() + assert_matches_type(SessionInitResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_list(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.list() as response: + async def test_streaming_response_init(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.init( + id="id", + message_id="messageID", + model_id="modelID", + provider_id="providerID", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(SessionListResponse, session, path=["response"]) + assert_matches_type(SessionInitResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_delete(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.delete( - "id", + async def test_path_params_init(self, async_client: AsyncOpencode) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.session.with_raw_response.init( + id="", + message_id="messageID", + model_id="modelID", + provider_id="providerID", + ) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_message(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.message( + message_id="messageID", + id="id", ) - assert_matches_type(SessionDeleteResponse, session, path=["response"]) + assert_matches_type(SessionMessageResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_delete(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.delete( - "id", + async def test_method_message_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.message( + message_id="messageID", + id="id", + directory="directory", + ) + assert_matches_type(SessionMessageResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_message(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.message( + message_id="messageID", + id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(SessionDeleteResponse, session, path=["response"]) + assert_matches_type(SessionMessageResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_delete(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.delete( - "id", + async def test_streaming_response_message(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.message( + message_id="messageID", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(SessionDeleteResponse, session, path=["response"]) + assert_matches_type(SessionMessageResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_path_params_delete(self, async_client: AsyncOpencode) -> None: + async def test_path_params_message(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.session.with_raw_response.delete( - "", + await async_client.session.with_raw_response.message( + message_id="messageID", + id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): + await async_client.session.with_raw_response.message( + message_id="", + id="id", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_abort(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.abort( - "id", + async def test_method_messages(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.messages( + id="id", ) - assert_matches_type(SessionAbortResponse, session, path=["response"]) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_abort(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.abort( - "id", + async def test_method_messages_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.messages( + id="id", + directory="directory", + ) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_messages(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.messages( + id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(SessionAbortResponse, session, path=["response"]) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_abort(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.abort( - "id", + async def test_streaming_response_messages(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.messages( + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(SessionAbortResponse, session, path=["response"]) + assert_matches_type(SessionMessagesResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_path_params_abort(self, async_client: AsyncOpencode) -> None: + async def test_path_params_messages(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.session.with_raw_response.abort( - "", + await async_client.session.with_raw_response.messages( + id="", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_chat(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.chat( + async def test_method_prompt(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.prompt( id="id", - model_id="modelID", parts=[ { "text": "text", "type": "text", } ], - provider_id="providerID", ) - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionPromptResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_chat_with_all_params(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.chat( + async def test_method_prompt_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.prompt( id="id", - model_id="modelID", parts=[ { "text": "text", @@ -774,193 +1640,149 @@ async def test_method_chat_with_all_params(self, async_client: AsyncOpencode) -> }, } ], - provider_id="providerID", + directory="directory", + agent="agent", message_id="msg", - mode="mode", + model={ + "model_id": "modelID", + "provider_id": "providerID", + }, system="system", tools={"foo": True}, ) - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionPromptResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_chat(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.chat( + async def test_raw_response_prompt(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.prompt( id="id", - model_id="modelID", parts=[ { "text": "text", "type": "text", } ], - provider_id="providerID", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionPromptResponse, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_chat(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.chat( + async def test_streaming_response_prompt(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.prompt( id="id", - model_id="modelID", parts=[ { "text": "text", "type": "text", } ], - provider_id="providerID", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(AssistantMessage, session, path=["response"]) + assert_matches_type(SessionPromptResponse, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_path_params_chat(self, async_client: AsyncOpencode) -> None: + async def test_path_params_prompt(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.session.with_raw_response.chat( + await async_client.session.with_raw_response.prompt( id="", - model_id="modelID", parts=[ { "text": "text", "type": "text", } ], - provider_id="providerID", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_init(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.init( + async def test_method_revert(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.revert( id="id", - message_id="messageID", - model_id="modelID", - provider_id="providerID", + message_id="msg", ) - assert_matches_type(SessionInitResponse, session, path=["response"]) + assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_init(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.init( + async def test_method_revert_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.revert( id="id", - message_id="messageID", - model_id="modelID", - provider_id="providerID", + message_id="msg", + directory="directory", + part_id="prt", ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - session = await response.parse() - assert_matches_type(SessionInitResponse, session, path=["response"]) + assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_init(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.init( + async def test_raw_response_revert(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.revert( id="id", - message_id="messageID", - model_id="modelID", - provider_id="providerID", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - session = await response.parse() - assert_matches_type(SessionInitResponse, session, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_path_params_init(self, async_client: AsyncOpencode) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.session.with_raw_response.init( - id="", - message_id="messageID", - model_id="modelID", - provider_id="providerID", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_messages(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.messages( - "id", - ) - assert_matches_type(SessionMessagesResponse, session, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_messages(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.messages( - "id", + message_id="msg", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(SessionMessagesResponse, session, path=["response"]) + assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_messages(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.messages( - "id", + async def test_streaming_response_revert(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.revert( + id="id", + message_id="msg", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(SessionMessagesResponse, session, path=["response"]) + assert_matches_type(Session, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_path_params_messages(self, async_client: AsyncOpencode) -> None: + async def test_path_params_revert(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.session.with_raw_response.messages( - "", + await async_client.session.with_raw_response.revert( + id="", + message_id="msg", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_revert(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.revert( + async def test_method_share(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.share( id="id", - message_id="msg", ) assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_revert_with_all_params(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.revert( + async def test_method_share_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.share( id="id", - message_id="msg", - part_id="prt", + directory="directory", ) assert_matches_type(Session, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_revert(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.revert( + async def test_raw_response_share(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.share( id="id", - message_id="msg", ) assert response.is_closed is True @@ -970,10 +1792,9 @@ async def test_raw_response_revert(self, async_client: AsyncOpencode) -> None: @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_revert(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.revert( + async def test_streaming_response_share(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.share( id="id", - message_id="msg", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -985,53 +1806,71 @@ async def test_streaming_response_revert(self, async_client: AsyncOpencode) -> N @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_path_params_revert(self, async_client: AsyncOpencode) -> None: + async def test_path_params_share(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.session.with_raw_response.revert( + await async_client.session.with_raw_response.share( id="", - message_id="msg", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_method_share(self, async_client: AsyncOpencode) -> None: - session = await async_client.session.share( - "id", + async def test_method_shell(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.shell( + id="id", + agent="agent", + command="command", ) - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(AssistantMessage, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_raw_response_share(self, async_client: AsyncOpencode) -> None: - response = await async_client.session.with_raw_response.share( - "id", + async def test_method_shell_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.shell( + id="id", + agent="agent", + command="command", + directory="directory", + ) + assert_matches_type(AssistantMessage, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_shell(self, async_client: AsyncOpencode) -> None: + response = await async_client.session.with_raw_response.shell( + id="id", + agent="agent", + command="command", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(AssistantMessage, session, path=["response"]) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_streaming_response_share(self, async_client: AsyncOpencode) -> None: - async with async_client.session.with_streaming_response.share( - "id", + async def test_streaming_response_shell(self, async_client: AsyncOpencode) -> None: + async with async_client.session.with_streaming_response.shell( + id="id", + agent="agent", + command="command", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() - assert_matches_type(Session, session, path=["response"]) + assert_matches_type(AssistantMessage, session, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="Prism tests are disabled") @parametrize - async def test_path_params_share(self, async_client: AsyncOpencode) -> None: + async def test_path_params_shell(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.session.with_raw_response.share( - "", + await async_client.session.with_raw_response.shell( + id="", + agent="agent", + command="command", ) @pytest.mark.skip(reason="Prism tests are disabled") @@ -1044,6 +1883,17 @@ async def test_method_summarize(self, async_client: AsyncOpencode) -> None: ) assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_summarize_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.summarize( + id="id", + model_id="modelID", + provider_id="providerID", + directory="directory", + ) + assert_matches_type(SessionSummarizeResponse, session, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_summarize(self, async_client: AsyncOpencode) -> None: @@ -1088,7 +1938,16 @@ async def test_path_params_summarize(self, async_client: AsyncOpencode) -> None: @parametrize async def test_method_unrevert(self, async_client: AsyncOpencode) -> None: session = await async_client.session.unrevert( - "id", + id="id", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_unrevert_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.unrevert( + id="id", + directory="directory", ) assert_matches_type(Session, session, path=["response"]) @@ -1096,7 +1955,7 @@ async def test_method_unrevert(self, async_client: AsyncOpencode) -> None: @parametrize async def test_raw_response_unrevert(self, async_client: AsyncOpencode) -> None: response = await async_client.session.with_raw_response.unrevert( - "id", + id="id", ) assert response.is_closed is True @@ -1108,7 +1967,7 @@ async def test_raw_response_unrevert(self, async_client: AsyncOpencode) -> None: @parametrize async def test_streaming_response_unrevert(self, async_client: AsyncOpencode) -> None: async with async_client.session.with_streaming_response.unrevert( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1123,14 +1982,23 @@ async def test_streaming_response_unrevert(self, async_client: AsyncOpencode) -> async def test_path_params_unrevert(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.session.with_raw_response.unrevert( - "", + id="", ) @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_unshare(self, async_client: AsyncOpencode) -> None: session = await async_client.session.unshare( - "id", + id="id", + ) + assert_matches_type(Session, session, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_unshare_with_all_params(self, async_client: AsyncOpencode) -> None: + session = await async_client.session.unshare( + id="id", + directory="directory", ) assert_matches_type(Session, session, path=["response"]) @@ -1138,7 +2006,7 @@ async def test_method_unshare(self, async_client: AsyncOpencode) -> None: @parametrize async def test_raw_response_unshare(self, async_client: AsyncOpencode) -> None: response = await async_client.session.with_raw_response.unshare( - "id", + id="id", ) assert response.is_closed is True @@ -1150,7 +2018,7 @@ async def test_raw_response_unshare(self, async_client: AsyncOpencode) -> None: @parametrize async def test_streaming_response_unshare(self, async_client: AsyncOpencode) -> None: async with async_client.session.with_streaming_response.unshare( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1165,5 +2033,5 @@ async def test_streaming_response_unshare(self, async_client: AsyncOpencode) -> async def test_path_params_unshare(self, async_client: AsyncOpencode) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.session.with_raw_response.unshare( - "", + id="", ) diff --git a/tests/api_resources/test_tui.py b/tests/api_resources/test_tui.py index 06145a5..20ee299 100644 --- a/tests/api_resources/test_tui.py +++ b/tests/api_resources/test_tui.py @@ -9,7 +9,17 @@ from opencode_ai import Opencode, AsyncOpencode from tests.utils import assert_matches_type -from opencode_ai.types import TuiOpenHelpResponse, TuiAppendPromptResponse +from opencode_ai.types import ( + TuiOpenHelpResponse, + TuiShowToastResponse, + TuiOpenModelsResponse, + TuiOpenThemesResponse, + TuiClearPromptResponse, + TuiAppendPromptResponse, + TuiOpenSessionsResponse, + TuiSubmitPromptResponse, + TuiExecuteCommandResponse, +) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -25,6 +35,15 @@ def test_method_append_prompt(self, client: Opencode) -> None: ) assert_matches_type(TuiAppendPromptResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_append_prompt_with_all_params(self, client: Opencode) -> None: + tui = client.tui.append_prompt( + text="text", + directory="directory", + ) + assert_matches_type(TuiAppendPromptResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_append_prompt(self, client: Opencode) -> None: @@ -51,12 +70,99 @@ def test_streaming_response_append_prompt(self, client: Opencode) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_clear_prompt(self, client: Opencode) -> None: + tui = client.tui.clear_prompt() + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_clear_prompt_with_all_params(self, client: Opencode) -> None: + tui = client.tui.clear_prompt( + directory="directory", + ) + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_clear_prompt(self, client: Opencode) -> None: + response = client.tui.with_raw_response.clear_prompt() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = response.parse() + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_clear_prompt(self, client: Opencode) -> None: + with client.tui.with_streaming_response.clear_prompt() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = response.parse() + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_execute_command(self, client: Opencode) -> None: + tui = client.tui.execute_command( + command="command", + ) + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_execute_command_with_all_params(self, client: Opencode) -> None: + tui = client.tui.execute_command( + command="command", + directory="directory", + ) + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_execute_command(self, client: Opencode) -> None: + response = client.tui.with_raw_response.execute_command( + command="command", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = response.parse() + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_execute_command(self, client: Opencode) -> None: + with client.tui.with_streaming_response.execute_command( + command="command", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = response.parse() + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_open_help(self, client: Opencode) -> None: tui = client.tui.open_help() assert_matches_type(TuiOpenHelpResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_open_help_with_all_params(self, client: Opencode) -> None: + tui = client.tui.open_help( + directory="directory", + ) + assert_matches_type(TuiOpenHelpResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_open_help(self, client: Opencode) -> None: @@ -79,6 +185,198 @@ def test_streaming_response_open_help(self, client: Opencode) -> None: assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_open_models(self, client: Opencode) -> None: + tui = client.tui.open_models() + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_open_models_with_all_params(self, client: Opencode) -> None: + tui = client.tui.open_models( + directory="directory", + ) + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_open_models(self, client: Opencode) -> None: + response = client.tui.with_raw_response.open_models() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = response.parse() + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_open_models(self, client: Opencode) -> None: + with client.tui.with_streaming_response.open_models() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = response.parse() + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_open_sessions(self, client: Opencode) -> None: + tui = client.tui.open_sessions() + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_open_sessions_with_all_params(self, client: Opencode) -> None: + tui = client.tui.open_sessions( + directory="directory", + ) + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_open_sessions(self, client: Opencode) -> None: + response = client.tui.with_raw_response.open_sessions() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = response.parse() + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_open_sessions(self, client: Opencode) -> None: + with client.tui.with_streaming_response.open_sessions() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = response.parse() + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_open_themes(self, client: Opencode) -> None: + tui = client.tui.open_themes() + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_open_themes_with_all_params(self, client: Opencode) -> None: + tui = client.tui.open_themes( + directory="directory", + ) + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_open_themes(self, client: Opencode) -> None: + response = client.tui.with_raw_response.open_themes() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = response.parse() + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_open_themes(self, client: Opencode) -> None: + with client.tui.with_streaming_response.open_themes() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = response.parse() + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_show_toast(self, client: Opencode) -> None: + tui = client.tui.show_toast( + message="message", + variant="info", + ) + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_show_toast_with_all_params(self, client: Opencode) -> None: + tui = client.tui.show_toast( + message="message", + variant="info", + directory="directory", + title="title", + ) + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_show_toast(self, client: Opencode) -> None: + response = client.tui.with_raw_response.show_toast( + message="message", + variant="info", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = response.parse() + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_show_toast(self, client: Opencode) -> None: + with client.tui.with_streaming_response.show_toast( + message="message", + variant="info", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = response.parse() + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_submit_prompt(self, client: Opencode) -> None: + tui = client.tui.submit_prompt() + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_method_submit_prompt_with_all_params(self, client: Opencode) -> None: + tui = client.tui.submit_prompt( + directory="directory", + ) + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_raw_response_submit_prompt(self, client: Opencode) -> None: + response = client.tui.with_raw_response.submit_prompt() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = response.parse() + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + def test_streaming_response_submit_prompt(self, client: Opencode) -> None: + with client.tui.with_streaming_response.submit_prompt() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = response.parse() + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + class TestAsyncTui: parametrize = pytest.mark.parametrize( @@ -93,6 +391,15 @@ async def test_method_append_prompt(self, async_client: AsyncOpencode) -> None: ) assert_matches_type(TuiAppendPromptResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_append_prompt_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.append_prompt( + text="text", + directory="directory", + ) + assert_matches_type(TuiAppendPromptResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_append_prompt(self, async_client: AsyncOpencode) -> None: @@ -119,12 +426,99 @@ async def test_streaming_response_append_prompt(self, async_client: AsyncOpencod assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_clear_prompt(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.clear_prompt() + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_clear_prompt_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.clear_prompt( + directory="directory", + ) + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_clear_prompt(self, async_client: AsyncOpencode) -> None: + response = await async_client.tui.with_raw_response.clear_prompt() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = await response.parse() + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_clear_prompt(self, async_client: AsyncOpencode) -> None: + async with async_client.tui.with_streaming_response.clear_prompt() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = await response.parse() + assert_matches_type(TuiClearPromptResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_execute_command(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.execute_command( + command="command", + ) + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_execute_command_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.execute_command( + command="command", + directory="directory", + ) + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_execute_command(self, async_client: AsyncOpencode) -> None: + response = await async_client.tui.with_raw_response.execute_command( + command="command", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = await response.parse() + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_execute_command(self, async_client: AsyncOpencode) -> None: + async with async_client.tui.with_streaming_response.execute_command( + command="command", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = await response.parse() + assert_matches_type(TuiExecuteCommandResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_open_help(self, async_client: AsyncOpencode) -> None: tui = await async_client.tui.open_help() assert_matches_type(TuiOpenHelpResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_open_help_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.open_help( + directory="directory", + ) + assert_matches_type(TuiOpenHelpResponse, tui, path=["response"]) + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_open_help(self, async_client: AsyncOpencode) -> None: @@ -146,3 +540,195 @@ async def test_streaming_response_open_help(self, async_client: AsyncOpencode) - assert_matches_type(TuiOpenHelpResponse, tui, path=["response"]) assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_open_models(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.open_models() + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_open_models_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.open_models( + directory="directory", + ) + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_open_models(self, async_client: AsyncOpencode) -> None: + response = await async_client.tui.with_raw_response.open_models() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = await response.parse() + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_open_models(self, async_client: AsyncOpencode) -> None: + async with async_client.tui.with_streaming_response.open_models() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = await response.parse() + assert_matches_type(TuiOpenModelsResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_open_sessions(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.open_sessions() + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_open_sessions_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.open_sessions( + directory="directory", + ) + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_open_sessions(self, async_client: AsyncOpencode) -> None: + response = await async_client.tui.with_raw_response.open_sessions() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = await response.parse() + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_open_sessions(self, async_client: AsyncOpencode) -> None: + async with async_client.tui.with_streaming_response.open_sessions() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = await response.parse() + assert_matches_type(TuiOpenSessionsResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_open_themes(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.open_themes() + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_open_themes_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.open_themes( + directory="directory", + ) + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_open_themes(self, async_client: AsyncOpencode) -> None: + response = await async_client.tui.with_raw_response.open_themes() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = await response.parse() + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_open_themes(self, async_client: AsyncOpencode) -> None: + async with async_client.tui.with_streaming_response.open_themes() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = await response.parse() + assert_matches_type(TuiOpenThemesResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_show_toast(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.show_toast( + message="message", + variant="info", + ) + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_show_toast_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.show_toast( + message="message", + variant="info", + directory="directory", + title="title", + ) + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_show_toast(self, async_client: AsyncOpencode) -> None: + response = await async_client.tui.with_raw_response.show_toast( + message="message", + variant="info", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = await response.parse() + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_show_toast(self, async_client: AsyncOpencode) -> None: + async with async_client.tui.with_streaming_response.show_toast( + message="message", + variant="info", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = await response.parse() + assert_matches_type(TuiShowToastResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_submit_prompt(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.submit_prompt() + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_method_submit_prompt_with_all_params(self, async_client: AsyncOpencode) -> None: + tui = await async_client.tui.submit_prompt( + directory="directory", + ) + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_raw_response_submit_prompt(self, async_client: AsyncOpencode) -> None: + response = await async_client.tui.with_raw_response.submit_prompt() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + tui = await response.parse() + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + @pytest.mark.skip(reason="Prism tests are disabled") + @parametrize + async def test_streaming_response_submit_prompt(self, async_client: AsyncOpencode) -> None: + async with async_client.tui.with_streaming_response.submit_prompt() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + tui = await response.parse() + assert_matches_type(TuiSubmitPromptResponse, tui, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/test_client.py b/tests/test_client.py index f8225c7..7f7092c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,13 +6,10 @@ import os import sys import json -import time import asyncio import inspect -import subprocess import tracemalloc from typing import Any, Union, cast -from textwrap import dedent from unittest import mock from typing_extensions import Literal @@ -23,6 +20,7 @@ from opencode_ai import Opencode, AsyncOpencode, APIResponseValidationError from opencode_ai._types import Omit +from opencode_ai._utils import asyncify from opencode_ai._models import BaseModel, FinalRequestOptions from opencode_ai._streaming import Stream, AsyncStream from opencode_ai._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError @@ -30,8 +28,10 @@ DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, + OtherPlatform, DefaultHttpxClient, DefaultAsyncHttpxClient, + get_platform, make_request_options, ) @@ -59,47 +59,45 @@ def _get_open_connections(client: Opencode | AsyncOpencode) -> int: class TestOpencode: - client = Opencode(base_url=base_url, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - def test_raw_response(self, respx_mock: MockRouter) -> None: + def test_raw_response(self, respx_mock: MockRouter, client: Opencode) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Opencode) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, client: Opencode) -> None: + copied = client.copy() + assert id(copied) != id(client) - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, client: Opencode) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = Opencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"}) @@ -132,6 +130,7 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() def test_copy_default_query(self) -> None: client = Opencode(base_url=base_url, _strict_response_validation=True, default_query={"foo": "bar"}) @@ -167,13 +166,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + client.close() + + def test_copy_signature(self, client: Opencode) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -184,12 +185,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, client: Opencode) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -246,14 +247,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + def test_request_timeout(self, client: Opencode) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( - FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) - ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) @@ -264,6 +263,8 @@ def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + client.close() + def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: @@ -273,6 +274,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + client.close() + # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Opencode(base_url=base_url, _strict_response_validation=True, http_client=http_client) @@ -281,6 +284,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + client.close() + # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Opencode(base_url=base_url, _strict_response_validation=True, http_client=http_client) @@ -289,18 +294,20 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + client.close() + async def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): async with httpx.AsyncClient() as http_client: Opencode(base_url=base_url, _strict_response_validation=True, http_client=cast(Any, http_client)) def test_default_headers_option(self) -> None: - client = Opencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"}) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + test_client = Opencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"}) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = Opencode( + test_client2 = Opencode( base_url=base_url, _strict_response_validation=True, default_headers={ @@ -308,10 +315,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + test_client.close() + test_client2.close() + def test_default_query_option(self) -> None: client = Opencode(base_url=base_url, _strict_response_validation=True, default_query={"query_param": "bar"}) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -328,8 +338,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + client.close() + + def test_request_extra_json(self, client: Opencode) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -340,7 +352,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -351,7 +363,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -362,8 +374,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Opencode) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -373,7 +385,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -384,8 +396,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Opencode) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -398,7 +410,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -412,7 +424,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -455,7 +467,7 @@ def test_multipart_repeating_array(self, client: Opencode) -> None: ] @pytest.mark.respx(base_url=base_url) - def test_basic_union_response(self, respx_mock: MockRouter) -> None: + def test_basic_union_response(self, respx_mock: MockRouter, client: Opencode) -> None: class Model1(BaseModel): name: str @@ -464,12 +476,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + def test_union_response_different_types(self, respx_mock: MockRouter, client: Opencode) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -480,18 +492,18 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Opencode) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -507,7 +519,7 @@ class Model(BaseModel): ) ) - response = self.client.get("/foo", cast_to=Model) + response = client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 @@ -519,6 +531,8 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" + client.close() + def test_base_url_env(self) -> None: with update_env(OPENCODE_BASE_URL="http://localhost:5000/from/env"): client = Opencode(_strict_response_validation=True) @@ -545,6 +559,7 @@ def test_base_url_trailing_slash(self, client: Opencode) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -567,6 +582,7 @@ def test_base_url_no_trailing_slash(self, client: Opencode) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -589,35 +605,36 @@ def test_absolute_request_url(self, client: Opencode) -> None: ), ) assert request.url == "https://myapi.com/foo" + client.close() def test_copied_client_does_not_close_http(self) -> None: - client = Opencode(base_url=base_url, _strict_response_validation=True) - assert not client.is_closed() + test_client = Opencode(base_url=base_url, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied - assert not client.is_closed() + assert not test_client.is_closed() def test_client_context_manager(self) -> None: - client = Opencode(base_url=base_url, _strict_response_validation=True) - with client as c2: - assert c2 is client + test_client = Opencode(base_url=base_url, _strict_response_validation=True) + with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + def test_client_response_validation_error(self, respx_mock: MockRouter, client: Opencode) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - self.client.get("/foo", cast_to=Model) + client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -626,13 +643,13 @@ def test_client_max_retries_validation(self) -> None: Opencode(base_url=base_url, _strict_response_validation=True, max_retries=cast(Any, None)) @pytest.mark.respx(base_url=base_url) - def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + def test_default_stream_cls(self, respx_mock: MockRouter, client: Opencode) -> None: class Model(BaseModel): name: str respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - stream = self.client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) + stream = client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) assert isinstance(stream, Stream) stream.response.close() @@ -648,11 +665,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - client = Opencode(base_url=base_url, _strict_response_validation=False) + non_strict_client = Opencode(base_url=base_url, _strict_response_validation=False) - response = client.get("/foo", cast_to=Model) + response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + strict_client.close() + non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -675,9 +695,9 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = Opencode(base_url=base_url, _strict_response_validation=True) - + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: Opencode + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) @@ -691,7 +711,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien with pytest.raises(APITimeoutError): client.session.with_streaming_response.list().__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -700,7 +720,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client with pytest.raises(APIStatusError): client.session.with_streaming_response.list().__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -802,79 +822,73 @@ def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - def test_follow_redirects(self, respx_mock: MockRouter) -> None: + def test_follow_redirects(self, respx_mock: MockRouter, client: Opencode) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Opencode) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - self.client.post( - "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response - ) + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" class TestAsyncOpencode: - client = AsyncOpencode(base_url=base_url, _strict_response_validation=True) - @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response(self, respx_mock: MockRouter) -> None: + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, async_client: AsyncOpencode) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, async_client: AsyncOpencode) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = async_client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert async_client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(async_client.timeout, httpx.Timeout) - def test_copy_default_headers(self) -> None: + async def test_copy_default_headers(self) -> None: client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"}) assert client.default_headers["X-Foo"] == "bar" @@ -905,8 +919,9 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() - def test_copy_default_query(self) -> None: + async def test_copy_default_query(self) -> None: client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, default_query={"foo": "bar"}) assert _get_params(client)["foo"] == "bar" @@ -940,13 +955,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + await client.close() + + def test_copy_signature(self, async_client: AsyncOpencode) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + async_client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(async_client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -957,12 +974,12 @@ def test_copy_signature(self) -> None: assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") - def test_copy_build_request(self) -> None: + def test_copy_build_request(self, async_client: AsyncOpencode) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = async_client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -1019,12 +1036,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - async def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + async def test_request_timeout(self, async_client: AsyncOpencode) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( + request = async_client._build_request( FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) ) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -1037,6 +1054,8 @@ async def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + await client.close() + async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: @@ -1046,6 +1065,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + await client.close() + # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, http_client=http_client) @@ -1054,6 +1075,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + await client.close() + # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, http_client=http_client) @@ -1062,18 +1085,22 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + await client.close() + def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): with httpx.Client() as http_client: AsyncOpencode(base_url=base_url, _strict_response_validation=True, http_client=cast(Any, http_client)) - def test_default_headers_option(self) -> None: - client = AsyncOpencode(base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"}) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + async def test_default_headers_option(self) -> None: + test_client = AsyncOpencode( + base_url=base_url, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = AsyncOpencode( + test_client2 = AsyncOpencode( base_url=base_url, _strict_response_validation=True, default_headers={ @@ -1081,11 +1108,14 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" - def test_default_query_option(self) -> None: + await test_client.close() + await test_client2.close() + + async def test_default_query_option(self) -> None: client = AsyncOpencode( base_url=base_url, _strict_response_validation=True, default_query={"query_param": "bar"} ) @@ -1103,8 +1133,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + await client.close() + + def test_request_extra_json(self, client: Opencode) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1115,7 +1147,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1126,7 +1158,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1137,8 +1169,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: Opencode) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1148,7 +1180,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1159,8 +1191,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: Opencode) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1173,7 +1205,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1187,7 +1219,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1230,7 +1262,7 @@ def test_multipart_repeating_array(self, async_client: AsyncOpencode) -> None: ] @pytest.mark.respx(base_url=base_url) - async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: class Model1(BaseModel): name: str @@ -1239,12 +1271,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -1255,18 +1287,20 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncOpencode + ) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -1282,11 +1316,11 @@ class Model(BaseModel): ) ) - response = await self.client.get("/foo", cast_to=Model) + response = await async_client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 - def test_base_url_setter(self) -> None: + async def test_base_url_setter(self) -> None: client = AsyncOpencode(base_url="https://example.com/from_init", _strict_response_validation=True) assert client.base_url == "https://example.com/from_init/" @@ -1294,7 +1328,9 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" - def test_base_url_env(self) -> None: + await client.close() + + async def test_base_url_env(self) -> None: with update_env(OPENCODE_BASE_URL="http://localhost:5000/from/env"): client = AsyncOpencode(_strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @@ -1311,7 +1347,7 @@ def test_base_url_env(self) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None: + async def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1320,6 +1356,7 @@ def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1333,7 +1370,7 @@ def test_base_url_trailing_slash(self, client: AsyncOpencode) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None: + async def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1342,6 +1379,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1355,7 +1393,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncOpencode) -> None: ], ids=["standard", "custom http client"], ) - def test_absolute_request_url(self, client: AsyncOpencode) -> None: + async def test_absolute_request_url(self, client: AsyncOpencode) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1364,37 +1402,37 @@ def test_absolute_request_url(self, client: AsyncOpencode) -> None: ), ) assert request.url == "https://myapi.com/foo" + await client.close() async def test_copied_client_does_not_close_http(self) -> None: - client = AsyncOpencode(base_url=base_url, _strict_response_validation=True) - assert not client.is_closed() + test_client = AsyncOpencode(base_url=base_url, _strict_response_validation=True) + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied await asyncio.sleep(0.2) - assert not client.is_closed() + assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - client = AsyncOpencode(base_url=base_url, _strict_response_validation=True) - async with client as c2: - assert c2 is client + test_client = AsyncOpencode(base_url=base_url, _strict_response_validation=True) + async with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - await self.client.get("/foo", cast_to=Model) + await async_client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -1403,19 +1441,17 @@ async def test_client_max_retries_validation(self) -> None: AsyncOpencode(base_url=base_url, _strict_response_validation=True, max_retries=cast(Any, None)) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_default_stream_cls(self, respx_mock: MockRouter) -> None: + async def test_default_stream_cls(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: class Model(BaseModel): name: str respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - stream = await self.client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) + stream = await async_client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) assert isinstance(stream, AsyncStream) await stream.response.aclose() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str @@ -1427,11 +1463,14 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - client = AsyncOpencode(base_url=base_url, _strict_response_validation=False) + non_strict_client = AsyncOpencode(base_url=base_url, _strict_response_validation=False) - response = await client.get("/foo", cast_to=Model) + response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + await strict_client.close() + await non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -1454,13 +1493,12 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - @pytest.mark.asyncio - async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = AsyncOpencode(base_url=base_url, _strict_response_validation=True) - + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncOpencode + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -1473,7 +1511,7 @@ async def test_retrying_timeout_errors_doesnt_leak( with pytest.raises(APITimeoutError): await async_client.session.with_streaming_response.list().__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -1484,12 +1522,11 @@ async def test_retrying_status_errors_doesnt_leak( with pytest.raises(APIStatusError): await async_client.session.with_streaming_response.list().__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( self, @@ -1521,7 +1558,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_omit_retry_count_header( self, async_client: AsyncOpencode, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1545,7 +1581,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("opencode_ai._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_overwrite_retry_count_header( self, async_client: AsyncOpencode, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1566,50 +1601,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.http_request.headers.get("x-stainless-retry-count") == "42" - def test_get_platform(self) -> None: - # A previous implementation of asyncify could leave threads unterminated when - # used with nest_asyncio. - # - # Since nest_asyncio.apply() is global and cannot be un-applied, this - # test is run in a separate process to avoid affecting other tests. - test_code = dedent(""" - import asyncio - import nest_asyncio - import threading - - from opencode_ai._utils import asyncify - from opencode_ai._base_client import get_platform - - async def test_main() -> None: - result = await asyncify(get_platform)() - print(result) - for thread in threading.enumerate(): - print(thread.name) - - nest_asyncio.apply() - asyncio.run(test_main()) - """) - with subprocess.Popen( - [sys.executable, "-c", test_code], - text=True, - ) as process: - timeout = 10 # seconds - - start_time = time.monotonic() - while True: - return_code = process.poll() - if return_code is not None: - if return_code != 0: - raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") - - # success - break - - if time.monotonic() - start_time > timeout: - process.kill() - raise AssertionError("calling get_platform using asyncify resulted in a hung process") - - time.sleep(0.1) + async def test_get_platform(self) -> None: + platform = await asyncify(get_platform)() + assert isinstance(platform, (str, OtherPlatform)) async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly @@ -1634,26 +1628,26 @@ async def test_default_client_creation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) - response = await self.client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - async def test_follow_redirects_disabled(self, respx_mock: MockRouter) -> None: + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncOpencode) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: - await self.client.post( + await async_client.post( "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response ) diff --git a/tests/test_models.py b/tests/test_models.py index 94fa22d..f5c177f 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -8,8 +8,8 @@ from pydantic import Field from opencode_ai._utils import PropertyInfo -from opencode_ai._compat import PYDANTIC_V2, parse_obj, model_dump, model_json -from opencode_ai._models import BaseModel, construct_type +from opencode_ai._compat import PYDANTIC_V1, parse_obj, model_dump, model_json +from opencode_ai._models import DISCRIMINATOR_CACHE, BaseModel, construct_type class BasicModel(BaseModel): @@ -294,12 +294,12 @@ class Model(BaseModel): assert cast(bool, m.foo) is True m = Model.construct(foo={"name": 3}) - if PYDANTIC_V2: - assert isinstance(m.foo, Submodel1) - assert m.foo.name == 3 # type: ignore - else: + if PYDANTIC_V1: assert isinstance(m.foo, Submodel2) assert m.foo.name == "3" + else: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore def test_list_of_unions() -> None: @@ -426,10 +426,10 @@ class Model(BaseModel): expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) - if PYDANTIC_V2: - expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' - else: + if PYDANTIC_V1: expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + else: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' model = Model.construct(created_at="2019-12-27T18:11:19.117Z") assert model.created_at == expected @@ -531,7 +531,7 @@ class Model2(BaseModel): assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} assert m4.to_dict(mode="json") == {"created_at": time_str} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_dict(warnings=False) @@ -556,7 +556,7 @@ class Model(BaseModel): assert m3.model_dump() == {"foo": None} assert m3.model_dump(exclude_none=True) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump(round_trip=True) @@ -580,10 +580,10 @@ class Model(BaseModel): assert json.loads(m.to_json()) == {"FOO": "hello"} assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} - if PYDANTIC_V2: - assert m.to_json(indent=None) == '{"FOO":"hello"}' - else: + if PYDANTIC_V1: assert m.to_json(indent=None) == '{"FOO": "hello"}' + else: + assert m.to_json(indent=None) == '{"FOO":"hello"}' m2 = Model() assert json.loads(m2.to_json()) == {} @@ -595,7 +595,7 @@ class Model(BaseModel): assert json.loads(m3.to_json()) == {"FOO": None} assert json.loads(m3.to_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_json(warnings=False) @@ -622,7 +622,7 @@ class Model(BaseModel): assert json.loads(m3.model_dump_json()) == {"foo": None} assert json.loads(m3.model_dump_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump_json(round_trip=True) @@ -679,12 +679,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_unknown_variant() -> None: @@ -768,12 +768,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.foo_type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: @@ -809,7 +809,7 @@ class B(BaseModel): UnionType = cast(Any, Union[A, B]) - assert not hasattr(UnionType, "__discriminator__") + assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) @@ -818,7 +818,7 @@ class B(BaseModel): assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] - discriminator = UnionType.__discriminator__ + discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( @@ -830,10 +830,10 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache - assert UnionType.__discriminator__ is discriminator + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_type_alias_type() -> None: Alias = TypeAliasType("Alias", str) # pyright: ignore @@ -849,7 +849,7 @@ class Model(BaseModel): assert m.union == "bar" -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_field_named_cls() -> None: class Model(BaseModel): cls: str @@ -936,7 +936,7 @@ class Type2(BaseModel): assert isinstance(model.value, InnerType2) -@pytest.mark.skipif(not PYDANTIC_V2, reason="this is only supported in pydantic v2 for now") +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now") def test_extra_properties() -> None: class Item(BaseModel): prop: int diff --git a/tests/test_transform.py b/tests/test_transform.py index 14a6ae1..5adf5f4 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -8,14 +8,14 @@ import pytest -from opencode_ai._types import NOT_GIVEN, Base64FileInput +from opencode_ai._types import Base64FileInput, omit, not_given from opencode_ai._utils import ( PropertyInfo, transform as _transform, parse_datetime, async_transform as _async_transform, ) -from opencode_ai._compat import PYDANTIC_V2 +from opencode_ai._compat import PYDANTIC_V1 from opencode_ai._models import BaseModel _T = TypeVar("_T") @@ -189,7 +189,7 @@ class DateModel(BaseModel): @pytest.mark.asyncio async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - tz = "Z" if PYDANTIC_V2 else "+00:00" + tz = "+00:00" if PYDANTIC_V1 else "Z" assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] @@ -297,11 +297,11 @@ async def test_pydantic_unknown_field(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_types(use_async: bool) -> None: model = MyModel.construct(foo=True) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": True} @@ -309,11 +309,11 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_object_type(use_async: bool) -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": {"hello": "world"}} @@ -450,4 +450,11 @@ async def test_transform_skipping(use_async: bool) -> None: @pytest.mark.asyncio async def test_strips_notgiven(use_async: bool) -> None: assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} - assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {} diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py new file mode 100644 index 0000000..7363e95 --- /dev/null +++ b/tests/test_utils/test_datetime_parse.py @@ -0,0 +1,110 @@ +""" +Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py +with modifications so it works without pydantic v1 imports. +""" + +from typing import Type, Union +from datetime import date, datetime, timezone, timedelta + +import pytest + +from opencode_ai._utils import parse_date, parse_datetime + + +def create_tz(minutes: int) -> timezone: + return timezone(timedelta(minutes=minutes)) + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + ("1494012444.883309", date(2017, 5, 5)), + (b"1494012444.883309", date(2017, 5, 5)), + (1_494_012_444.883_309, date(2017, 5, 5)), + ("1494012444", date(2017, 5, 5)), + (1_494_012_444, date(2017, 5, 5)), + (0, date(1970, 1, 1)), + ("2012-04-23", date(2012, 4, 23)), + (b"2012-04-23", date(2012, 4, 23)), + ("2012-4-9", date(2012, 4, 9)), + (date(2012, 4, 9), date(2012, 4, 9)), + (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)), + # Invalid inputs + ("x20120423", ValueError), + ("2012-04-56", ValueError), + (19_999_999_999, date(2603, 10, 11)), # just before watershed + (20_000_000_001, date(1970, 8, 20)), # just after watershed + (1_549_316_052, date(2019, 2, 4)), # nowish in s + (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms + (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs + (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns + ("infinity", date(9999, 12, 31)), + ("inf", date(9999, 12, 31)), + (float("inf"), date(9999, 12, 31)), + ("infinity ", date(9999, 12, 31)), + (int("1" + "0" * 100), date(9999, 12, 31)), + (1e1000, date(9999, 12, 31)), + ("-infinity", date(1, 1, 1)), + ("-inf", date(1, 1, 1)), + ("nan", ValueError), + ], +) +def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_date(value) + else: + assert parse_date(value) == result + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + # values in seconds + ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + # values in ms + ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)), + ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)), + (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)), + ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)), + ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)), + ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))), + ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))), + ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))), + ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (datetime(2017, 5, 5), datetime(2017, 5, 5)), + (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)), + # Invalid inputs + ("x20120423091500", ValueError), + ("2012-04-56T09:15:90", ValueError), + ("2012-04-23T11:05:00-25:00", ValueError), + (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed + (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed + (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s + (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms + (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs + (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns + ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)), + (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)), + (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("-infinity", datetime(1, 1, 1, 0, 0)), + ("-inf", datetime(1, 1, 1, 0, 0)), + ("nan", ValueError), + ], +) +def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_datetime(value) + else: + assert parse_datetime(value) == result diff --git a/tests/utils.py b/tests/utils.py index 5842949..bf5bda6 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,7 +4,7 @@ import inspect import traceback import contextlib -from typing import Any, TypeVar, Iterator, cast +from typing import Any, TypeVar, Iterator, Sequence, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type @@ -15,10 +15,11 @@ is_list_type, is_union_type, extract_type_arg, + is_sequence_type, is_annotated_type, is_type_alias_type, ) -from opencode_ai._compat import PYDANTIC_V2, field_outer_type, get_model_fields +from opencode_ai._compat import PYDANTIC_V1, field_outer_type, get_model_fields from opencode_ai._models import BaseModel BaseModelT = TypeVar("BaseModelT", bound=BaseModel) @@ -27,12 +28,12 @@ def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: for name, field in get_model_fields(model).items(): field_value = getattr(value, name) - if PYDANTIC_V2: - allow_none = False - else: + if PYDANTIC_V1: # in v1 nullability was structured differently # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields allow_none = getattr(field, "allow_none", False) + else: + allow_none = False assert_matches_type( field_outer_type(field), @@ -71,6 +72,13 @@ def assert_matches_type( if is_list_type(type_): return _assert_list_type(type_, value) + if is_sequence_type(type_): + assert isinstance(value, Sequence) + inner_type = get_args(type_)[0] + for entry in value: # type: ignore + assert_type(inner_type, entry) # type: ignore + return + if origin == str: assert isinstance(value, str) elif origin == int: