bynn-decisionnode 0.3.1__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.
- bynn_decisionnode-0.3.1/.gitignore +33 -0
- bynn_decisionnode-0.3.1/CHANGELOG.md +216 -0
- bynn_decisionnode-0.3.1/LICENSE +21 -0
- bynn_decisionnode-0.3.1/PKG-INFO +939 -0
- bynn_decisionnode-0.3.1/README.md +908 -0
- bynn_decisionnode-0.3.1/pyproject.toml +80 -0
- bynn_decisionnode-0.3.1/src/decisionnode/__init__.py +195 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_base.py +548 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_batches.py +333 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_boosters.py +553 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_client.py +2269 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_errors.py +218 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_images.py +283 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_questions.py +262 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_sessions.py +711 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_types.py +764 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_uploads.py +265 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_validate.py +934 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_version.py +5 -0
- bynn_decisionnode-0.3.1/src/decisionnode/_videos.py +197 -0
- bynn_decisionnode-0.3.1/src/decisionnode/py.typed +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
wheels/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
.coverage.*
|
|
21
|
+
htmlcov/
|
|
22
|
+
.tox/
|
|
23
|
+
|
|
24
|
+
# Local checkout of the conformance cases
|
|
25
|
+
/conformance/
|
|
26
|
+
|
|
27
|
+
# Editor files
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
30
|
+
*.swp
|
|
31
|
+
|
|
32
|
+
# OS noise
|
|
33
|
+
.DS_Store
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this package are listed here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.3.0] - Unreleased
|
|
8
|
+
|
|
9
|
+
Implements version 1.3 of the SDK specification (`SPEC_VERSION` is `"1.3"`).
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Several places in one answer: `point()` and `box()` take `max` (2 to 16), and the
|
|
14
|
+
answer is a `PointsAnswer` or `BoxesAnswer` (`count`, `present`, `complete`, `points`
|
|
15
|
+
of `PointPlace` or `boxes` of `BoxPlace`, `image`, `width`, `height`), told by its own
|
|
16
|
+
list, in the order and numbers sent. `max` (a whole number from 2 to 16) is checked
|
|
17
|
+
before sending; whether a model version answers several places is the API's to say.
|
|
18
|
+
- Boosters: `boosters` on `decide`, `check`, `classify`, `score`, `number`, `gate`,
|
|
19
|
+
`decide_many` requests, batch requests and `check_request`, as booster names,
|
|
20
|
+
`Booster(name, images, require, options)` objects (sent as `{"name": ...}` with each
|
|
21
|
+
setting only when given, or as the bare name when none is) or wire-form mappings, sent
|
|
22
|
+
in the order given. Left out or
|
|
23
|
+
empty, the request has no `boosters` field. The client checks nothing about boosters
|
|
24
|
+
itself; the API answers. Sessions take no boosters.
|
|
25
|
+
- `Decision.evidence`: booster name to `Evidence` (`version`, `status`, `latency_ms`,
|
|
26
|
+
`billed_calls`, `results`), with `AgeReading`, `AiGeneratedReading` (its `TopGenerator`, `None` unless
|
|
27
|
+
the reading says likely generated, `c2pa_signer`, and `c2pa`, `untrusted` for Content
|
|
28
|
+
Credentials that validate but are not trusted) and `UnknownReading` for a reading of a
|
|
29
|
+
booster this version does not know or with a field missing or of the wrong type. `None`
|
|
30
|
+
when the request named no boosters.
|
|
31
|
+
- `Decision.usage.booster_tokens` and `Decision.usage.boosters` (`None` when absent).
|
|
32
|
+
- The readings of the `ocr`, `face_detection` and `face_recognition` boosters: `OcrReading`
|
|
33
|
+
(`text`, `truncated`), `FaceDetectionReading` (`faces`, `detections` of `FaceDetection`
|
|
34
|
+
with a `FaceBox` and a `confidence`) and `FaceRecognitionReading` (`faces`, `matches` of
|
|
35
|
+
`FaceMatch` with a `FaceBox`, a `score`, the `collection` searched, the item's
|
|
36
|
+
`reference`, and `name` or `label`, `None` when absent). Box corners are kept as
|
|
37
|
+
received. A malformed reading is an `UnknownReading`.
|
|
38
|
+
- Collections of faces, as `dn.collections`: `create`, `list`, `get`, `delete`, `enroll`
|
|
39
|
+
(one face from one image, sent as a decision's image is), `list_items` and
|
|
40
|
+
`delete_item`, each returning the API's object as sent. `create` and `enroll` send an
|
|
41
|
+
`Idempotency-Key`, a new UUID per call and the same on every attempt of it, so the API
|
|
42
|
+
creates, enrolls and bills once; they retry like any call, and also while the API
|
|
43
|
+
reports the first attempt still running (a 409 `idempotency_in_progress`, after its
|
|
44
|
+
`Retry-After`). A 409 `idempotency_key_reused` is raised and never retried. A
|
|
45
|
+
collection's type and name, an item's reference and label and the items list's `offset`
|
|
46
|
+
and `limit` are checked before anything is sent; of the picture only a URL's rules are,
|
|
47
|
+
every other fault is the API's 422 `image_unreadable`.
|
|
48
|
+
- A decision whose boosters name `ocr` waits 30 seconds per attempt unless a `timeout` was
|
|
49
|
+
set on the client, and is never retried after a timeout, since the server finishes that
|
|
50
|
+
attempt and may bill it; its other retries stay.
|
|
51
|
+
- `Usage.cost`: what the answer cost in USD, a `Cost` (`usd`, `model_usd`, `boosters_usd`,
|
|
52
|
+
`free_tier`) read as sent, on decisions, batch result lines, a session's open and frame
|
|
53
|
+
replies; `None` when absent, never zeros.
|
|
54
|
+
- `DecisionNodeError.boosters`: the `x-decisionnode-boosters` header as booster name to
|
|
55
|
+
`BoosterStatus` (`status`, `latency_ms`), on every error; empty when the header is absent.
|
|
56
|
+
- Fine-tuned models: `ModelInfo.fine_tuned`, `base`, `adapter_version` and `status`
|
|
57
|
+
(`None` on the catalogue's own entries). A fine-tuned model's name is sent as given, and
|
|
58
|
+
`Decision.model` is the pinned name that answered.
|
|
59
|
+
- `RequestTooLargeError` for the API's 25 MiB request body limit: raised for every 413,
|
|
60
|
+
never retried, and before anything is sent or uploaded when a request, a `decide_many`
|
|
61
|
+
request or a batch line is over 26,214,400 bytes as it will be sent (images to upload
|
|
62
|
+
counted by their `upload_id`). `check_request` measures it too.
|
|
63
|
+
- The fallback: `fallback` (`True` or `False`) on `decide`, the helpers, `decide_many`
|
|
64
|
+
requests, batch requests and `check_request`, sent only when given. `Decision.fallback`
|
|
65
|
+
is a `Fallback` (`served_model`, `requested_model`) on an answer from another model than
|
|
66
|
+
the one asked for, read from `x-decisionnode-fallback` and
|
|
67
|
+
`x-decisionnode-requested-model`; `None` otherwise.
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
- The default model is `decisionnode-moe` (it was `decisionnode-latest`), and the docs use
|
|
72
|
+
the family ids `decisionnode-moe` and `decisionnode-flash`, which always mean the current
|
|
73
|
+
release; a pinned id is `decisionnode-<family>-<version>`, as `models()` lists it. The
|
|
74
|
+
older ids are still accepted by the API and sent as given.
|
|
75
|
+
- `PermissionDeniedError` is documented as every 403 that is not a refusal, a booster
|
|
76
|
+
turned off for the workspace included; `OverloadedError` as also raised, and retried,
|
|
77
|
+
for a required booster that failed (`booster_unavailable`).
|
|
78
|
+
- An error whose body is not JSON (an HTML or text page, or no body) is classed by its
|
|
79
|
+
status alone, with `error_type` `None`, `request_id` and `reason` from the headers and
|
|
80
|
+
the client's own message, `HTTP <status> <text>` (`HTTP 413 Content Too Large`), with
|
|
81
|
+
the request id when sent. The page is never read: `body` is `None`.
|
|
82
|
+
- An image id of more than 64 characters is refused before anything is sent:
|
|
83
|
+
`BadRequestError` `api_usage_error`, "Image <n>: its id is longer than 64 characters."
|
|
84
|
+
|
|
85
|
+
## [0.2.0] - Unreleased
|
|
86
|
+
|
|
87
|
+
Implements version 1.2 of the SDK specification (`SPEC_VERSION` is `"1.2"`) and passes
|
|
88
|
+
conformance suite 1.10.
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
|
|
92
|
+
- Videos sent as frames: `Video(id, frames, fps, start, end)` and `VideoFrame(t, data,
|
|
93
|
+
media_type)`, taken by `decide`, `check`, `classify`, `score`, `number`,
|
|
94
|
+
`gate`, `decide_many` requests, batch requests and `check_request` as `videos`. A frame's
|
|
95
|
+
data is bytes (its type read from the first bytes) or base64 text with its media type.
|
|
96
|
+
Frames always go inline.
|
|
97
|
+
- Before anything is sent: ids unique across images and videos, frame times of at least 0
|
|
98
|
+
and strictly increasing, JPEG, PNG or WebP frames, `fps` above 0 and at most 4, and
|
|
99
|
+
`end` after `start`, each raising `BadRequestError` with the API's message. More than 4
|
|
100
|
+
videos raises the API's `ValidationError` (a `too_long` entry on the videos). Frame
|
|
101
|
+
counts are the API's.
|
|
102
|
+
- `Decision.usage.video_frames`, `Decision.usage.video_seconds` (`None` when absent) and
|
|
103
|
+
`Decision.warnings` (an empty list when absent).
|
|
104
|
+
- The `point` and `box` question types: the `point(instructions, image, units)` and
|
|
105
|
+
`box(instructions, image, units)` builders, and `PointAnswer` (`x`, `y`) and `BoxAnswer`
|
|
106
|
+
(`x1`, `y1`, `x2`, `y2`), each with `present`, `confidence`, `image`, `width` and
|
|
107
|
+
`height`. A request with one needs an image, and `image` names one of the request's
|
|
108
|
+
images (required with two or more); with `units` these are checked before anything is
|
|
109
|
+
sent and raise `ValidationError` with the API's entries.
|
|
110
|
+
|
|
111
|
+
### Removed
|
|
112
|
+
|
|
113
|
+
- `systemone()`. `POST /v1/systemone` is not part of the API; every decision is sent to
|
|
114
|
+
`POST /v1/decide`.
|
|
115
|
+
|
|
116
|
+
### Changed
|
|
117
|
+
|
|
118
|
+
- A request holds up to 16 images (4 before); a 17th raises the API's `ValidationError`
|
|
119
|
+
before anything is sent: a `too_long` entry on the images, listed with the request's
|
|
120
|
+
other entries, a local image in its `input` without its bytes. Up to 16 images are
|
|
121
|
+
uploaded through one presign; `upload()` of more raises `BadRequestError`
|
|
122
|
+
(`files: 1 to 16 files, got 17.`) before anything is sent.
|
|
123
|
+
|
|
124
|
+
## [0.1.0] - Unreleased
|
|
125
|
+
|
|
126
|
+
First version. Implements version 1.1 of the specification shared by every official
|
|
127
|
+
DecisionNode SDK, exported as `SPEC_VERSION`, and passes conformance suite 1.6.
|
|
128
|
+
|
|
129
|
+
### Added
|
|
130
|
+
|
|
131
|
+
- The `number` question type: the `number` builder, the `number` helper and
|
|
132
|
+
`NumberAnswer` (`number`, `expected`, `confidence`, `probabilities` keyed by the value as
|
|
133
|
+
the API writes it).
|
|
134
|
+
- `systemone` for `POST /v1/systemone`, the strict path.
|
|
135
|
+
- Truth answers read as `TruthAnswer` under either wire name the API uses for the type.
|
|
136
|
+
- Images by `upload_id`, and `upload(images)`. Local images whose bytes total
|
|
137
|
+
`DEFAULT_INLINE_THRESHOLD` (512 KiB) or more in a request are uploaded first (one
|
|
138
|
+
presign, parallel `PUT`s with exactly the signed headers) and sent by `upload_id`;
|
|
139
|
+
smaller ones go inline. The client option `inline_threshold` changes the threshold.
|
|
140
|
+
Uploads are remembered by the SHA-256 of their bytes and reused until 5 minutes before
|
|
141
|
+
they expire; a stale one is uploaded again and the request sent once more.
|
|
142
|
+
- Every fixed rule the API documents is checked before any request, in the API's order,
|
|
143
|
+
raising its own error class, `error_type`, message and 422 entries: the 128-level
|
|
144
|
+
nesting limit; question types and shapes, option, level and truth rules and the number
|
|
145
|
+
grid (`number` is refused on `systemone`); the state's type; image count, ids, format
|
|
146
|
+
by magic bytes, size, pixel dimensions read from the JPEG, PNG and WebP headers and an
|
|
147
|
+
upload reference's media type; image URLs (`https`, at most 2,048 characters, no user
|
|
148
|
+
information, port 443, no literal private address); session `window` and
|
|
149
|
+
`ttl_seconds`; frames refused by `Session.send` with nothing written and no `seq` used;
|
|
150
|
+
batch `custom_id` rules, an empty add and `batches.list` paging. Limits a model
|
|
151
|
+
publishes are checked from the values held after a `models()` call, found by the
|
|
152
|
+
model's name or by the alias or pinned version an entry states.
|
|
153
|
+
- `check_request(method, **arguments)` and `Session.check_frame(...)` run every local
|
|
154
|
+
check a call would run, raise the same error, and send and upload nothing.
|
|
155
|
+
- `upload_timeout` (default 60 seconds): each storage `PUT` attempt of an image has its
|
|
156
|
+
own deadline, apart from `timeout`.
|
|
157
|
+
- A session's stream goes through the HTTP client's proxy and uses its TLS settings.
|
|
158
|
+
- Batch results are read from the network line by line, the whole file.
|
|
159
|
+
- A storage `PUT` answered `403` after its URL's lifetime is presigned again once.
|
|
160
|
+
- Sessions: `open_session`, `Session` and `AsyncSession` (`connect`, `send`, `replies`,
|
|
161
|
+
`next_event`, `end`, `close`), `Reply`, `FrameError`, `SessionClosed`, and `supervise`.
|
|
162
|
+
`SessionLimitError`, a `RateLimitError`, for a key at its open-session limit.
|
|
163
|
+
- The Batch API: `batches.create`, `add`, `finalize`, `get`, `list`, `results` and
|
|
164
|
+
`cancel`, and `run_batch`; `Batch`, `BatchPage`, `BatchResult` and `BatchRun`.
|
|
165
|
+
- Connections kept open between calls (up to 100, 30 seconds idle), and an `http2`
|
|
166
|
+
option with the `http2` extra.
|
|
167
|
+
|
|
168
|
+
- `DecisionNode` and `AsyncDecisionNode` clients with `decide`, `decide_many`, `check`,
|
|
169
|
+
`classify`, `score`, `gate`, `models` and `decide_raw`. `check`, `classify`, `score`
|
|
170
|
+
and `gate` take `images` and `model` like `decide`.
|
|
171
|
+
- `gate` with fixed wording, the same in every official SDK.
|
|
172
|
+
- `ModelInfo` for each entry of `models()`, and `DecisionResult` for each result of
|
|
173
|
+
`decide_many`.
|
|
174
|
+
- Question builders `truth`, `choice` and `score`; wire-form mappings are accepted too.
|
|
175
|
+
- Typed answers: `TruthAnswer`, `ChoiceAnswer`, `ScoreAnswer`, and `UnknownAnswer` for
|
|
176
|
+
answer types this version does not know.
|
|
177
|
+
- Images from a file path, bytes (or base64 text) or an `https` URL. The media type of a
|
|
178
|
+
file, and of bytes given without one, is read from the first bytes (JPEG, PNG or WebP);
|
|
179
|
+
a file that cannot be read raises `ValueError` when the `Image` is made.
|
|
180
|
+
- An answer of a known type with a missing or wrongly typed field comes back as
|
|
181
|
+
`UnknownAnswer` with its raw fields; the rest of the decision parses as usual.
|
|
182
|
+
- Retries on 408, 429, 5xx, timeouts and connection errors, honouring `retry-after-ms`
|
|
183
|
+
and `Retry-After` (seconds or an HTTP date), capped at 60 seconds.
|
|
184
|
+
- A 10 second timeout that covers the whole attempt, from sending the request to the
|
|
185
|
+
last byte of the response body. `timeout` must be above 0 and `max_retries` 0 or
|
|
186
|
+
more.
|
|
187
|
+
- Exception classes for the documented statuses, all derived from `DecisionNodeError`,
|
|
188
|
+
including `PermissionDeniedError` for a 403 that is not a refusal. A redirect and any
|
|
189
|
+
other status raise `DecisionNodeError` itself with its `status`; a 2xx whose body is
|
|
190
|
+
not JSON raises `ServerError`. `str(error)` is the server's message, `""` when it
|
|
191
|
+
sent none.
|
|
192
|
+
- Argument problems found before sending (an empty question map, a value that cannot be
|
|
193
|
+
sent as JSON, an unreadable file, a bad `base_url`, `timeout` or `max_retries`) raise
|
|
194
|
+
Python's own `ValueError`, never a `DecisionNodeError`. `decide_many` checks every
|
|
195
|
+
request before sending any.
|
|
196
|
+
- Redirects are never followed, also on an `http_client` passed in that is set to
|
|
197
|
+
follow them.
|
|
198
|
+
- The base URL must be `https`; plain `http` is accepted only for `localhost`,
|
|
199
|
+
`127.0.0.1` and `::1`.
|
|
200
|
+
- `Decision.safety`, parsed from the `x-decisionnode-safety` response header. The
|
|
201
|
+
README shows how to send `x-decisionnode-safety-body: true` to also get the result
|
|
202
|
+
in the response body.
|
|
203
|
+
- Settings from `DECISIONNODE_API_KEY`, `DECISIONNODE_BASE_URL` and `DECISIONNODE_MODEL`;
|
|
204
|
+
an empty argument counts as not given.
|
|
205
|
+
- `decide_many` refuses a `concurrency` below 1 with `ValueError` before sending.
|
|
206
|
+
- `check`, `classify` and `score` raise `ServerError` (`status` 200, `reason` from
|
|
207
|
+
`x-decisionnode-error`) when the answer to their question is missing or malformed;
|
|
208
|
+
`decide` returns such an answer as `UnknownAnswer`.
|
|
209
|
+
- A caller's `http_client` is checked when the client is made: it must be an
|
|
210
|
+
`httpx.Client` (`httpx.AsyncClient`) itself, with httpx's own transports, or it is
|
|
211
|
+
refused with `ValueError`, since another type may follow redirects. For an `https`
|
|
212
|
+
base URL it may not turn off certificate or hostname checks.
|
|
213
|
+
- `DecisionNodeError` is exported by that name and is the base of every exception.
|
|
214
|
+
- CI pins every action to a commit SHA, sets `persist-credentials: false` on every
|
|
215
|
+
checkout, installs a pinned uv and checks out the conformance suite at a pinned
|
|
216
|
+
commit (suite 1.6).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bynn Intelligence, Inc.
|
|
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.
|