pi-agent-python-sdk 0.2.1__tar.gz → 0.2.3__tar.gz

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.
Files changed (20) hide show
  1. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/CHANGELOG.md +19 -0
  2. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/PKG-INFO +168 -111
  3. pi_agent_python_sdk-0.2.3/README.md +337 -0
  4. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/pyproject.toml +3 -3
  5. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/_launch.py +1 -1
  6. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/types.py +37 -1
  7. pi_agent_python_sdk-0.2.1/README.md +0 -280
  8. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/.gitignore +0 -0
  9. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/LICENSE +0 -0
  10. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/__init__.py +0 -0
  11. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/_collections.py +0 -0
  12. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/_events.py +0 -0
  13. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/_observation.py +0 -0
  14. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/_runs.py +0 -0
  15. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/_transport.py +0 -0
  16. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/_usage.py +0 -0
  17. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/client.py +0 -0
  18. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/errors.py +0 -0
  19. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/py.typed +0 -0
  20. {pi_agent_python_sdk-0.2.1 → pi_agent_python_sdk-0.2.3}/src/pi_agent/sync.py +0 -0
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.3
6
+
7
+ ### Added
8
+
9
+ - Describe Pi 0.87.0 context-edit session entries and optional model image input
10
+ limits in the typed wire annotations. Both remain Pi-owned behavior; unknown
11
+ fields continue to pass through unchanged.
12
+
13
+ ### Changed
14
+
15
+ - Update the tested Pi runtime to 0.87.0, retaining 0.85.1 as the minimum.
16
+ - Refresh the README's quick start, branding, and demonstration video.
17
+
18
+ ## 0.2.2
19
+
20
+ ### Changed
21
+
22
+ - Update the tested Pi runtime to 0.86.1, retaining 0.85.1 as the minimum.
23
+
5
24
  ## 0.2.1
6
25
 
7
26
  ### Added
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pi-agent-python-sdk
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Typed async and synchronous Python clients for Pi's agent RPC runtime
5
5
  Project-URL: Homepage, https://github.com/cheenulabs/pi-agent-python-sdk
6
- Project-URL: Documentation, https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.1/docs
6
+ Project-URL: Documentation, https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.3/docs
7
7
  Project-URL: Issues, https://github.com/cheenulabs/pi-agent-python-sdk/issues
8
- Project-URL: Changelog, https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/CHANGELOG.md
8
+ Project-URL: Changelog, https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/CHANGELOG.md
9
9
  Author: cheenulabs
10
10
  License-Expression: MIT
11
11
  License-File: LICENSE
@@ -23,70 +23,80 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  # Pi Agent Python SDK
25
25
 
26
- [![PyPI](https://badge.fury.io/py/pi-agent-python-sdk.svg)][pypi]
26
+ <p align="center">
27
+ <strong>Your terminal agent. Your Python API.</strong>
28
+ </p>
29
+
30
+ <p align="center">
31
+ <a href="https://github.com/cheenulabs/pi-agent-python-sdk">
32
+ <img alt="Pi Agent Python SDK" src="https://raw.githubusercontent.com/cheenulabs/pi-agent-python-sdk/v0.2.3/docs/logo.svg" width="160">
33
+ </a>
34
+ </p>
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/pi-agent-python-sdk.svg?style=flat-square)][pypi]
27
37
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square)][metadata]
28
38
  [![MIT license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)][license]
29
39
 
