mirror of
https://github.com/anomalyco/opencode-sdk-python.git
synced 2026-04-28 12:39:54 +00:00
feat(api): update via SDK Studio
This commit is contained in:
parent
6d8571b73d
commit
d70071b1fc
6 changed files with 84 additions and 35 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
configured_endpoints: 16
|
configured_endpoints: 16
|
||||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-b4a3f35e4a44e5a5034508ced15d7b44c1924000062e0f5293797413d26ee412.yml
|
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-b4a3f35e4a44e5a5034508ced15d7b44c1924000062e0f5293797413d26ee412.yml
|
||||||
openapi_spec_hash: f17b1091020f90126e6cefc2d38ff85f
|
openapi_spec_hash: f17b1091020f90126e6cefc2d38ff85f
|
||||||
config_hash: 1156f6f6fb7245e7b021daddf23153e3
|
config_hash: e2d21e779cfc4e26a99b9e4e75de3f50
|
||||||
|
|
|
||||||
26
README.md
26
README.md
|
|
@ -80,6 +80,32 @@ async def main() -> None:
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Streaming responses
|
||||||
|
|
||||||
|
We provide support for streaming responses using Server Side Events (SSE).
|
||||||
|
|
||||||
|
```python
|
||||||
|
from opencode_ai import Opencode
|
||||||
|
|
||||||
|
client = Opencode()
|
||||||
|
|
||||||
|
stream = client.event.list()
|
||||||
|
for events in stream:
|
||||||
|
print(events)
|
||||||
|
```
|
||||||
|
|
||||||
|
The async client uses the exact same interface.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from opencode_ai import AsyncOpencode
|
||||||
|
|
||||||
|
client = AsyncOpencode()
|
||||||
|
|
||||||
|
stream = await client.event.list()
|
||||||
|
async for events in stream:
|
||||||
|
print(events)
|
||||||
|
```
|
||||||
|
|
||||||
## Using types
|
## Using types
|
||||||
|
|
||||||
Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
|
Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,8 @@ class Opencode(SyncAPIClient):
|
||||||
_strict_response_validation=_strict_response_validation,
|
_strict_response_validation=_strict_response_validation,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._default_stream_cls = Stream
|
||||||
|
|
||||||
self.event = event.EventResource(self)
|
self.event = event.EventResource(self)
|
||||||
self.app = app.AppResource(self)
|
self.app = app.AppResource(self)
|
||||||
self.file = file.FileResource(self)
|
self.file = file.FileResource(self)
|
||||||
|
|
@ -247,6 +249,8 @@ class AsyncOpencode(AsyncAPIClient):
|
||||||
_strict_response_validation=_strict_response_validation,
|
_strict_response_validation=_strict_response_validation,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._default_stream_cls = AsyncStream
|
||||||
|
|
||||||
self.event = event.AsyncEventResource(self)
|
self.event = event.AsyncEventResource(self)
|
||||||
self.app = app.AsyncAppResource(self)
|
self.app = app.AsyncAppResource(self)
|
||||||
self.file = file.AsyncFileResource(self)
|
self.file = file.AsyncFileResource(self)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from .._response import (
|
||||||
async_to_raw_response_wrapper,
|
async_to_raw_response_wrapper,
|
||||||
async_to_streamed_response_wrapper,
|
async_to_streamed_response_wrapper,
|
||||||
)
|
)
|
||||||
|
from .._streaming import Stream, AsyncStream
|
||||||
from .._base_client import make_request_options
|
from .._base_client import make_request_options
|
||||||
from ..types.event_list_response import EventListResponse
|
from ..types.event_list_response import EventListResponse
|
||||||
|
|
||||||
|
|
@ -50,17 +51,16 @@ class EventResource(SyncAPIResource):
|
||||||
extra_query: Query | None = None,
|
extra_query: Query | None = None,
|
||||||
extra_body: Body | None = None,
|
extra_body: Body | None = None,
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
||||||
) -> EventListResponse:
|
) -> Stream[EventListResponse]:
|
||||||
"""Get events"""
|
"""Get events"""
|
||||||
return cast(
|
return self._get(
|
||||||
EventListResponse,
|
"/event",
|
||||||
self._get(
|
options=make_request_options(
|
||||||
"/event",
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
||||||
options=make_request_options(
|
|
||||||
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system
|
|
||||||
),
|
),
|
||||||
|
cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system
|
||||||
|
stream=True,
|
||||||
|
stream_cls=Stream[EventListResponse],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -93,17 +93,16 @@ class AsyncEventResource(AsyncAPIResource):
|
||||||
extra_query: Query | None = None,
|
extra_query: Query | None = None,
|
||||||
extra_body: Body | None = None,
|
extra_body: Body | None = None,
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
||||||
) -> EventListResponse:
|
) -> AsyncStream[EventListResponse]:
|
||||||
"""Get events"""
|
"""Get events"""
|
||||||
return cast(
|
return await self._get(
|
||||||
EventListResponse,
|
"/event",
|
||||||
await self._get(
|
options=make_request_options(
|
||||||
"/event",
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
||||||
options=make_request_options(
|
|
||||||
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system
|
|
||||||
),
|
),
|
||||||
|
cast_to=cast(Any, EventListResponse), # Union types cannot be passed in as arguments in the type system
|
||||||
|
stream=True,
|
||||||
|
stream_cls=AsyncStream[EventListResponse],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ from typing import Any, cast
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from opencode_ai import Opencode, AsyncOpencode
|
from opencode_ai import Opencode, AsyncOpencode
|
||||||
from tests.utils import assert_matches_type
|
|
||||||
from opencode_ai.types import EventListResponse
|
|
||||||
|
|
||||||
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
|
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
|
||||||
|
|
||||||
|
|
@ -20,18 +18,17 @@ class TestEvent:
|
||||||
@pytest.mark.skip()
|
@pytest.mark.skip()
|
||||||
@parametrize
|
@parametrize
|
||||||
def test_method_list(self, client: Opencode) -> None:
|
def test_method_list(self, client: Opencode) -> None:
|
||||||
event = client.event.list()
|
event_stream = client.event.list()
|
||||||
assert_matches_type(EventListResponse, event, path=["response"])
|
event_stream.response.close()
|
||||||
|
|
||||||
@pytest.mark.skip()
|
@pytest.mark.skip()
|
||||||
@parametrize
|
@parametrize
|
||||||
def test_raw_response_list(self, client: Opencode) -> None:
|
def test_raw_response_list(self, client: Opencode) -> None:
|
||||||
response = client.event.with_raw_response.list()
|
response = client.event.with_raw_response.list()
|
||||||
|
|
||||||
assert response.is_closed is True
|
|
||||||
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
||||||
event = response.parse()
|
stream = response.parse()
|
||||||
assert_matches_type(EventListResponse, event, path=["response"])
|
stream.close()
|
||||||
|
|
||||||
@pytest.mark.skip()
|
@pytest.mark.skip()
|
||||||
@parametrize
|
@parametrize
|
||||||
|
|
@ -40,8 +37,8 @@ class TestEvent:
|
||||||
assert not response.is_closed
|
assert not response.is_closed
|
||||||
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
||||||
|
|
||||||
event = response.parse()
|
stream = response.parse()
|
||||||
assert_matches_type(EventListResponse, event, path=["response"])
|
stream.close()
|
||||||
|
|
||||||
assert cast(Any, response.is_closed) is True
|
assert cast(Any, response.is_closed) is True
|
||||||
|
|
||||||
|
|
@ -54,18 +51,17 @@ class TestAsyncEvent:
|
||||||
@pytest.mark.skip()
|
@pytest.mark.skip()
|
||||||
@parametrize
|
@parametrize
|
||||||
async def test_method_list(self, async_client: AsyncOpencode) -> None:
|
async def test_method_list(self, async_client: AsyncOpencode) -> None:
|
||||||
event = await async_client.event.list()
|
event_stream = await async_client.event.list()
|
||||||
assert_matches_type(EventListResponse, event, path=["response"])
|
await event_stream.response.aclose()
|
||||||
|
|
||||||
@pytest.mark.skip()
|
@pytest.mark.skip()
|
||||||
@parametrize
|
@parametrize
|
||||||
async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
|
async def test_raw_response_list(self, async_client: AsyncOpencode) -> None:
|
||||||
response = await async_client.event.with_raw_response.list()
|
response = await async_client.event.with_raw_response.list()
|
||||||
|
|
||||||
assert response.is_closed is True
|
|
||||||
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
||||||
event = await response.parse()
|
stream = await response.parse()
|
||||||
assert_matches_type(EventListResponse, event, path=["response"])
|
await stream.close()
|
||||||
|
|
||||||
@pytest.mark.skip()
|
@pytest.mark.skip()
|
||||||
@parametrize
|
@parametrize
|
||||||
|
|
@ -74,7 +70,7 @@ class TestAsyncEvent:
|
||||||
assert not response.is_closed
|
assert not response.is_closed
|
||||||
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
|
||||||
|
|
||||||
event = await response.parse()
|
stream = await response.parse()
|
||||||
assert_matches_type(EventListResponse, event, path=["response"])
|
await stream.close()
|
||||||
|
|
||||||
assert cast(Any, response.is_closed) is True
|
assert cast(Any, response.is_closed) is True
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ from pydantic import ValidationError
|
||||||
from opencode_ai import Opencode, AsyncOpencode, APIResponseValidationError
|
from opencode_ai import Opencode, AsyncOpencode, APIResponseValidationError
|
||||||
from opencode_ai._types import Omit
|
from opencode_ai._types import Omit
|
||||||
from opencode_ai._models import BaseModel, FinalRequestOptions
|
from opencode_ai._models import BaseModel, FinalRequestOptions
|
||||||
|
from opencode_ai._streaming import Stream, AsyncStream
|
||||||
from opencode_ai._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError
|
from opencode_ai._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError
|
||||||
from opencode_ai._base_client import (
|
from opencode_ai._base_client import (
|
||||||
DEFAULT_TIMEOUT,
|
DEFAULT_TIMEOUT,
|
||||||
|
|
@ -624,6 +625,17 @@ class TestOpencode:
|
||||||
with pytest.raises(TypeError, match=r"max_retries cannot be None"):
|
with pytest.raises(TypeError, match=r"max_retries cannot be None"):
|
||||||
Opencode(base_url=base_url, _strict_response_validation=True, max_retries=cast(Any, 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:
|
||||||
|
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])
|
||||||
|
assert isinstance(stream, Stream)
|
||||||
|
stream.response.close()
|
||||||
|
|
||||||
@pytest.mark.respx(base_url=base_url)
|
@pytest.mark.respx(base_url=base_url)
|
||||||
def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
|
def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
|
||||||
class Model(BaseModel):
|
class Model(BaseModel):
|
||||||
|
|
@ -1390,6 +1402,18 @@ class TestAsyncOpencode:
|
||||||
with pytest.raises(TypeError, match=r"max_retries cannot be None"):
|
with pytest.raises(TypeError, match=r"max_retries cannot be None"):
|
||||||
AsyncOpencode(base_url=base_url, _strict_response_validation=True, max_retries=cast(Any, 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:
|
||||||
|
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])
|
||||||
|
assert isinstance(stream, AsyncStream)
|
||||||
|
await stream.response.aclose()
|
||||||
|
|
||||||
@pytest.mark.respx(base_url=base_url)
|
@pytest.mark.respx(base_url=base_url)
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
|
async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue