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 @@
[)](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: