agent-runtime-client 0.1.0__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,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-runtime-client
3
+ Version: 0.1.0
4
+ Summary: Python client for agent-runtime over the standard library: generate_object() with answers validated against a full JSON Schema, generate_text(), and the admin API.
5
+ Author-email: Petr Tripolsky <tripolskypetr@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/tripolskypetr/agent-runtime/tree/master/packages/python-sdk#readme
8
+ Project-URL: Repository, https://github.com/tripolskypetr/agent-runtime
9
+ Project-URL: Issues, https://github.com/tripolskypetr/agent-runtime/issues
10
+ Project-URL: Server, https://github.com/tripolskypetr/agent-runtime
11
+ Keywords: agent-runtime,sdk,client,ai-agent,ollama,structured-output,json-schema,generateObject,generateText
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: unicode
24
+ Requires-Dist: regex>=2024.4.16; extra == "unicode"
25
+ Dynamic: license-file
26
+
27
+ # agent-runtime-client
28
+
29
+ > Python client for [agent-runtime](https://github.com/tripolskypetr/agent-runtime) over the standard library. `generate_object()` returns an answer validated against your JSON Schema, `generate_text()` a plain text one, and the admin API gives you the transcripts.
30
+
31
+ agent-runtime is a REST harness for a [pi](https://github.com/earendil-works/pi)-based agent on Ollama: it runs the agent with `bash`, `read_file` and `provide_answer` tools in an isolated workspace and returns an answer that matches a JSON schema. This package is the Python counterpart of [agent-runtime-sdk](https://www.npmjs.com/package/agent-runtime-sdk) for Node.js: the same `generate_object()` / `generate_text()` pair, the same error model, the same admin API.
32
+
33
+ ## Features
34
+
35
+ - `generate_object()`: the answer as a `dict`, validated on the client with a port of the server's own validator; a mismatch runs the inference again
36
+ - `generate_text()`: plain text answers from the same agent
37
+ - `inference()` / `text()`: the full typed response (`status` is `"success"` or `"error"`) without raising
38
+ - `AgentRuntimeError` with the server's error code, per-attempt details and inference id
39
+ - Admin API: list and search inferences, read transcripts, download attachments, delete runs
40
+ - Zero dependencies, Python 3.9+, `urllib` underneath; the HTTP layer is replaceable for tests
41
+ - `format` is a full JSON Schema (draft 2020-12), enforced server-side by ajv
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ pip install agent-runtime-client
47
+ ```
48
+
49
+ Run the server first (see the [agent-runtime README](https://github.com/tripolskypetr/agent-runtime#quick-start-docker)):
50
+
51
+ ```bash
52
+ docker run -p 10050:10050 -e OLLAMA_API_KEY=... tripolskypetr/agent-runtime
53
+ ```
54
+
55
+ ## Quick start
56
+
57
+ ```python
58
+ from agent_runtime_client import encode_file, generate_object
59
+
60
+ report = generate_object(
61
+ [{"role": "user", "content": "sales.csv is in your working directory. Total the amount column and name the top city."}],
62
+ {
63
+ "type": "object",
64
+ "required": ["total", "topCity"],
65
+ "properties": {
66
+ "total": {"type": "number", "description": "Sum of the amount column"},
67
+ "topCity": {"type": "string", "enum": ["Berlin", "Paris"], "description": "City with the largest amount"},
68
+ "note": {"type": "string", "description": "Optional remark"},
69
+ },
70
+ },
71
+ files=[encode_file("sales.csv", "city,amount\nBerlin,120.5\nParis,340.25")],
72
+ )
73
+
74
+ report["total"] # 460.75
75
+ report["topCity"] # "Paris"
76
+ report.get("note") # None unless the model filled it
77
+ ```
78
+
79
+ The standalone functions create a client per call. Without `base_url` they talk to `AGENT_RUNTIME_URL` or `http://localhost:10050`:
80
+
81
+ ```python
82
+ from agent_runtime_client import generate_text
83
+
84
+ summary = generate_text(
85
+ [{"role": "user", "content": "Summarise notes.txt"}],
86
+ files=[encode_file("notes.txt", notes)],
87
+ base_url="http://agent-runtime:10050",
88
+ headers={"x-token": "…"},
89
+ timeout=600,
90
+ )
91
+ ```
92
+
93
+ Keyword arguments: `files`, `model`, `instructions`, `base_url`, `headers`, `timeout` (seconds, `None` waits as long as the inference takes), `validate_answer`, `max_attempts`, `transport`.
94
+
95
+ For many calls against one server, or for the admin API, keep a client:
96
+
97
+ ```python
98
+ from agent_runtime_client import create_client
99
+
100
+ client = create_client("http://localhost:10050")
101
+ report = client.generate_object(messages, format)
102
+ ```
103
+
104
+ ## Images
105
+
106
+ Images go into `messages[].images` as base64; the server shows them to the model in user messages and stores them in the history. They are not written to the workspace: to process a picture with bash or `read_file`, pass it in `files`.
107
+
108
+ ```python
109
+ from agent_runtime_client import generate_object, user_message
110
+
111
+ answer = generate_object(
112
+ [user_message("Describe the chart", [open("chart.png", "rb").read()])],
113
+ {"type": "object", "required": ["trend"], "properties": {"trend": {"type": "string", "enum": ["up", "down", "flat"]}}},
114
+ )
115
+ ```
116
+
117
+ Records of a `wwwroot.jsonl` file (`{"role", "content", "images"}`) can be passed as `messages` directly.
118
+
119
+ ## Client-side validation and retries
120
+
121
+ The package ships a port of the server's answer validator ([validation.py](https://github.com/tripolskypetr/agent-runtime/blob/master/packages/python-sdk/src/agent_runtime_client/validation.py): a dependency-free JSON Schema checker with the same messages as the TypeScript original; its tests pin them against the shared fixtures). The client runs it on every successful answer, and when the answer does not match `format` it runs the inference again, up to `max_attempts` (3) times in total, then fails with `schema_mismatch` and one detail line per rejected inference. This should never trigger against a current server, which validates with the same logic; it is the client's guarantee that the value you get really matches the schema.
122
+
123
+ ```python
124
+ client = create_client(validate_answer=True, max_attempts=3) # defaults
125
+ generate_object(messages, format, validate_answer=False) # trust the server, no retries
126
+ ```
127
+
128
+ The validator is also exported for answers that come from somewhere else, e.g. a cache on disk after a schema change:
129
+
130
+ ```python
131
+ from agent_runtime_client import assert_answer, validate_answer
132
+
133
+ result = validate_answer(json.loads(cached), Agent1Format)
134
+ if result.success:
135
+ result.data["verdict"]
136
+ else:
137
+ print(result.error) # 'Invalid arguments: signal.direction: must be exactly "long"'
138
+
139
+ answer = assert_answer(json.loads(cached), Agent1Format) # raises ValueError instead
140
+ ```
141
+
142
+ Supported keywords: `type` (string or list, `null` included), `enum`, `const`, `oneOf` / `anyOf` / `allOf` / `not`, `if` / `then` / `else`, local `$ref`, `properties` / `required` / `additionalProperties` / `patternProperties` / `propertyNames` / `dependentRequired` / `dependentSchemas` / `minProperties` / `maxProperties`, `items` / `prefixItems` / `contains` / `uniqueItems` / `minItems` / `maxItems`, `minLength` / `maxLength` / `pattern`, `minimum` / `maximum` / `exclusiveMinimum` / `exclusiveMaximum` / `multipleOf`. `format` and unknown keywords are ignored, so the client is never stricter than the server. `pattern` is matched with Python's `re`; a pattern it cannot compile, such as `\p{L}` classes, is skipped unless the optional `regex` package is installed: `pip install agent-runtime-client[unicode]`.
143
+
144
+ ## Responses and errors
145
+
146
+ `generate_object()` and `generate_text()` raise `AgentRuntimeError` when the agent fails. `inference()` and `text()` return the typed response instead:
147
+
148
+ ```python
149
+ from agent_runtime_client import inference
150
+
151
+ response = inference(messages, format)
152
+ if response["status"] == "success":
153
+ response["data"] # the answer
154
+ response["turns"] # model calls
155
+ else:
156
+ response["error"]["code"] # "schema_mismatch" | "empty_answer" | "max_turns" | "timeout" | "provider_error" | "internal_error"
157
+ response["error"]["details"] # one line per failed attempt
158
+ response["id"] # look the transcript up in the admin UI
159
+ ```
160
+
161
+ ```python
162
+ from agent_runtime_client import AgentRuntimeError, generate_object
163
+
164
+ try:
165
+ generate_object(messages, format)
166
+ except AgentRuntimeError as error:
167
+ error.status # HTTP status, 0 for network failures
168
+ error.code # InferenceErrorCode ("invalid_format" for a rejected schema) or None for other HTTP errors
169
+ error.details # per-attempt details
170
+ error.inference_id # id of the stored run
171
+ ```
172
+
173
+ The server rejects a schema that does not compile (a `$ref` to a missing `$defs` entry) with HTTP 400 and `error.code = "invalid_format"`; no inference is recorded.
174
+
175
+ ## Admin API
176
+
177
+ ```python
178
+ listed = client.list_inferences(status="error", mode="object", limit=20)
179
+ found = client.list_inferences(q="Hilda") # case-insensitive substring search
180
+ record = client.get_inference(listed["items"][0]["id"]) # request, transcript, result/error, attachments
181
+ file = client.get_attachment(record["id"], record["attachments"][0]["id"]) # {...meta, "data": bytes}
182
+ client.delete_inference(record["id"])
183
+ client.health()
184
+ ```
185
+
186
+ ## Long inferences
187
+
188
+ An inference can run for minutes. The default `timeout` is `None`, so the client waits as long as the server takes; set `timeout` in seconds to bound a call. The HTTP layer is a plain function and can be replaced, for a `requests` session or a mock in tests:
189
+
190
+ ```python
191
+ def transport(method, url, headers, body, timeout):
192
+ response = session.request(method, url, headers=headers, data=body, timeout=timeout)
193
+ return response.status_code, dict(response.headers), response.content
194
+
195
+ client = create_client(transport=transport)
196
+ ```
197
+
198
+ ## API
199
+
200
+ Standalone functions (create the client internally):
201
+
202
+ | Function | Returns |
203
+ |----------|---------|
204
+ | `generate_object(messages, format, **kw)` | `dict` |
205
+ | `generate_text(messages, **kw)` | `str` |
206
+ | `inference(messages, format, **kw)` | `InferenceResponse` |
207
+ | `text(messages, **kw)` | `TextResponse` |
208
+
209
+ Client:
210
+
211
+ | Method | Returns |
212
+ |--------|---------|
213
+ | `create_client(base_url=None, **options)` / `AgentRuntimeClient(...)` | options: `headers`, `timeout`, `validate_answer` (True), `max_attempts` (3), `transport` |
214
+ | `generate_object(messages, format, *, files, model, instructions, headers, timeout)` | `dict` |
215
+ | `generate_text(messages, *, ...)` | `str` |
216
+ | `inference(...)` / `text(...)` | typed response |
217
+ | `health()` | `Health` |
218
+ | `list_inferences(*, limit, offset, status, mode, q)` | `{"total", "items"}` |
219
+ | `get_inference(id)` | `InferenceRecord` |
220
+ | `get_attachment(id, attachment_id)` | `Attachment` with `data: bytes` |
221
+ | `delete_inference(id)` | `{"ok": bool}` |
222
+ | `validate_answer(value, format)`, `assert_answer(value, format)` | client-side validation |
223
+ | `validate_tool_arguments(value, format)`, `validate_value(value, schema, path)` | the raw validator |
224
+ | `encode_file(name, bytes_or_str)`, `encode_image(bytes)`, `user_message(text, images=None)` | request helpers |
225
+
226
+ The response shapes are `TypedDict`s in `agent_runtime_client.models`.
227
+
228
+ ## Development
229
+
230
+ ```bash
231
+ python3 -m venv .venv && .venv/bin/pip install build twine
232
+ .venv/bin/python -m unittest discover -s tests -v # validator and client, no server
233
+ BASE_URL=http://localhost:10151 .venv/bin/python -m unittest tests.test_runtime -v # against a test server
234
+ .venv/bin/python -m build && .venv/bin/twine check dist/*
235
+ ```
236
+
237
+ ## License
238
+
239
+ MIT
@@ -0,0 +1,13 @@
1
+ agent_runtime_client/__init__.py,sha256=L4DFG4elna0b66j9C-2pnPyhzc_W2V3BaBeGXTxhATw,2204
2
+ agent_runtime_client/client.py,sha256=jpn0wKTdQKxRQQFBhQ5XPgxy6391wuDil3AWP036tiU,13934
3
+ agent_runtime_client/errors.py,sha256=Fh9LELn8BxZQ-So54QpM2uv4MdiwPWIAE0DY2-H6MGc,2096
4
+ agent_runtime_client/functions.py,sha256=md_V-YyIbHRVPdE5ETTgVuCYZIf4hSKobbclb21TNMo,3889
5
+ agent_runtime_client/helpers.py,sha256=ZeJqnyH4vV2wuF8ua8xde_m-yvh0Hvnc313fP08GYCc,1184
6
+ agent_runtime_client/models.py,sha256=Txx22HM_MtPVvJ-pr__1K_7EmGnQU9a0I07cZvp1Gck,3115
7
+ agent_runtime_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ agent_runtime_client/validation.py,sha256=-OJDtO6WzXel_IWpGe55aA-GtbynBrt42SjHbdW3nCQ,21934
9
+ agent_runtime_client-0.1.0.dist-info/licenses/LICENSE,sha256=Uhf1Tkbzxde181XZb65AhW1_VrDZVSOONM1nikPbyeY,1071
10
+ agent_runtime_client-0.1.0.dist-info/METADATA,sha256=IY1HNfLejpnYNyomZj5kz-fn5bkFe1Kl79wxMnWQ0e4,11689
11
+ agent_runtime_client-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
+ agent_runtime_client-0.1.0.dist-info/top_level.txt,sha256=27sXE1KAB-4N0AvhobyRESOTkMlx41ZSs9Ymj7Ey_jw,21
13
+ agent_runtime_client-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Petr Tripolsky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ agent_runtime_client