opencode-py 0.2.1__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,491 @@
1
+ Metadata-Version: 2.4
2
+ Name: opencode-py
3
+ Version: 0.2.2
4
+ Summary: Python SDK for Opencode — the open source AI coding agent
5
+ Project-URL: Homepage, https://github.com/skislyakow/opencode-py
6
+ Project-URL: Repository, https://github.com/skislyakow/opencode-py
7
+ Project-URL: Documentation, https://github.com/skislyakow/opencode-py
8
+ Project-URL: Changelog, https://github.com/skislyakow/opencode-py/blob/main/CHANGELOG.md
9
+ Author-email: Sergey Kislyakov <s.kislyakov84@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: ai,coding-agent,llm,opencode,python-sdk,sdk
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: typing-extensions>=4.6.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.0; extra == 'dev'
28
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
29
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.5.0; extra == 'dev'
33
+ Requires-Dist: twine>=4.0; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # Opencode Python SDK
37
+
38
+ <p align="center">
39
+ <a href="https://pypi.org/project/opencode-py/"><img src="https://img.shields.io/pypi/v/opencode-py" alt="PyPI version"></a>
40
+ <a href="https://pypi.org/project/opencode-py/"><img src="https://img.shields.io/pypi/pyversions/opencode-py" alt="Python versions"></a>
41
+ <a href="https://github.com/skislyakow/opencode-py/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/opencode-py" alt="License"></a>
42
+ <a href="https://pypi.org/project/opencode-py/"><img src="https://img.shields.io/pypi/dm/opencode-py" alt="Downloads"></a>
43
+ <a href="https://github.com/skislyakow/opencode-py/actions/workflows/test.yml"><img src="https://github.com/skislyakow/opencode-py/actions/workflows/test.yml/badge.svg" alt="Tests"></a>
44
+ <img src="https://img.shields.io/badge/build-hatchling-4051b5" alt="Hatchling">
45
+ <img src="https://img.shields.io/badge/http-httpx-blue" alt="httpx">
46
+ <img src="https://img.shields.io/badge/models-pydantic-E92063" alt="pydantic">
47
+ </p>
48
+
49
+ Python SDK for [Opencode](https://opencode.ai) — the open source AI coding agent.
50
+
51
+ ```bash
52
+ pip install opencode-py
53
+ ```
54
+
55
+ ## CLI
56
+
57
+ After installation, the `opencode-py` command is available **system-wide** from any directory:
58
+
59
+ ```bash
60
+ opencode-py "What is the capital of France?" # one-shot prompt
61
+ echo "What is the capital of France?" | opencode-py # via pipe
62
+ opencode-py --help # show all options
63
+ ```
64
+
65
+ All CLI flags:
66
+
67
+ | Flag | Description |
68
+ |------|-------------|
69
+ | `prompt` (positional) | Prompt text or read from stdin |
70
+ | `--model` / `-m` | Model name (e.g. `opencode/big-pickle`) |
71
+ | `--keep` / `-k` | Keep session alive between calls |
72
+ | `--auto-tools` | Enable agentic tool execution |
73
+ | `--directory` / `-d` | Working directory |
74
+ | `--port` / `-p` | Server port (default: 4096) |
75
+
76
+ You can also use `python -m opencode`:
77
+
78
+ ```bash
79
+ python -m opencode "Explain dependency injection"
80
+ python -m opencode --model "opencode/big-pickle" "Hello"
81
+ ```
82
+
83
+ ## Client library reference
84
+
85
+ ### One-shot (spawns server, asks, cleans up)
86
+
87
+ ```python
88
+ from opencode import opencode
89
+
90
+ answer = opencode("What is the capital of France?")
91
+ print(answer)
92
+ ```
93
+
94
+ ### Context manager (recommended)
95
+
96
+ ```python
97
+ from opencode import Opencode
98
+
99
+ with Opencode() as ai:
100
+ answer = ai.ask("Explain dependency injection")
101
+ print(answer)
102
+ ```
103
+
104
+ ### Streaming
105
+
106
+ ```python
107
+ with Opencode() as ai:
108
+ for chunk in ai.ask_stream("Write a Python function"):
109
+ print(chunk, end="")
110
+ ```
111
+
112
+ ### Conversations
113
+
114
+ ```python
115
+ with Opencode() as ai:
116
+ session = ai.create_session()
117
+ msg1 = session.prompt("Suggest a project name")
118
+ print(f"AI: {msg1}")
119
+ msg2 = session.prompt("Now write a tagline for it")
120
+ print(f"AI: {msg2}")
121
+ ```
122
+
123
+ ### Session methods
124
+
125
+ Every `Session` object provides additional methods:
126
+
127
+ ```python
128
+ with Opencode() as ai:
129
+ session = ai.create_session()
130
+ session.prompt("Hello")
131
+
132
+ # Get conversation history
133
+ ctx = session.context() # list of all messages
134
+ msgs = session.messages() # paginated message list
135
+
136
+ # Control
137
+ session.abort() # abort current generation
138
+ session.compact() # compact conversation
139
+ session.fork() # fork into new session
140
+
141
+ # Inspect
142
+ session.diff() # file changes made by AI
143
+ session.todo() # remaining TODOs
144
+ ```
145
+
146
+ ### Multi-turn (keep mode)
147
+
148
+ Reuses server and session across calls:
149
+
150
+ ```python
151
+ from opencode import opencode
152
+
153
+ r1 = opencode("My name is Alice", keep=True)
154
+ r2 = opencode("What's my name?", keep=True) # remembers conversation
155
+ r3 = opencode("That's all", keep=False) # closes server
156
+
157
+ # Also accepts: model, format, port, directory, config, agent
158
+ ```
159
+
160
+ ### Auto-tools (agentic tool execution)
161
+
162
+ ```python
163
+ r = opencode("Create a file called hello.txt", auto_tools=True)
164
+ ```
165
+
166
+ Available tools: `bash`, `write`, `edit`, `read`, `glob`, `grep`.
167
+
168
+ By default `bash` asks for permission in the console, all others run without prompting.
169
+
170
+ Custom permissions via `Session.ask()`:
171
+
172
+ ```python
173
+ from opencode import Opencode, ToolExecutor
174
+
175
+ with Opencode() as ai:
176
+ session = ai.create_session()
177
+ msg = session.ask(
178
+ "Write test.py with print('hello')",
179
+ tool_executor=ToolExecutor(
180
+ permissions={"write": "allow"},
181
+ workdir="/path/to/sandbox", # restrict file operations
182
+ ),
183
+ max_tool_rounds=25, # safety limit
184
+ quiet=True, # suppress tool logs
185
+ )
186
+ ```
187
+
188
+ The first AI response in `ask()` enters plan mode — the SDK auto-confirms with
189
+ `"Exit plan mode and proceed"` to make the model execute tools immediately.
190
+
191
+ ### Low-level client (any endpoint)
192
+
193
+ ```python
194
+ with Opencode() as ai:
195
+ content = ai.client.file_read("src/main.py")
196
+ diff = ai.client.vcs_diff("HEAD~3")
197
+ config = ai.client.config_get()
198
+ session = ai.client.session_create()
199
+ ai.client.v2_session_prompt(session.id, {"text": "Hello"})
200
+ ```
201
+
202
+ All client methods return typed Pydantic models — IDE autocomplete,
203
+ `.model_dump()`, `.model_dump_json()`.
204
+
205
+ #### Connecting to an existing server
206
+
207
+ Skip subprocess management by pointing at a running `opencode serve`:
208
+
209
+ ```python
210
+ from opencode import OpencodeClient
211
+
212
+ client = OpencodeClient(base_url="http://127.0.0.1:4096", directory=".")
213
+ health = client.health()
214
+ ```
215
+
216
+ ```python
217
+ from opencode import AsyncOpendcodeClient
218
+
219
+ async with AsyncOpendcodeClient(base_url="http://127.0.0.1:4096") as client:
220
+ health = await client.health()
221
+ ```
222
+
223
+ #### Cloning a client
224
+
225
+ ```python
226
+ client2 = client.copy(base_url="http://other:4096", timeout=60.0)
227
+
228
+ # Or via with_options:
229
+ faster = client.with_options(timeout=10.0, max_retries=0)
230
+ ```
231
+
232
+ ### Retry & error handling
233
+
234
+ Typed exception hierarchy. All errors are importable from `opencode`:
235
+
236
+ ```python
237
+ from opencode import OpencodeClient, RateLimitError, InternalServerError
238
+
239
+ client = OpencodeClient(max_retries=3) # exponential backoff with jitter
240
+
241
+ try:
242
+ health = client.health()
243
+ print(health.version)
244
+ except RateLimitError:
245
+ print("too many requests — retried but failed")
246
+ except InternalServerError:
247
+ print("server error")
248
+ ```
249
+
250
+ Full error class hierarchy:
251
+
252
+ | Class | HTTP status | When raised |
253
+ |-------|-------------|-------------|
254
+ | `OpencodeError` | — | Base for all SDK errors |
255
+ | `APIConnectionError` | — | Network / connection failure |
256
+ | `APITimeoutError` | — | Request timed out |
257
+ | `APIResponseValidationError` | — | Response doesn't match schema |
258
+ | `APIStatusError` | 4xx/5xx | Base for HTTP error responses |
259
+ | `BadRequestError` | 400 | Malformed request |
260
+ | `AuthenticationError` | 401 | Invalid or missing API key |
261
+ | `PermissionDeniedError` | 403 | Access denied |
262
+ | `NotFoundError` | 404 | Resource not found |
263
+ | `ConflictError` | 409 | Resource conflict |
264
+ | `UnprocessableEntityError` | 422 | Validation error in request body |
265
+ | `RateLimitError` | 429 | Rate limit exceeded |
266
+ | `InternalServerError` | 500+ | Server-side error |
267
+ | `BinaryNotFoundError` | — | `opencode` binary not on PATH |
268
+ | `ServerStartupTimeoutError` | — | Server didn't start in time |
269
+
270
+ Retry policy: 408, 409, 429, 5xx and timeouts are retried with exponential
271
+ backoff + jitter. `Retry-After` and `retry-after-ms` headers are respected.
272
+
273
+ ### Structured output
274
+
275
+ ```python
276
+ with Opencode(model="anthropic/claude-sonnet-4") as ai:
277
+ result = ai.ask(
278
+ "Generate a user profile",
279
+ format={
280
+ "type": "json_schema",
281
+ "schema": {
282
+ "type": "object",
283
+ "properties": {
284
+ "name": {"type": "string"},
285
+ "age": {"type": "integer"},
286
+ },
287
+ "required": ["name", "age"],
288
+ },
289
+ },
290
+ )
291
+ # result is a JSON string matching the schema
292
+ ```
293
+
294
+ Works with `opencode()`, `async_opencode()`, `Session.prompt()`, and `Session.ask()`.
295
+
296
+ Requires a model that supports `tool_choice="required"` (Claude, GPT-4).
297
+ The free `opencode/big-pickle` (DeepSeek) does NOT support this.
298
+
299
+ ### Debug logging
300
+
301
+ ```bash
302
+ # Linux / macOS (bash/zsh)
303
+ OPENCODE_LOG=debug python my_script.py
304
+
305
+ # Windows (PowerShell)
306
+ $env:OPENCODE_LOG="debug"; python my_script.py
307
+
308
+ # Windows (cmd)
309
+ set OPENCODE_LOG=debug && python my_script.py
310
+ ```
311
+
312
+ Shows all HTTP requests/responses with timing.
313
+
314
+ ### Web UI (zero dependencies)
315
+
316
+ ```bash
317
+ python web/server.py
318
+ # → open http://127.0.0.1:3000
319
+ ```
320
+
321
+ Built-in HTTP server + proxy to `opencode serve` — no extra dependencies.
322
+
323
+ ### Interactive dialog
324
+
325
+ ```bash
326
+ python live.py
327
+ ```
328
+
329
+ Multi-turn dialog with `keep=True`, server cleaned up on exit via `atexit`.
330
+
331
+ ### ToolExecutor reference
332
+
333
+ ```python
334
+ from opencode import ToolExecutor
335
+
336
+ # Default permissions:
337
+ # bash → "ask" (prompts in console)
338
+ # write → "allow"
339
+ # edit → "allow"
340
+ # read → "allow"
341
+ # glob → "allow"
342
+ # grep → "allow"
343
+
344
+ executor = ToolExecutor(
345
+ permissions={
346
+ "bash": "allow", # always allow
347
+ "write": "deny", # always deny
348
+ "grep": "ask", # ask each time
349
+ },
350
+ workdir="/path/to/sandbox", # restrict file operations here
351
+ confirm=lambda name, inp: name != "bash", # custom confirm function
352
+ )
353
+
354
+ # Use with Session.ask():
355
+ session.ask("Create a project", tool_executor=executor)
356
+ ```
357
+
358
+ ### Binary management
359
+
360
+ When `opencode` is not on PATH, the SDK auto-downloads it to
361
+ `~/.opencode/bin/opencode`.
362
+
363
+ Resolution order:
364
+ 1. `PATH` — `shutil.which("opencode")`
365
+ 2. `~/.opencode/bin/opencode` — previously downloaded copy
366
+ 3. GitHub releases — download for current platform
367
+
368
+ Supported platforms: `win32-x64`, `win32-arm64`, `darwin-x64`, `darwin-arm64`,
369
+ `linux-x64`, `linux-arm64`.
370
+
371
+ Override the binary path directly:
372
+
373
+ ```python
374
+ with Opencode(opencode_binary="/custom/path/opencode") as ai:
375
+ ...
376
+ ```
377
+
378
+ ### OpencodeServer (low-level server control)
379
+
380
+ ```python
381
+ from opencode import OpencodeServer, create_opencode_server
382
+
383
+ server = create_opencode_server(
384
+ port=4096,
385
+ hostname="127.0.0.1",
386
+ timeout=30.0,
387
+ config={"model": "opencode/big-pickle"},
388
+ opencode_binary="/path/to/opencode",
389
+ )
390
+ print(server.url) # "http://127.0.0.1:4096"
391
+
392
+ # Later:
393
+ server.close() # kills the subprocess
394
+ ```
395
+
396
+ ## Configuration reference
397
+
398
+ All parameters for `Opendcode()` / `AsyncOpendcode()`:
399
+
400
+ | Parameter | Default | Description |
401
+ |-----------|---------|-------------|
402
+ | `model` | `None` | Model name, e.g. `"opencode/big-pickle"` or `"provider/model"` |
403
+ | `hostname` | `"127.0.0.1"` | Bind address for the server |
404
+ | `port` | `4096` | Port for the server |
405
+ | `directory` | `None` | Working directory passed to all API calls |
406
+ | `workspace` | `None` | Workspace directory for the session |
407
+ | `server_timeout` | `30.0` | Seconds to wait for server startup |
408
+ | `client_timeout` | `300.0` | Seconds before HTTP request timeout |
409
+ | `config` | `None` | Server config dict (see opencode docs) |
410
+ | `opencode_binary` | `None` | Path to opencode binary (auto-downloaded if not set) |
411
+
412
+ All parameters are keyword-only.
413
+
414
+ ## Async API
415
+
416
+ ### Basic
417
+
418
+ ```python
419
+ import asyncio
420
+ from opencode import AsyncOpendcode
421
+
422
+ async def main():
423
+ async with AsyncOpendcode() as ai:
424
+ answer = await ai.ask("Explain async/await in Python")
425
+ print(answer)
426
+
427
+ asyncio.run(main())
428
+ ```
429
+
430
+ ### Async streaming
431
+
432
+ ```python
433
+ async with AsyncOpendcode() as ai:
434
+ async for chunk in ai.ask_stream("Write a poem"):
435
+ print(chunk, end="")
436
+ ```
437
+
438
+ ### Async conversations
439
+
440
+ ```python
441
+ async with AsyncOpendcode() as ai:
442
+ session = await ai.create_session()
443
+ msg1 = await session.prompt("Suggest a project name")
444
+ msg2 = await session.prompt("Now write a tagline for it")
445
+ ```
446
+
447
+ ### Async low-level client
448
+
449
+ ```python
450
+ from opencode import AsyncOpendcodeClient
451
+
452
+ async with AsyncOpendcodeClient() as client:
453
+ health = await client.health()
454
+ print(health.version) # typed Pydantic model
455
+ ```
456
+
457
+ ### Async convenience function
458
+
459
+ ```python
460
+ from opencode import async_opencode
461
+
462
+ result = await async_opencode("Hello", keep=True)
463
+ result2 = await async_opencode("Still there?", keep=True)
464
+ result3 = await async_opencode("Bye")
465
+
466
+ # Also accepts: model, format, port, directory, config, agent, auto_tools
467
+ ```
468
+
469
+ ## OpenAPI response models
470
+
471
+ ```python
472
+ from opencode._response_models import HealthResponse, SessionResponse, FileContentResponse
473
+
474
+ # These are Pydantic BaseModel classes with:
475
+ # .model_dump() -> dict
476
+ # .model_dump_json() -> str
477
+ # .model_validate(dict) -> classmethod
478
+ ```
479
+
480
+ ## Development
481
+
482
+ ```bash
483
+ # Install in editable mode
484
+ pip install -e ".[dev]"
485
+
486
+ # Run tests
487
+ pytest
488
+
489
+ # Build
490
+ python -m build --wheel
491
+ ```
@@ -16,8 +16,8 @@ opencode/_session.py,sha256=WA9O36a9jIQPVWbiRa_Qq--Q2_sQgfZY-qriFYuIEUk,6273
16
16
  opencode/_tools.py,sha256=pXIcGiuBLHZJrhFoIr02PD5rXRZ_kmEfH_yCDL-4kCw,6186
17
17
  opencode/_types.py,sha256=abvR9AgIDy1Ao4udciG4bm5EVrS1a7iI8UjTnfp_4F0,513
18
18
  opencode/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
19
- opencode_py-0.2.1.dist-info/METADATA,sha256=hJyLnVo-OF9lrRu1bpxIeDKKObPwjou23-9fjauUZ70,6642
20
- opencode_py-0.2.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
21
- opencode_py-0.2.1.dist-info/entry_points.txt,sha256=uFnHHKDTlnLNw7YZ7oV7aAOQZKKmhet5DMAkGKkoSU4,55
22
- opencode_py-0.2.1.dist-info/licenses/LICENSE,sha256=ELTf0xrIX83aTz71tsvSJmHlanAsDLGOc3trjTn1exA,1073
23
- opencode_py-0.2.1.dist-info/RECORD,,
19
+ opencode_py-0.2.2.dist-info/METADATA,sha256=4mIasdpNlyz1UpBxDf81T0Dl2_lslSzKGPeDqaCX1tw,14097
20
+ opencode_py-0.2.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
21
+ opencode_py-0.2.2.dist-info/entry_points.txt,sha256=uFnHHKDTlnLNw7YZ7oV7aAOQZKKmhet5DMAkGKkoSU4,55
22
+ opencode_py-0.2.2.dist-info/licenses/LICENSE,sha256=ELTf0xrIX83aTz71tsvSJmHlanAsDLGOc3trjTn1exA,1073
23
+ opencode_py-0.2.2.dist-info/RECORD,,
@@ -1,246 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: opencode-py
3
- Version: 0.2.1
4
- Summary: Python SDK for Opencode — the open source AI coding agent
5
- Project-URL: Homepage, https://github.com/skislyakow/opencode-py
6
- Project-URL: Repository, https://github.com/skislyakow/opencode-py
7
- Project-URL: Documentation, https://github.com/skislyakow/opencode-py
8
- Project-URL: Changelog, https://github.com/skislyakow/opencode-py/blob/main/CHANGELOG.md
9
- Author-email: Sergey Kislyakov <s.kislyakov84@gmail.com>
10
- License: MIT
11
- License-File: LICENSE
12
- Keywords: ai,coding-agent,llm,opencode,python-sdk,sdk
13
- Classifier: Development Status :: 4 - Beta
14
- Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3 :: Only
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Classifier: Programming Language :: Python :: 3.13
22
- Requires-Python: >=3.10
23
- Requires-Dist: httpx>=0.27.0
24
- Requires-Dist: pydantic>=2.0.0
25
- Requires-Dist: typing-extensions>=4.6.0
26
- Provides-Extra: dev
27
- Requires-Dist: build>=1.0; extra == 'dev'
28
- Requires-Dist: httpx>=0.27.0; extra == 'dev'
29
- Requires-Dist: mypy>=1.10.0; extra == 'dev'
30
- Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
31
- Requires-Dist: pytest>=8.0; extra == 'dev'
32
- Requires-Dist: ruff>=0.5.0; extra == 'dev'
33
- Requires-Dist: twine>=4.0; extra == 'dev'
34
- Description-Content-Type: text/markdown
35
-
36
- # Opencode Python SDK
37
-
38
- <p align="center">
39
- <a href="https://pypi.org/project/opencode-py/"><img src="https://img.shields.io/pypi/v/opencode-py" alt="PyPI version"></a>
40
- <a href="https://pypi.org/project/opencode-py/"><img src="https://img.shields.io/pypi/pyversions/opencode-py" alt="Python versions"></a>
41
- <a href="https://github.com/skislyakow/opencode-py/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/opencode-py" alt="License"></a>
42
- <a href="https://pypi.org/project/opencode-py/"><img src="https://img.shields.io/pypi/dm/opencode-py" alt="Downloads"></a>
43
- <a href="https://github.com/skislyakow/opencode-py/actions/workflows/test.yml"><img src="https://github.com/skislyakow/opencode-py/actions/workflows/test.yml/badge.svg" alt="Tests"></a>
44
- <img src="https://img.shields.io/badge/build-hatchling-4051b5" alt="Hatchling">
45
- <img src="https://img.shields.io/badge/http-httpx-blue" alt="httpx">
46
- <img src="https://img.shields.io/badge/models-pydantic-E92063" alt="pydantic">
47
- </p>
48
-
49
- Python SDK for [Opencode](https://opencode.ai) — the open source AI coding agent.
50
-
51
- ```bash
52
- pip install opencode-py
53
- ```
54
-
55
- ## Quick start
56
-
57
- ### One-shot (spawns server, asks, cleans up)
58
-
59
- ```python
60
- from opencode import opencode
61
-
62
- answer = opencode("What is the capital of France?")
63
- print(answer)
64
- ```
65
-
66
- ### Context manager (recommended)
67
-
68
- ```python
69
- from opencode import Opencode
70
-
71
- with Opencode() as ai:
72
- answer = ai.ask("Explain dependency injection")
73
- print(answer)
74
- ```
75
-
76
- ### Streaming
77
-
78
- ```python
79
- with Opencode() as ai:
80
- for chunk in ai.ask_stream("Write a Python function"):
81
- print(chunk, end="")
82
- ```
83
-
84
- ### Conversations
85
-
86
- ```python
87
- with Opencode() as ai:
88
- session = ai.create_session()
89
- msg1 = session.prompt("Suggest a project name")
90
- print(f"AI: {msg1}")
91
- msg2 = session.prompt("Now write a tagline for it")
92
- print(f"AI: {msg2}")
93
- ```
94
-
95
- ### Multi-turn (keep mode)
96
-
97
- ```python
98
- from opencode import opencode
99
-
100
- # keep=True — server and session stay alive between calls
101
- r1 = opencode("My name is Alice", keep=True)
102
- r2 = opencode("What's my name?", keep=True) # remembers the conversation
103
- r3 = opencode("That's all", keep=False) # keep=False closes the server
104
- ```
105
-
106
- ### Auto-tools (agentic tool execution)
107
-
108
- ```python
109
- r = opencode("Create a file called hello.txt", auto_tools=True)
110
- ```
111
-
112
- Available tools: `bash`, `write`, `edit`, `read`, `glob`, `grep`.
113
-
114
- By default, `bash` asks for permission in the console, all others run without prompting.
115
-
116
- Custom permissions via `Session.ask()`:
117
-
118
- ```python
119
- from opencode import Opencode, ToolExecutor
120
-
121
- with Opencode() as ai:
122
- session = ai.create_session()
123
- msg = session.ask(
124
- "Write test.py with print('hello')",
125
- tool_executor=ToolExecutor(permissions={"write": "allow"}),
126
- )
127
- ```
128
-
129
- ### Low-level API (any endpoint)
130
-
131
- ```python
132
- with Opencode() as ai:
133
- content = ai.client.file_read("src/main.py")
134
- diff = ai.client.vcs_diff("HEAD~3")
135
- config = ai.client.config_get()
136
- session = ai.client.session_create()
137
- ai.client.v2_session_prompt(session.id, {"text": "Hello"})
138
- ```
139
-
140
- All client methods return typed Pydantic models — IDE autocomplete, validation, `.model_dump()`, `.model_dump_json()`.
141
-
142
- ### Retry & error handling
143
-
144
- ```python
145
- from opencode import OpencodeClient, RateLimitError, InternalServerError
146
-
147
- client = OpencodeClient(max_retries=3) # exponential backoff with jitter
148
-
149
- try:
150
- health = client.health()
151
- print(health.version)
152
- except RateLimitError:
153
- print("too many requests — retried but failed")
154
- except InternalServerError:
155
- print("server error")
156
- ```
157
-
158
- ### Debug logging
159
-
160
- ```bash
161
- OPENCODE_LOG=debug python my_script.py
162
- ```
163
-
164
- Shows all HTTP requests/responses with timing.
165
-
166
- ### Web UI (zero dependencies)
167
-
168
- ```bash
169
- python web/server.py
170
- # → open http://127.0.0.1:3000
171
- ```
172
-
173
- Built-in HTTP server + proxy to `opencode serve` — no extra dependencies.
174
-
175
- ### Interactive dialog
176
-
177
- ```bash
178
- python live.py
179
- ```
180
-
181
- Multi-turn dialog with `keep=True`, server cleaned up on exit via `atexit`.
182
-
183
- ### Configuration
184
-
185
- ```python
186
- with Opencode(
187
- model="claude-sonnet-4-20250514",
188
- directory="/path/to/project",
189
- port=4096,
190
- ) as ai:
191
- ...
192
- ```
193
-
194
- ## Async API
195
-
196
- ```python
197
- import asyncio
198
- from opencode import AsyncOpendcode
199
-
200
- async def main():
201
- async with AsyncOpendcode() as ai:
202
- answer = await ai.ask("Explain async/await in Python")
203
- print(answer)
204
-
205
- asyncio.run(main())
206
- ```
207
-
208
- ### Async streaming
209
-
210
- ```python
211
- async with AsyncOpendcode() as ai:
212
- async for chunk in ai.ask_stream("Write a poem"):
213
- print(chunk, end="")
214
- ```
215
-
216
- ### Async conversations
217
-
218
- ```python
219
- async with AsyncOpendcode() as ai:
220
- session = await ai.create_session()
221
- msg1 = await session.prompt("Suggest a project name")
222
- msg2 = await session.prompt("Now write a tagline for it")
223
- ```
224
-
225
- ### Async low-level client
226
-
227
- ```python
228
- from opencode import AsyncOpendcodeClient
229
-
230
- async with AsyncOpendcodeClient() as client:
231
- health = await client.health()
232
- print(health.version) # typed Pydantic model
233
- ```
234
-
235
- ## Development
236
-
237
- ```bash
238
- # Install in editable mode
239
- pip install -e ".[dev]"
240
-
241
- # Run tests
242
- pytest
243
-
244
- # Build
245
- python -m build --wheel
246
- ```