30
- **Use your installed Pi coding agent as a Python API. Stream responses,
31
- continue conversations, and control sessions from Python.**
40
+ [Pi](https://github.com/earendil-works/pi) is the coding agent that runs in your
41
+ terminal. This unofficial community SDK puts *your* Pi behind a typed sync and
42
+ async Python API — run prompts, stream events, and keep sessions going without
43
+ leaving Python.
32
44
 
33
- A community Python SDK for [Pi coding agent](https://github.com/earendil-works/pi).
34
- It starts Pi as a subprocess and exposes the same agent runtime through a typed
35
- Python interface for RPC commands, streaming, and session control.
45
+ <!-- Inline player: GitHub only auto-embeds bare user-attachments URLs
46
+ (repo-relative / raw / release URLs are stripped or become download links). -->
47
+ https://github.com/user-attachments/assets/c64dde3a-e812-42d4-a005-aa60df861e92
36
48
 
37
- > Requires Python **3.11+** and a separate Pi installation. The tested protocol
38
- > baseline is **Pi 0.86.0**; see [compatibility][compatibility] for version and
39
- > platform scope.
40
-
41
- [Quick start](#quick-start) · [Streaming](#streaming) · [Async](#async-usage) ·
42
- [Configuration](#configuration) · [Thinking](#models-and-thinking) ·
43
- [RPC](#rpc-access) · [Documentation](#documentation)
49
+ ```python
50
+ from pi_agent import PiClient
44
51
 
45
- ## Why use this?
52
+ with PiClient() as pi:
53
+ print(pi.run("Explain the current project without changing files.").text)
54
+ ```
46
55
 
47
- If you already use Pi from the terminal, this SDK lets you use the same agent
48
- from Python code. Instead of managing a separate implementation, you can start Pi
49
- in RPC mode and call it as a programmatic API from scripts, apps, services, or
50
- background jobs.
56
+ Requires an installed, configured Pi CLI — see [Prerequisites](#prerequisites).
51
57
 
52
- This is useful when you want to:
58
+ [Prerequisites](#prerequisites) · [Quick start](#quick-start) ·
59
+ [Streaming](#streaming) · [Async](#async-usage) ·
60
+ [Configuration](#configuration) · [Thinking](#models-and-thinking) ·
61
+ [RPC](#rpc-access) · [Documentation](#documentation) ·
62
+ [Troubleshooting](#troubleshooting) · [Support](#support)
53
63
 
54
- - Add Pi-powered coding or reasoning to a Python application.
55
- - Automate project analysis, refactors, or repository workflows.
56
- - Build a service or CLI around Pi without reimplementing the agent runtime.
57
- - Stream text, thinking, tool, and session events in Python.
58
- - Reuse your existing Pi configuration, models, tools, and extensions.
64
+ ## What you can do
59
65
 
60
- The SDK keeps Pi's usual setup and model selection, while exposing a typed Python
61
- interface for prompts, follow-ups, model control, and RPC access. It launches Pi
62
- as a subprocess and communicates over its JSONL protocol; it does not attach to
63
- an already-running interactive Pi terminal session.
66
+ - **Run prompts from Python** — get a final answer, or stream text, thinking, and
67
+ tool events as they happen.
68
+ - **Keep conversations going** — send follow-ups on the same client, or resume,
69
+ fork, and clone sessions.
70
+ - **Control the agent** — switch models, set thinking level, steer mid-run, and
71
+ compact context.
72
+ - **Reuse your Pi setup** — same configuration, tools, and extensions as the CLI,
73
+ with sync or async clients.
64
74
 
65
- ## What you can do
75
+ ## Prerequisites
66
76
 
67
- - **Run and stream:** get a final answer or consume text, thinking, and tool events.
68
- - **Keep a conversation:** send follow-up prompts, resume sessions, fork, or clone.
69
- - **Control Pi:** select models, adjust thinking, steer work, compact context, and
70
- call all 33 RPC commands in the pinned baseline.
71
- - **Integrate with your application:** use sync or async clients, typed results,
72
- raw event dictionaries, and extension UI callbacks.
77
+ | Requirement | Notes |
78
+ | --- | --- |
79
+ | Python **3.11+** | Package metadata requires 3.11 or newer |
80
+ | Node.js **22.19.0+** | Required by Pi 0.87.0 |
81
+ | Pi **0.85.1+** | Minimum accepted version; **0.87.0** is the tested baseline |
82
+ | Model provider | Required; configure with the Pi CLI (see Quick start) |
73
83
 
74
84
  ## Quick start
75
85
 
76
- Install Pi with Node.js **22.19.0 or newer**:
86
+ Install Pi with Node.js **22.19.0 or newer** (pin matches the tested baseline):
77
87
 
78
88
  ```sh
79
- npm install -g @earendil-works/pi-coding-agent@0.86.0
89
+ npm install -g @earendil-works/pi-coding-agent@0.87.0
80
90
  pi --version
81
91
  ```
82
92
 
83
- Run `pi` once to configure your provider and model using Pi's normal setup.
84
- The SDK uses that configuration when it starts Pi.
93
+ Run `pi` once to configure your provider and model. The SDK uses that
94
+ configuration when it starts Pi.
85
95
 
86
96
  Install the Python package from [PyPI][pypi]:
87
97
 
88
98
  ```sh
89
- python -m pip install pi-agent-python-sdk==0.2.1
99
+ python -m pip install pi-agent-python-sdk
90
100
  ```
91
101
 
92
102
  The distribution is named `pi-agent-python-sdk`; import it as
@@ -101,8 +111,8 @@ with PiClient() as pi:
101
111
  ```
102
112
 
103
113
  The context manager starts and closes Pi. `run()` waits for the conversation to
104
- settle, including retries and queued follow-ups. The result includes finalized
105
- messages, session identity, elapsed time, and observed assistant usage.
114
+ settle. The result includes finalized messages, session identity, elapsed time,
115
+ and observed assistant usage.
106
116
 
107
117
  Continue the conversation with another call on the same client:
108
118
 
@@ -131,9 +141,15 @@ with PiClient() as pi:
131
141
  print(f"\nStop reason: {result.stop_reason}")
132
142
  ```
133
143
 
144
+ <details>
145
+ <summary>Streaming details</summary>
146
+
134
147
  `event.raw` contains the full Pi event, including fields the SDK does not yet
135
- recognize. Keep the stream inside its context: leaving early cleans up unfinished work. See [errors and cancellation][errors] for
136
- handling timeouts and partial results.
148
+ recognize. Keep the stream inside its context: leaving early cleans up unfinished
149
+ work. See [errors and cancellation][errors] for handling timeouts and partial
150
+ results.
151
+
152
+ </details>
137
153
 
138
154
  ## Async usage
139
155
 
@@ -156,8 +172,13 @@ if __name__ == "__main__":
156
172
  asyncio.run(main())
157
173
  ```
158
174
 
159
- Async streaming uses `async with pi.stream(...)`, `async for event in stream`,
160
- and `await stream.result()`. See the [complete streaming example][stream-example].
175
+ <details>
176
+ <summary>Async streaming</summary>
177
+
178
+ Use `async with pi.stream(...)`, `async for event in stream`, and
179
+ `await stream.result()`. See the [complete streaming example][stream-example].
180
+
181
+ </details>
161
182
 
162
183
  ## Configuration
163
184
 
@@ -171,46 +192,70 @@ with PiClient(cwd=".", no_session=True) as pi:
171
192
  ```
172
193
 
173
194
  Leave `no_session` unset to preserve Pi's normal session persistence. Use
174
- `provider=` and `model=` to override Pi's configured model, or `session=` to open
175
- an existing session. See [constructor options][constructor-options]
176
- for environment overrides, executable paths, and deadlines.
195
+ `session=` to open an existing session. For model overrides, see
196
+ [Models and thinking](#models-and-thinking).
197
+
198
+ <details>
199
+ <summary>More configuration</summary>
200
+
201
+ See [constructor options][constructor-options] for environment overrides,
202
+ executable paths, and deadlines.
177
203
 
178
204
  Extensions are installed and configured through Pi. To load your own extension,
179
- pass `extra_args=["--extension", "/absolute/path/to/your-extension.ts"]` to either
180
- client. See [using your own extensions][extensions]
181
- for details and [extension UI][ui-example] for an interactive example.
205
+ pass `extra_args=["--extension", "/absolute/path/to/your-extension.ts"]` to
206
+ either client. See [using your own extensions][extensions] for details and
207
+ [extension UI][ui-example] for an interactive example.
208
+
209
+ </details>
182
210
 
183
211
  ## Models and thinking
184
212
 
185
- The SDK uses Pi's configured model unless you pass `provider=` and `model=`.
186
- Pi determines which thinking levels that model supports; query them before
187
- choosing a level:
213
+ By default the SDK uses whatever model you already configured in the Pi CLI.
214
+ Override it when you construct the client, or switch later on the same client.
215
+ Set the model and thinking level **before** `run()` or `stream()`.
216
+
217
+ Select a model at construction:
218
+
219
+ ```python
220
+ from pi_agent import PiClient
221
+
222
+ with PiClient(provider="openai", model="gpt-5") as pi:
223
+ print(pi.run("Summarise this repo without changing files.").text)
224
+ ```
225
+
226
+ Thinking levels depend on the current model. Query them, set one, then confirm
227
+ what Pi actually applied:
188
228
 
189
229
  ```python
190
230
  from pi_agent import PiClient
191
231
 
192
232
  with PiClient() as pi:
193
233
  levels = pi.get_available_thinking_levels()
194
- print("Supported thinking levels:", levels)
234
+ print("available:", levels)
195
235
  if "high" in levels:
196
236
  pi.set_thinking_level("high")
197
- print("Effective thinking level:", pi.get_state()["thinkingLevel"])
198
-
199
- result = pi.run("Analyse this project's architecture without changing files.")
200
- print(result.text)
237
+ print("effective:", pi.get_state()["thinkingLevel"])
238
+ print(pi.run("Analyse this project's architecture without changing files.").text)
201
239
  ```
202
240
 
203
- Set the model and thinking level before starting `run()` or `stream()`. Pi can
204
- adjust unsupported level requests, so read `get_state()["thinkingLevel"]` for the
205
- effective value. The setting applies to subsequent prompts in the current
206
- session without changing global defaults. With `AsyncPiClient`, await the same
207
- methods. See the [model and thinking commands][thinking-reference] for cycling
208
- levels and other controls.
241
+ Replace the example provider/model ids with ones from your Pi install
242
+ (`pi --list-models` or `get_available_models()`).
243
+
244
+ <details>
245
+ <summary>List models and switch after start</summary>
209
246
 
210
- The thinking level controls Pi's reasoning setting. Thinking **output** is
211
- separate: when Pi emits it, deltas are available in `event.raw` during streaming
212
- and finalized thinking blocks in `result.messages`. `result.text` contains the
213
- final assistant text, not thinking.
247
+ ```python
248
+ from pi_agent import PiClient
249
+
250
+ with PiClient() as pi:
251
+ for model in pi.get_available_models():
252
+ print(model["provider"], model["id"])
253
+ selected = pi.set_model("openai", "gpt-5")
254
+ print("using", selected["provider"], selected["id"])
255
+ print(pi.run("Summarise this repo without changing files.").text)
256
+ ```
257
+
258
+ </details>
214
259
 
215
260
  ## RPC access
216
261
 
@@ -223,7 +268,18 @@ Your Python application
223
268
  Models · tools · extensions · sessions
224
269
  ```
225
270
 
226
- Choose the interface that fits the work:
271
+ Inspect session state without starting a model run:
272
+
273
+ ```python
274
+ from pi_agent import PiClient
275
+
276
+ with PiClient() as pi:
277
+ state = pi.get_state()
278
+ print(state["sessionId"])
279
+ ```
280
+
281
+ <details>
282
+ <summary>Which interface to use</summary>
227
283
 
228
284
  | You need | Use |
229
285
  | --- | --- |
@@ -235,29 +291,20 @@ Choose the interface that fits the work:
235
291
  | A specific Pi operation | `get_state()`, `set_model()`, `fork()`, and other command methods |
236
292
  | A raw command response envelope | `request()` |
237
293
 
238
- For example, inspect session state without starting a model run:
239
-
240
- ```python
241
- from pi_agent import PiClient
242
-
243
- with PiClient() as pi:
244
- state = pi.get_state()
245
- print(state["sessionId"])
246
- ```
247
-
248
294
  Python arguments use `snake_case`; wire dictionaries retain Pi's `camelCase`
249
295
  fields. `prompt()` acknowledges submission, which may be handled entirely by an
250
296
  extension. It does not wait for a completed answer.
251
297
 
252
- Use one client per conversation and wait for each `run()` or stream to finish
253
- before starting the next. After using `prompt()` for your own event handling,
254
- use a fresh client for `run()` or `stream()` so results cannot include delayed
255
- events from earlier work. The SDK launches a new process and cannot attach to an existing
256
- Pi terminal session.
298
+ Each client starts its own Pi subprocess. Use one client per conversation and
299
+ wait for each `run()` or stream to finish before starting the next. After using
300
+ `prompt()` for your own event handling, use a fresh client for `run()` or
301
+ `stream()` so results cannot include delayed events from earlier work.
257
302
 
258
303
  See [RPC structure][rpc] for the protocol mapping and module layout, and
259
304
  the [command reference][commands] for every method.
260
305
 
306
+ </details>
307
+
261
308
  ## Documentation
262
309
 
263
310
  | Guide | Contents |
@@ -268,36 +315,46 @@ the [command reference][commands] for every method.
268
315
  | [RPC structure][rpc] | How the Python client maps to Pi's protocol |
269
316
  | [Compatibility][compatibility] | Runtime versions and platform validation |
270
317
  | [Examples][examples] | Runnable sync, async, streaming, sessions, steering, and UI examples |
271
- | [Release notes][release] | Changes included in 0.2.1 |
318
+ | [Release notes][release] | Changes included in 0.2.3 |
272
319
 
273
320
  Examples use your configured Pi and may make provider calls. Development checks
274
321
  use an isolated local test provider; see [CONTRIBUTING.md][contributing] for
275
322
  setup and validation commands.
276
323
 
277
- Maintainers: [protocol discovery][discovery] ·
278
- [maintenance][maintenance] · [release checklist][releasing].
324
+ ## Troubleshooting
325
+
326
+ | Symptom | What to check |
327
+ | --- | --- |
328
+ | `pi` not found / launch fails | Ensure the Pi CLI is on `PATH`, or pass `executable=` to the client |
329
+ | Version rejected | Upgrade to Pi **0.85.1+**; prefer the tested baseline **0.87.0** |
330
+ | No model / auth errors | Run `pi` once and complete provider setup |
331
+ | Unexpected file or command changes | Review Pi tool/extension settings before automation |
332
+
333
+ ## Support
334
+
335
+ - [Open an issue][issues] for bugs, questions, or compatibility reports.
336
+ - See [CONTRIBUTING.md][contributing] for local setup and pull requests.
337
+ - For security reports, open a [private security advisory][security].
279
338
 
280
339
  ## License
281
340
 
282
341
  [MIT][license]
283
342
 
284
343
  [pypi]: https://pypi.org/project/pi-agent-python-sdk/
285
- [release]: https://github.com/cheenulabs/pi-agent-python-sdk/releases/tag/v0.2.1
286
- [metadata]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/pyproject.toml
287
- [license]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/LICENSE
288
- [compatibility]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/compatibility.md
289
- [errors]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/errors.md
290
- [stream-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/examples/stream.py
291
- [constructor-options]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md#constructor-options
292
- [extensions]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/usage.md#using-your-own-extensions
293
- [ui-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/examples/ui.py
294
- [rpc]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/rpc.md
295
- [commands]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md#all-33-rpc-commands
296
- [thinking-reference]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md#models-state-and-compaction
297
- [usage]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/usage.md
298
- [api]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md
299
- [examples]: https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.1/examples
300
- [contributing]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/CONTRIBUTING.md
301
- [discovery]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/discovery.md
302
- [maintenance]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/maintenance.md
303
- [releasing]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/releasing.md
344
+ [release]: https://github.com/cheenulabs/pi-agent-python-sdk/releases/tag/v0.2.3
345
+ [metadata]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/pyproject.toml
346
+ [license]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/LICENSE
347
+ [compatibility]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/compatibility.md
348
+ [errors]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/errors.md
349
+ [stream-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/examples/stream.py
350
+ [constructor-options]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/api.md#constructor-options
351
+ [extensions]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/usage.md#using-your-own-extensions
352
+ [ui-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/examples/ui.py
353
+ [rpc]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/rpc.md
354
+ [commands]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/api.md#all-33-rpc-commands
355
+ [usage]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/usage.md
356
+ [api]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/api.md
357
+ [examples]: https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.3/examples
358
+ [contributing]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/CONTRIBUTING.md
359
+ [issues]: https://github.com/cheenulabs/pi-agent-python-sdk/issues
360
+ [security]: https://github.com/cheenulabs/pi-agent-python-sdk/security/advisories/new
@@ -0,0 +1,337 @@
1
+ # Pi Agent Python SDK
2
+
3
+ <p align="center">
4
+ <strong>Your terminal agent. Your Python API.</strong>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <a href="https://github.com/cheenulabs/pi-agent-python-sdk">
9
+ <img alt="Pi Agent Python SDK" src="https://raw.githubusercontent.com/cheenulabs/pi-agent-python-sdk/v0.2.3/docs/logo.svg" width="160">
10
+ </a>
11
+ </p>
12
+
13
+ [![PyPI](https://img.shields.io/pypi/v/pi-agent-python-sdk.svg?style=flat-square)][pypi]
14
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square)][metadata]
15
+ [![MIT license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)][license]
16
+
17
+ [Pi](https://github.com/earendil-works/pi) is the coding agent that runs in your
18
+ terminal. This unofficial community SDK puts *your* Pi behind a typed sync and
19
+ async Python API — run prompts, stream events, and keep sessions going without
20
+ leaving Python.
21
+
22
+ <!-- Inline player: GitHub only auto-embeds bare user-attachments URLs
23
+ (repo-relative / raw / release URLs are stripped or become download links). -->
24
+ https://github.com/user-attachments/assets/c64dde3a-e812-42d4-a005-aa60df861e92
25
+
26
+ ```python
27
+ from pi_agent import PiClient
28
+
29
+ with PiClient() as pi:
30
+ print(pi.run("Explain the current project without changing files.").text)
31
+ ```
32
+
33
+ Requires an installed, configured Pi CLI — see [Prerequisites](#prerequisites).
34
+
35
+ [Prerequisites](#prerequisites) · [Quick start](#quick-start) ·
36
+ [Streaming](#streaming) · [Async](#async-usage) ·
37
+ [Configuration](#configuration) · [Thinking](#models-and-thinking) ·
38
+ [RPC](#rpc-access) · [Documentation](#documentation) ·
39
+ [Troubleshooting](#troubleshooting) · [Support](#support)
40
+
41
+ ## What you can do
42
+
43
+ - **Run prompts from Python** — get a final answer, or stream text, thinking, and
44
+ tool events as they happen.
45
+ - **Keep conversations going** — send follow-ups on the same client, or resume,
46
+ fork, and clone sessions.
47
+ - **Control the agent** — switch models, set thinking level, steer mid-run, and
48
+ compact context.
49
+ - **Reuse your Pi setup** — same configuration, tools, and extensions as the CLI,
50
+ with sync or async clients.
51
+
52
+ ## Prerequisites
53
+
54
+ | Requirement | Notes |
55
+ | --- | --- |
56
+ | Python **3.11+** | Package metadata requires 3.11 or newer |
57
+ | Node.js **22.19.0+** | Required by Pi 0.87.0 |
58
+ | Pi **0.85.1+** | Minimum accepted version; **0.87.0** is the tested baseline |
59
+ | Model provider | Required; configure with the Pi CLI (see Quick start) |
60
+
61
+ ## Quick start
62
+
63
+ Install Pi with Node.js **22.19.0 or newer** (pin matches the tested baseline):
64
+
65
+ ```sh
66
+ npm install -g @earendil-works/pi-coding-agent@0.87.0
67
+ pi --version
68
+ ```
69
+
70
+ Run `pi` once to configure your provider and model. The SDK uses that
71
+ configuration when it starts Pi.
72
+
73
+ Install the Python package from [PyPI][pypi]:
74
+
75
+ ```sh
76
+ python -m pip install pi-agent-python-sdk
77
+ ```
78
+
79
+ The distribution is named `pi-agent-python-sdk`; import it as
80
+ `pi_agent`.
81
+
82
+ ```python
83
+ from pi_agent import PiClient
84
+
85
+ with PiClient() as pi:
86
+ result = pi.run("Explain the current project without changing files.")
87
+ print(result.text)
88
+ ```
89
+
90
+ The context manager starts and closes Pi. `run()` waits for the conversation to
91
+ settle. The result includes finalized messages, session identity, elapsed time,
92
+ and observed assistant usage.
93
+
94
+ Continue the conversation with another call on the same client:
95
+
96
+ ```python
97
+ from pi_agent import PiClient
98
+
99
+ with PiClient() as pi:
100
+ print(pi.run("Explain this project's entry points without editing files.").text)
101
+ print(pi.run("Which of those entry points handles configuration?").text)
102
+ ```
103
+
104
+ Pi keeps the conversation context. Each result contains only the messages and
105
+ answer from that call; a call with no assistant output has empty text.
106
+
107
+ ## Streaming
108
+
109
+ ```python
110
+ from pi_agent import PiClient
111
+
112
+ with PiClient() as pi:
113
+ with pi.stream("Explain this project's entry points without editing files.") as stream:
114
+ for event in stream:
115
+ if event.text_delta is not None:
116
+ print(event.text_delta, end="", flush=True)
117
+ result = stream.result()
118
+ print(f"\nStop reason: {result.stop_reason}")
119
+ ```
120
+
121
+ <details>
122
+ <summary>Streaming details</summary>
123
+
124
+ `event.raw` contains the full Pi event, including fields the SDK does not yet
125
+ recognize. Keep the stream inside its context: leaving early cleans up unfinished
126
+ work. See [errors and cancellation][errors] for handling timeouts and partial
127
+ results.
128
+
129
+ </details>
130
+
131
+ ## Async usage
132
+
133
+ Use `AsyncPiClient` in applications that already run an event loop. Command
134
+ arguments and results match the synchronous client.
135
+
136
+ ```python
137
+ import asyncio
138
+
139
+ from pi_agent import AsyncPiClient
140
+
141
+
142
+ async def main() -> None:
143
+ async with AsyncPiClient() as pi:
144
+ result = await pi.run("Explain the current project without changing files.")
145
+ print(result.text)
146
+
147
+
148
+ if __name__ == "__main__":
149
+ asyncio.run(main())
150
+ ```
151
+
152
+ <details>
153
+ <summary>Async streaming</summary>
154
+
155
+ Use `async with pi.stream(...)`, `async for event in stream`, and
156
+ `await stream.result()`. See the [complete streaming example][stream-example].
157
+
158
+ </details>
159
+
160
+ ## Configuration
161
+
162
+ Choose the project directory and whether to save the conversation:
163
+
164
+ ```python
165
+ from pi_agent import PiClient
166
+
167
+ with PiClient(cwd=".", no_session=True) as pi:
168
+ print(pi.run("Describe this project without changing files.").text)
169
+ ```
170
+
171
+ Leave `no_session` unset to preserve Pi's normal session persistence. Use
172
+ `session=` to open an existing session. For model overrides, see
173
+ [Models and thinking](#models-and-thinking).
174
+
175
+ <details>
176
+ <summary>More configuration</summary>
177
+
178
+ See [constructor options][constructor-options] for environment overrides,
179
+ executable paths, and deadlines.
180
+
181
+ Extensions are installed and configured through Pi. To load your own extension,
182
+ pass `extra_args=["--extension", "/absolute/path/to/your-extension.ts"]` to
183
+ either client. See [using your own extensions][extensions] for details and
184
+ [extension UI][ui-example] for an interactive example.
185
+
186
+ </details>
187
+
188
+ ## Models and thinking
189
+
190
+ By default the SDK uses whatever model you already configured in the Pi CLI.
191
+ Override it when you construct the client, or switch later on the same client.
192
+ Set the model and thinking level **before** `run()` or `stream()`.
193
+
194
+ Select a model at construction:
195
+
196
+ ```python
197
+ from pi_agent import PiClient
198
+
199
+ with PiClient(provider="openai", model="gpt-5") as pi:
200
+ print(pi.run("Summarise this repo without changing files.").text)
201
+ ```
202
+
203
+ Thinking levels depend on the current model. Query them, set one, then confirm
204
+ what Pi actually applied:
205
+
206
+ ```python
207
+ from pi_agent import PiClient
208
+
209
+ with PiClient() as pi:
210
+ levels = pi.get_available_thinking_levels()
211
+ print("available:", levels)
212
+ if "high" in levels:
213
+ pi.set_thinking_level("high")
214
+ print("effective:", pi.get_state()["thinkingLevel"])
215
+ print(pi.run("Analyse this project's architecture without changing files.").text)
216
+ ```
217
+
218
+ Replace the example provider/model ids with ones from your Pi install
219
+ (`pi --list-models` or `get_available_models()`).
220
+
221
+ <details>
222
+ <summary>List models and switch after start</summary>
223
+
224
+ ```python
225
+ from pi_agent import PiClient
226
+
227
+ with PiClient() as pi:
228
+ for model in pi.get_available_models():
229
+ print(model["provider"], model["id"])
230
+ selected = pi.set_model("openai", "gpt-5")
231
+ print("using", selected["provider"], selected["id"])
232
+ print(pi.run("Summarise this repo without changing files.").text)
233
+ ```
234
+
235
+ </details>
236
+
237
+ ## RPC access
238
+
239
+ The SDK speaks Pi's existing JSONL protocol over stdin/stdout:
240
+
241
+ ```text
242
+ Your Python application
243
+ PiClient / AsyncPiClient
244
+ pi --mode rpc
245
+ Models · tools · extensions · sessions
246
+ ```
247
+
248
+ Inspect session state without starting a model run:
249
+
250
+ ```python
251
+ from pi_agent import PiClient
252
+
253
+ with PiClient() as pi:
254
+ state = pi.get_state()
255
+ print(state["sessionId"])
256
+ ```
257
+
258
+ <details>
259
+ <summary>Which interface to use</summary>
260
+
261
+ | You need | Use |
262
+ | --- | --- |
263
+ | A completed conversation result | `run()` |
264
+ | Events while a conversation runs | `stream()` |
265
+ | Prompt acknowledgement and your own event handling | `prompt()` with `events()` |
266
+ | Session events through the next settlement | `prompt_and_wait()` or `collect_events()` |
267
+ | Every event without retaining history | `on_event(callback)` |
268
+ | A specific Pi operation | `get_state()`, `set_model()`, `fork()`, and other command methods |
269
+ | A raw command response envelope | `request()` |
270
+
271
+ Python arguments use `snake_case`; wire dictionaries retain Pi's `camelCase`
272
+ fields. `prompt()` acknowledges submission, which may be handled entirely by an
273
+ extension. It does not wait for a completed answer.
274
+
275
+ Each client starts its own Pi subprocess. Use one client per conversation and
276
+ wait for each `run()` or stream to finish before starting the next. After using
277
+ `prompt()` for your own event handling, use a fresh client for `run()` or
278
+ `stream()` so results cannot include delayed events from earlier work.
279
+
280
+ See [RPC structure][rpc] for the protocol mapping and module layout, and
281
+ the [command reference][commands] for every method.
282
+
283
+ </details>
284
+
285
+ ## Documentation
286
+
287
+ | Guide | Contents |
288
+ | --- | --- |
289
+ | [Usage][usage] | Sessions, images, concurrency, events, and extension UI |
290
+ | [API reference][api] | Constructors, commands, types, and results |
291
+ | [Errors and cancellation][errors] | Deadlines, partial results, cleanup, and recovery |
292
+ | [RPC structure][rpc] | How the Python client maps to Pi's protocol |
293
+ | [Compatibility][compatibility] | Runtime versions and platform validation |
294
+ | [Examples][examples] | Runnable sync, async, streaming, sessions, steering, and UI examples |
295
+ | [Release notes][release] | Changes included in 0.2.3 |
296
+
297
+ Examples use your configured Pi and may make provider calls. Development checks
298
+ use an isolated local test provider; see [CONTRIBUTING.md][contributing] for
299
+ setup and validation commands.
300
+
301
+ ## Troubleshooting
302
+
303
+ | Symptom | What to check |
304
+ | --- | --- |
305
+ | `pi` not found / launch fails | Ensure the Pi CLI is on `PATH`, or pass `executable=` to the client |
306
+ | Version rejected | Upgrade to Pi **0.85.1+**; prefer the tested baseline **0.87.0** |
307
+ | No model / auth errors | Run `pi` once and complete provider setup |
308
+ | Unexpected file or command changes | Review Pi tool/extension settings before automation |
309
+
310
+ ## Support
311
+
312
+ - [Open an issue][issues] for bugs, questions, or compatibility reports.
313
+ - See [CONTRIBUTING.md][contributing] for local setup and pull requests.
314
+ - For security reports, open a [private security advisory][security].
315
+
316
+ ## License
317
+
318
+ [MIT][license]
319
+
320
+ [pypi]: https://pypi.org/project/pi-agent-python-sdk/
321
+ [release]: https://github.com/cheenulabs/pi-agent-python-sdk/releases/tag/v0.2.3
322
+ [metadata]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/pyproject.toml
323
+ [license]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/LICENSE
324
+ [compatibility]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/compatibility.md
325
+ [errors]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/errors.md
326
+ [stream-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/examples/stream.py
327
+ [constructor-options]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/api.md#constructor-options
328
+ [extensions]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/usage.md#using-your-own-extensions
329
+ [ui-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/examples/ui.py
330
+ [rpc]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/rpc.md
331
+ [commands]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/api.md#all-33-rpc-commands
332
+ [usage]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/usage.md
333
+ [api]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/docs/api.md
334
+ [examples]: https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.3/examples
335
+ [contributing]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/CONTRIBUTING.md
336
+ [issues]: https://github.com/cheenulabs/pi-agent-python-sdk/issues
337
+ [security]: https://github.com/cheenulabs/pi-agent-python-sdk/security/advisories/new
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pi-agent-python-sdk"
7
- version = "0.2.1"
7
+ version = "0.2.3"
8
8
  description = "Typed async and synchronous Python clients for Pi's agent RPC runtime"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -26,9 +26,9 @@ dependencies = []
26
26
 
27
27
  [project.urls]
28
28
  Homepage = "https://github.com/cheenulabs/pi-agent-python-sdk"
29
- Documentation = "https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.1/docs"
29
+ Documentation = "https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.3/docs"
30
30
  Issues = "https://github.com/cheenulabs/pi-agent-python-sdk/issues"
31
- Changelog = "https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/CHANGELOG.md"
31
+ Changelog = "https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.3/CHANGELOG.md"
32
32
 
33
33
  [dependency-groups]
34
34
  dev = [
@@ -13,7 +13,7 @@ from ._transport import _close_process_pipes, _wait_for_exit
13
13
  from .errors import PiProcessError, PiVersionError
14
14
 
15
15
  MINIMUM_PI_VERSION = "0.85.1"
16
- TESTED_PI_VERSION = "0.86.0"
16
+ TESTED_PI_VERSION = "0.87.0"
17
17
 
18
18
  # The client owns these options. In particular --print consumes an initial prompt.
19
19
  _RESERVED = {
@@ -1,4 +1,4 @@
1
- """Pi 0.86.0 wire annotations and small Python conveniences.
1
+ """Pi 0.87.0 wire annotations and small Python conveniences.
2
2
 
3
3
  Wire fields retain Pi's spelling and remain ordinary dictionaries. These
4
4
  annotations describe known shapes, not a recursive runtime validator: extensions
@@ -221,6 +221,24 @@ class ModelPromptCache(TypedDict, total=False):
221
221
  long: float
222
222
 
223
223
 
224
+ class ModelImageResizeOptions(TypedDict, total=False):
225
+ maxWidth: float
226
+ maxHeight: float
227
+ maxBytes: float
228
+ jpegQuality: float
229
+
230
+
231
+ class ModelImageInputLimits(TypedDict, total=False):
232
+ resize: ModelImageResizeOptions
233
+ maxPerMessage: float
234
+ maxPerRequest: float
235
+
236
+
237
+ class ModelInputLimits(TypedDict, total=False):
238
+ maxRequestBytes: float
239
+ images: ModelImageInputLimits
240
+
241
+
224
242
  class Model(TypedDict):
225
243
  id: str
226
244
  name: str
@@ -230,6 +248,7 @@ class Model(TypedDict):
230
248
  reasoning: bool
231
249
  thinkingLevelMap: NotRequired[dict[ThinkingLevel, str | None]]
232
250
  input: list[Literal["text", "image"]]
251
+ inputLimits: NotRequired[ModelInputLimits]
233
252
  cost: ModelCost
234
253
  contextWindow: float
235
254
  maxTokens: float
@@ -378,6 +397,22 @@ class CustomMessageEntry(_EntryBase):
378
397
  display: bool
379
398
 
380
399
 
400
+ # Keep the list arms separate so typing rejects mixed ToolCall and ImageContent lists.
401
+ ContextEditableContent: TypeAlias = (
402
+ str | list[TextContent | ImageContent] | list[TextContent | ThinkingContent | ToolCall]
403
+ )
404
+
405
+
406
+ class ContextEditReplacement(TypedDict):
407
+ content: ContextEditableContent
408
+
409
+
410
+ class ContextEditEntry(_EntryBase):
411
+ type: Literal["context_edit"]
412
+ targetId: str
413
+ replacement: ContextEditReplacement | None
414
+
415
+
381
416
  class LabelEntry(_EntryBase):
382
417
  type: Literal["label"]
383
418
  targetId: str
@@ -398,6 +433,7 @@ SessionEntry: TypeAlias = (
398
433
  | BranchSummaryEntry
399
434
  | CustomEntry
400
435
  | CustomMessageEntry
436
+ | ContextEditEntry
401
437
  | LabelEntry
402
438
  | SessionInfoEntry
403
439
  )
@@ -1,280 +0,0 @@
1
- # Pi Agent Python SDK
2
-
3
- [![PyPI](https://badge.fury.io/py/pi-agent-python-sdk.svg)][pypi]
4
- [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square)][metadata]
5
- [![MIT license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)][license]
6
-
7
- **Use your installed Pi coding agent as a Python API. Stream responses,
8
- continue conversations, and control sessions from Python.**
9
-
10
- A community Python SDK for [Pi coding agent](https://github.com/earendil-works/pi).
11
- It starts Pi as a subprocess and exposes the same agent runtime through a typed
12
- Python interface for RPC commands, streaming, and session control.
13
-
14
- > Requires Python **3.11+** and a separate Pi installation. The tested protocol
15
- > baseline is **Pi 0.86.0**; see [compatibility][compatibility] for version and
16
- > platform scope.
17
-
18
- [Quick start](#quick-start) · [Streaming](#streaming) · [Async](#async-usage) ·
19
- [Configuration](#configuration) · [Thinking](#models-and-thinking) ·
20
- [RPC](#rpc-access) · [Documentation](#documentation)
21
-
22
- ## Why use this?
23
-
24
- If you already use Pi from the terminal, this SDK lets you use the same agent
25
- from Python code. Instead of managing a separate implementation, you can start Pi
26
- in RPC mode and call it as a programmatic API from scripts, apps, services, or
27
- background jobs.
28
-
29
- This is useful when you want to:
30
-
31
- - Add Pi-powered coding or reasoning to a Python application.
32
- - Automate project analysis, refactors, or repository workflows.
33
- - Build a service or CLI around Pi without reimplementing the agent runtime.
34
- - Stream text, thinking, tool, and session events in Python.
35
- - Reuse your existing Pi configuration, models, tools, and extensions.
36
-
37
- The SDK keeps Pi's usual setup and model selection, while exposing a typed Python
38
- interface for prompts, follow-ups, model control, and RPC access. It launches Pi
39
- as a subprocess and communicates over its JSONL protocol; it does not attach to
40
- an already-running interactive Pi terminal session.
41
-
42
- ## What you can do
43
-
44
- - **Run and stream:** get a final answer or consume text, thinking, and tool events.
45
- - **Keep a conversation:** send follow-up prompts, resume sessions, fork, or clone.
46
- - **Control Pi:** select models, adjust thinking, steer work, compact context, and
47
- call all 33 RPC commands in the pinned baseline.
48
- - **Integrate with your application:** use sync or async clients, typed results,
49
- raw event dictionaries, and extension UI callbacks.
50
-
51
- ## Quick start
52
-
53
- Install Pi with Node.js **22.19.0 or newer**:
54
-
55
- ```sh
56
- npm install -g @earendil-works/pi-coding-agent@0.86.0
57
- pi --version
58
- ```
59
-
60
- Run `pi` once to configure your provider and model using Pi's normal setup.
61
- The SDK uses that configuration when it starts Pi.
62
-
63
- Install the Python package from [PyPI][pypi]:
64
-
65
- ```sh
66
- python -m pip install pi-agent-python-sdk==0.2.1
67
- ```
68
-
69
- The distribution is named `pi-agent-python-sdk`; import it as
70
- `pi_agent`.
71
-
72
- ```python
73
- from pi_agent import PiClient
74
-
75
- with PiClient() as pi:
76
- result = pi.run("Explain the current project without changing files.")
77
- print(result.text)
78
- ```
79
-
80
- The context manager starts and closes Pi. `run()` waits for the conversation to
81
- settle, including retries and queued follow-ups. The result includes finalized
82
- messages, session identity, elapsed time, and observed assistant usage.
83
-
84
- Continue the conversation with another call on the same client:
85
-
86
- ```python
87
- from pi_agent import PiClient
88
-
89
- with PiClient() as pi:
90
- print(pi.run("Explain this project's entry points without editing files.").text)
91
- print(pi.run("Which of those entry points handles configuration?").text)
92
- ```
93
-
94
- Pi keeps the conversation context. Each result contains only the messages and
95
- answer from that call; a call with no assistant output has empty text.
96
-
97
- ## Streaming
98
-
99
- ```python
100
- from pi_agent import PiClient
101
-
102
- with PiClient() as pi:
103
- with pi.stream("Explain this project's entry points without editing files.") as stream:
104
- for event in stream:
105
- if event.text_delta is not None:
106
- print(event.text_delta, end="", flush=True)
107
- result = stream.result()
108
- print(f"\nStop reason: {result.stop_reason}")
109
- ```
110
-
111
- `event.raw` contains the full Pi event, including fields the SDK does not yet
112
- recognize. Keep the stream inside its context: leaving early cleans up unfinished work. See [errors and cancellation][errors] for
113
- handling timeouts and partial results.
114
-
115
- ## Async usage
116
-
117
- Use `AsyncPiClient` in applications that already run an event loop. Command
118
- arguments and results match the synchronous client.
119
-
120
- ```python
121
- import asyncio
122
-
123
- from pi_agent import AsyncPiClient
124
-
125
-
126
- async def main() -> None:
127
- async with AsyncPiClient() as pi:
128
- result = await pi.run("Explain the current project without changing files.")
129
- print(result.text)
130
-
131
-
132
- if __name__ == "__main__":
133
- asyncio.run(main())
134
- ```
135
-
136
- Async streaming uses `async with pi.stream(...)`, `async for event in stream`,
137
- and `await stream.result()`. See the [complete streaming example][stream-example].
138
-
139
- ## Configuration
140
-
141
- Choose the project directory and whether to save the conversation:
142
-
143
- ```python
144
- from pi_agent import PiClient
145
-
146
- with PiClient(cwd=".", no_session=True) as pi:
147
- print(pi.run("Describe this project without changing files.").text)
148
- ```
149
-
150
- Leave `no_session` unset to preserve Pi's normal session persistence. Use
151
- `provider=` and `model=` to override Pi's configured model, or `session=` to open
152
- an existing session. See [constructor options][constructor-options]
153
- for environment overrides, executable paths, and deadlines.
154
-
155
- Extensions are installed and configured through Pi. To load your own extension,
156
- pass `extra_args=["--extension", "/absolute/path/to/your-extension.ts"]` to either
157
- client. See [using your own extensions][extensions]
158
- for details and [extension UI][ui-example] for an interactive example.
159
-
160
- ## Models and thinking
161
-
162
- The SDK uses Pi's configured model unless you pass `provider=` and `model=`.
163
- Pi determines which thinking levels that model supports; query them before
164
- choosing a level:
165
-
166
- ```python
167
- from pi_agent import PiClient
168
-
169
- with PiClient() as pi:
170
- levels = pi.get_available_thinking_levels()
171
- print("Supported thinking levels:", levels)
172
- if "high" in levels:
173
- pi.set_thinking_level("high")
174
- print("Effective thinking level:", pi.get_state()["thinkingLevel"])
175
-
176
- result = pi.run("Analyse this project's architecture without changing files.")
177
- print(result.text)
178
- ```
179
-
180
- Set the model and thinking level before starting `run()` or `stream()`. Pi can
181
- adjust unsupported level requests, so read `get_state()["thinkingLevel"]` for the
182
- effective value. The setting applies to subsequent prompts in the current
183
- session without changing global defaults. With `AsyncPiClient`, await the same
184
- methods. See the [model and thinking commands][thinking-reference] for cycling
185
- levels and other controls.
186
-
187
- The thinking level controls Pi's reasoning setting. Thinking **output** is
188
- separate: when Pi emits it, deltas are available in `event.raw` during streaming
189
- and finalized thinking blocks in `result.messages`. `result.text` contains the
190
- final assistant text, not thinking.
191
-
192
- ## RPC access
193
-
194
- The SDK speaks Pi's existing JSONL protocol over stdin/stdout:
195
-
196
- ```text
197
- Your Python application
198
- PiClient / AsyncPiClient
199
- pi --mode rpc
200
- Models · tools · extensions · sessions
201
- ```
202
-
203
- Choose the interface that fits the work:
204
-
205
- | You need | Use |
206
- | --- | --- |
207
- | A completed conversation result | `run()` |
208
- | Events while a conversation runs | `stream()` |
209
- | Prompt acknowledgement and your own event handling | `prompt()` with `events()` |
210
- | Session events through the next settlement | `prompt_and_wait()` or `collect_events()` |
211
- | Every event without retaining history | `on_event(callback)` |
212
- | A specific Pi operation | `get_state()`, `set_model()`, `fork()`, and other command methods |
213
- | A raw command response envelope | `request()` |
214
-
215
- For example, inspect session state without starting a model run:
216
-
217
- ```python
218
- from pi_agent import PiClient
219
-
220
- with PiClient() as pi:
221
- state = pi.get_state()
222
- print(state["sessionId"])
223
- ```
224
-
225
- Python arguments use `snake_case`; wire dictionaries retain Pi's `camelCase`
226
- fields. `prompt()` acknowledges submission, which may be handled entirely by an
227
- extension. It does not wait for a completed answer.
228
-
229
- Use one client per conversation and wait for each `run()` or stream to finish
230
- before starting the next. After using `prompt()` for your own event handling,
231
- use a fresh client for `run()` or `stream()` so results cannot include delayed
232
- events from earlier work. The SDK launches a new process and cannot attach to an existing
233
- Pi terminal session.
234
-
235
- See [RPC structure][rpc] for the protocol mapping and module layout, and
236
- the [command reference][commands] for every method.
237
-
238
- ## Documentation
239
-
240
- | Guide | Contents |
241
- | --- | --- |
242
- | [Usage][usage] | Sessions, images, concurrency, events, and extension UI |
243
- | [API reference][api] | Constructors, commands, types, and results |
244
- | [Errors and cancellation][errors] | Deadlines, partial results, cleanup, and recovery |
245
- | [RPC structure][rpc] | How the Python client maps to Pi's protocol |
246
- | [Compatibility][compatibility] | Runtime versions and platform validation |
247
- | [Examples][examples] | Runnable sync, async, streaming, sessions, steering, and UI examples |
248
- | [Release notes][release] | Changes included in 0.2.1 |
249
-
250
- Examples use your configured Pi and may make provider calls. Development checks
251
- use an isolated local test provider; see [CONTRIBUTING.md][contributing] for
252
- setup and validation commands.
253
-
254
- Maintainers: [protocol discovery][discovery] ·
255
- [maintenance][maintenance] · [release checklist][releasing].
256
-
257
- ## License
258
-
259
- [MIT][license]
260
-
261
- [pypi]: https://pypi.org/project/pi-agent-python-sdk/
262
- [release]: https://github.com/cheenulabs/pi-agent-python-sdk/releases/tag/v0.2.1
263
- [metadata]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/pyproject.toml
264
- [license]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/LICENSE
265
- [compatibility]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/compatibility.md
266
- [errors]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/errors.md
267
- [stream-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/examples/stream.py
268
- [constructor-options]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md#constructor-options
269
- [extensions]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/usage.md#using-your-own-extensions
270
- [ui-example]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/examples/ui.py
271
- [rpc]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/rpc.md
272
- [commands]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md#all-33-rpc-commands
273
- [thinking-reference]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md#models-state-and-compaction
274
- [usage]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/usage.md
275
- [api]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/api.md
276
- [examples]: https://github.com/cheenulabs/pi-agent-python-sdk/tree/v0.2.1/examples
277
- [contributing]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/CONTRIBUTING.md
278
- [discovery]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/discovery.md
279
- [maintenance]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/maintenance.md
280
- [releasing]: https://github.com/cheenulabs/pi-agent-python-sdk/blob/v0.2.1/docs/releasing.md