launchables 0.4.0__tar.gz → 0.5.0__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.
- {launchables-0.4.0 → launchables-0.5.0}/PKG-INFO +54 -5
- {launchables-0.4.0 → launchables-0.5.0}/README.md +53 -4
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/__init__.py +20 -0
- launchables-0.5.0/src/launchables/_version.py +1 -0
- launchables-0.5.0/src/launchables/batches.py +497 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/client.py +95 -18
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables.egg-info/PKG-INFO +54 -5
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables.egg-info/SOURCES.txt +2 -0
- launchables-0.5.0/tests/test_batches.py +480 -0
- launchables-0.4.0/src/launchables/_version.py +0 -1
- {launchables-0.4.0 → launchables-0.5.0}/LICENSE +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/pyproject.toml +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/setup.cfg +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/admin.py +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/errors.py +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/images.py +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/py.typed +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/questions.py +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables/responses.py +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables.egg-info/dependency_links.txt +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/src/launchables.egg-info/top_level.txt +0 -0
- {launchables-0.4.0 → launchables-0.5.0}/tests/test_client.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: launchables
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Python client for the Launchables Decision API
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://www.launchables.ai/docs
|
|
@@ -34,6 +34,7 @@ resp = client.decide(
|
|
|
34
34
|
"Customer bought the item 12 days ago and has no receipt. "
|
|
35
35
|
"Policy: refunds within 30 days with proof of purchase.",
|
|
36
36
|
{"refund": noul("Is a refund permitted under the policy?")},
|
|
37
|
+
model="myles-g-12b",
|
|
37
38
|
)
|
|
38
39
|
|
|
39
40
|
answer = resp.answers["refund"]
|
|
@@ -42,7 +43,9 @@ print(answer.decide(threshold_yes=0.9, threshold_no=0.1)) # "yes" | "no" | "per
|
|
|
42
43
|
print(resp.usage.input_tokens, resp.model)
|
|
43
44
|
```
|
|
44
45
|
|
|
45
|
-
`decide` returns a `DecisionResponse` with `.answers`, `.usage`, `.model` and `.raw` (the untouched JSON). `state` may be a string or a dict. To include images, see [Images](#images).
|
|
46
|
+
`decide(state, questions, model=...)` returns a `DecisionResponse` with `.answers`, `.usage`, `.model` and `.raw` (the untouched JSON). `state` may be a string or a dict. To include images, see [Images](#images).
|
|
47
|
+
|
|
48
|
+
Always pass `model`, a model id from `client.models()` (for example `"myles-g-12b"` or `"myles-g-31b"`), so every request names the model that answers it. The API is moving to requiring it; until then a request without one is answered by the server's default model, which can change.
|
|
46
49
|
|
|
47
50
|
### Question types
|
|
48
51
|
|
|
@@ -54,7 +57,7 @@ questions = {
|
|
|
54
57
|
"topic": choice("What is this about?", ["billing", "returns", "other"]), # or {label: description}
|
|
55
58
|
"urgency": score("How urgent is it?", ["not urgent", "low", "medium", "high"]), # lowest level first, numbered from 0
|
|
56
59
|
}
|
|
57
|
-
resp = client.decide(state, questions)
|
|
60
|
+
resp = client.decide(state, questions, model="myles-g-12b")
|
|
58
61
|
|
|
59
62
|
resp.answers["topic"].choice # "returns"
|
|
60
63
|
resp.answers["topic"].probabilities # {"billing": 0.06, "returns": 0.89, ...}
|
|
@@ -87,6 +90,7 @@ resp = client.decide(
|
|
|
87
90
|
{"receipt": "receipt_photo", "note": "Customer asks for a refund"},
|
|
88
91
|
{"total_matches": noul("Does the total printed on receipt_photo equal 23.40?")},
|
|
89
92
|
assets={"receipt_photo": Image.from_path("receipt.jpg")},
|
|
93
|
+
model="myles-g-12b",
|
|
90
94
|
image_budget=140, # optional: 70, 140 or 280 (the default)
|
|
91
95
|
)
|
|
92
96
|
```
|
|
@@ -104,6 +108,51 @@ Limits, checked by the server (each breach is an `InvalidRequestError` whose mes
|
|
|
104
108
|
|
|
105
109
|
Images count as input tokens and are included in `usage.input_tokens`. At budget 280 one image costs up to about 280 tokens, at 140 up to about 140, at 70 up to about 70. An image referenced in the state is billed once per request however many questions read it; one referenced in a question's instructions or criteria is billed for that question, once per use.
|
|
106
110
|
|
|
111
|
+
## Batches
|
|
112
|
+
|
|
113
|
+
The Batch API answers many decisions from one submission, within 24 hours, at half the live price per input token. Use it for work that does not need an answer straight away: back-catalogues, nightly reviews, re-scoring after a policy change. Batch support is in version 0.5.0 and later.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from launchables import Launchables, choice, noul
|
|
117
|
+
|
|
118
|
+
client = Launchables()
|
|
119
|
+
|
|
120
|
+
batch = client.batch([
|
|
121
|
+
{"custom_id": "ticket-1001", "model": "myles-g-12b", "state": "Charged twice for order 4471",
|
|
122
|
+
"questions": {"refund": noul("Is a refund due?")}},
|
|
123
|
+
{"custom_id": "ticket-1002", "model": "myles-g-31b", "state": {"message": "The app crashes on login"},
|
|
124
|
+
"questions": {"team": choice("Which team?", ["billing", "returns", "technical", "other"])}},
|
|
125
|
+
]) # CreatedBatch(id="batch_...", status="queued", lines=2, estimated_cost=..., expires_at=..., data_region=...)
|
|
126
|
+
|
|
127
|
+
finished = client.batches.wait_for(batch.id) # polls 5 s, 7.5 s, 11.25 s ... up to 60 s apart
|
|
128
|
+
|
|
129
|
+
for r in client.batches.stream_results(batch.id):
|
|
130
|
+
if r.ok:
|
|
131
|
+
print(r.custom_id, r.response.answers) # a DecisionResponse, exactly as decide() returns it
|
|
132
|
+
else:
|
|
133
|
+
print(r.custom_id, r.error.get("type")) # e.g. "expired", "cancelled", "invalid_question"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Each line is a dict: the body of one `decide` request with your own `custom_id` (1 to 64 letters, digits or `_ . : -`, unique in the batch) and a `model`, which every line must name: a batch has no default model. Keys are as they are sent: `custom_id`, `model`, `state`, `questions`, and optionally `assets` and `image_budget`. Lines may name different models. Images must be inline (`Image.from_path`, `Image.from_bytes`, or a dict with `data_base64`); a batch line cannot carry file parts.
|
|
137
|
+
|
|
138
|
+
| Call | What it does |
|
|
139
|
+
| --- | --- |
|
|
140
|
+
| `batch(lines, webhook_url=None)` or `batches.create(...)` | Checks every line, sends them as one JSONL file and returns the queued batch (`CreatedBatch`) with its cost estimate. |
|
|
141
|
+
| `batches.list()` | The calling key's batches (`Batch`), oldest first. |
|
|
142
|
+
| `batches.retrieve(batch_id)` | Status (`"queued"`, `"running"`, `"done"`, `"failed"` or `"cancelled"`), `counts` (`done`, `failed`, `pending`) and times. |
|
|
143
|
+
| `batches.wait_for(batch_id, ...)` | Polls until the results can be collected. The first check is at once; the wait after it, `poll_interval` (5 seconds), grows 1.5 times a check up to `max_interval` (60 seconds), both at least 1 second; `timeout` (25 hours) raises `BatchTimeoutError`; setting `stop` (a `threading.Event`) raises `BatchWaitCancelledError`; `on_poll(batch)` sees each status. It keeps waiting through a brief outage (connection errors, 429 and 5xx other than `batch_unavailable`). Giving up never cancels the batch. |
|
|
144
|
+
| `batches.stream_results(batch_id)` | Each result line as it arrives, in input order, so a large file is never held in memory. |
|
|
145
|
+
| `batches.results(batch_id)` | Every result line in one list. |
|
|
146
|
+
| `batches.cancel(batch_id)` | Stops the batch. Lines already answered stay in the results and are billed; the rest read as cancelled and are not. |
|
|
147
|
+
|
|
148
|
+
A result is an `AnsweredBatchResult` (`ok` is `True`, `response` is a `DecisionResponse`) or a `FailedBatchResult` (`ok` is `False`, `error` is a dict saying why). Both have `custom_id`, `model` (the exact model build that answered), `status` (the HTTP status the line got, 200 when answered, or `None` for a line that never ran), `usage` (`input_tokens` is what the line is billed on) and `raw`. `BatchTimeoutError` and `BatchWaitCancelledError` carry the last status read in `.batch`.
|
|
149
|
+
|
|
150
|
+
The client checks every line before sending and raises `ValidationError` naming the line (`line 3 (custom_id ticket-1003): ...`). The server checks every line again and refuses the whole batch if any is invalid: `InvalidRequestError` with `.type` `"invalid_batch"` and each bad line in `e.body["errors"]` (`{"line", "type", "error"}`), so nothing is half-queued. Other batch errors: `RateLimitError` with `.type` `"too_many_batches"` when the key already has its limit of batches queued or running (not retried; `.retry_after` says when to try); `ConflictError` with `.type` `"batch_not_finished"` for results before the batch has ended, or `"batch_finished"` for cancelling a batch that is already done or failed; `NotFoundError` (`"batch_not_found"`) for an id the key does not own; `APIError` with `.type` `"batch_unavailable"` where the Batch API is not switched on (not retried).
|
|
151
|
+
|
|
152
|
+
A submission is retried only after a refusal that stored nothing (429 `rate_limited`, 503 `busy`), never after a lost connection or a 502, so a retry cannot submit the same batch twice. Status, results and cancel calls are retried like any other.
|
|
153
|
+
|
|
154
|
+
Batches belong to the API key that submitted them. A rolled key is a new key: collect a batch's results with the key that submitted it, before that key stops working. See the [Batch API documentation](https://www.launchables.ai/docs/api/batches) for limits, the 24-hour window, webhooks, retention and where batch data is stored.
|
|
155
|
+
|
|
107
156
|
## Key expiry
|
|
108
157
|
|
|
109
158
|
Every API key expires. The client reads the expiry date from each response, so your program can plan rotation instead of finding out when the key stops working.
|
|
@@ -157,7 +206,7 @@ All errors derive from `LaunchablesError` and carry `.status`, `.message` (the s
|
|
|
157
206
|
| `PermissionDeniedError` | 403: the key's workspace may not use this model (`.type` `"model_not_allowed"`), or the admin key's maker is no longer an owner |
|
|
158
207
|
| `RateLimitError` | 429 (`.retry_after` seconds, if sent) |
|
|
159
208
|
| `InvalidRequestError` | 400, 411, 413, 422 and other 4xx; `ValidationError` is the local pre-flight subclass. `.type` says what was wrong with an image: `"invalid_assets"`, `"unsupported_image_format"`, `"invalid_image"`, `"image_too_large"`, `"image_dimensions"`, `"too_many_images"`, `"unreferenced_asset"` or `"image_reference_truncated"` (400), or `"invalid_image_budget"` (422). |
|
|
160
|
-
| `NotFoundError`, `ConflictError` | 404 and 409 from the key management API (subclasses of `InvalidRequestError`) |
|
|
209
|
+
| `NotFoundError`, `ConflictError` | 404 and 409 from the key management API and the Batch API (subclasses of `InvalidRequestError`) |
|
|
161
210
|
| `APIError` | 5xx |
|
|
162
211
|
| `APIConnectionError` | could not reach the server (including timeouts) |
|
|
163
212
|
|
|
@@ -165,7 +214,7 @@ All errors derive from `LaunchablesError` and carry `.status`, `.message` (the s
|
|
|
165
214
|
from launchables import OutOfCreditError, RateLimitError, InvalidRequestError, LaunchablesError
|
|
166
215
|
|
|
167
216
|
try:
|
|
168
|
-
resp = client.decide(state, questions)
|
|
217
|
+
resp = client.decide(state, questions, model="myles-g-12b")
|
|
169
218
|
except OutOfCreditError as e:
|
|
170
219
|
print("workspace budget reached" if e.type == "budget_exceeded" else "top up credit")
|
|
171
220
|
except RateLimitError as e:
|
|
@@ -17,6 +17,7 @@ resp = client.decide(
|
|
|
17
17
|
"Customer bought the item 12 days ago and has no receipt. "
|
|
18
18
|
"Policy: refunds within 30 days with proof of purchase.",
|
|
19
19
|
{"refund": noul("Is a refund permitted under the policy?")},
|
|
20
|
+
model="myles-g-12b",
|
|
20
21
|
)
|
|
21
22
|
|
|
22
23
|
answer = resp.answers["refund"]
|
|
@@ -25,7 +26,9 @@ print(answer.decide(threshold_yes=0.9, threshold_no=0.1)) # "yes" | "no" | "per
|
|
|
25
26
|
print(resp.usage.input_tokens, resp.model)
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
`decide` returns a `DecisionResponse` with `.answers`, `.usage`, `.model` and `.raw` (the untouched JSON). `state` may be a string or a dict. To include images, see [Images](#images).
|
|
29
|
+
`decide(state, questions, model=...)` returns a `DecisionResponse` with `.answers`, `.usage`, `.model` and `.raw` (the untouched JSON). `state` may be a string or a dict. To include images, see [Images](#images).
|
|
30
|
+
|
|
31
|
+
Always pass `model`, a model id from `client.models()` (for example `"myles-g-12b"` or `"myles-g-31b"`), so every request names the model that answers it. The API is moving to requiring it; until then a request without one is answered by the server's default model, which can change.
|
|
29
32
|
|
|
30
33
|
### Question types
|
|
31
34
|
|
|
@@ -37,7 +40,7 @@ questions = {
|
|
|
37
40
|
"topic": choice("What is this about?", ["billing", "returns", "other"]), # or {label: description}
|
|
38
41
|
"urgency": score("How urgent is it?", ["not urgent", "low", "medium", "high"]), # lowest level first, numbered from 0
|
|
39
42
|
}
|
|
40
|
-
resp = client.decide(state, questions)
|
|
43
|
+
resp = client.decide(state, questions, model="myles-g-12b")
|
|
41
44
|
|
|
42
45
|
resp.answers["topic"].choice # "returns"
|
|
43
46
|
resp.answers["topic"].probabilities # {"billing": 0.06, "returns": 0.89, ...}
|
|
@@ -70,6 +73,7 @@ resp = client.decide(
|
|
|
70
73
|
{"receipt": "receipt_photo", "note": "Customer asks for a refund"},
|
|
71
74
|
{"total_matches": noul("Does the total printed on receipt_photo equal 23.40?")},
|
|
72
75
|
assets={"receipt_photo": Image.from_path("receipt.jpg")},
|
|
76
|
+
model="myles-g-12b",
|
|
73
77
|
image_budget=140, # optional: 70, 140 or 280 (the default)
|
|
74
78
|
)
|
|
75
79
|
```
|
|
@@ -87,6 +91,51 @@ Limits, checked by the server (each breach is an `InvalidRequestError` whose mes
|
|
|
87
91
|
|
|
88
92
|
Images count as input tokens and are included in `usage.input_tokens`. At budget 280 one image costs up to about 280 tokens, at 140 up to about 140, at 70 up to about 70. An image referenced in the state is billed once per request however many questions read it; one referenced in a question's instructions or criteria is billed for that question, once per use.
|
|
89
93
|
|
|
94
|
+
## Batches
|
|
95
|
+
|
|
96
|
+
The Batch API answers many decisions from one submission, within 24 hours, at half the live price per input token. Use it for work that does not need an answer straight away: back-catalogues, nightly reviews, re-scoring after a policy change. Batch support is in version 0.5.0 and later.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from launchables import Launchables, choice, noul
|
|
100
|
+
|
|
101
|
+
client = Launchables()
|
|
102
|
+
|
|
103
|
+
batch = client.batch([
|
|
104
|
+
{"custom_id": "ticket-1001", "model": "myles-g-12b", "state": "Charged twice for order 4471",
|
|
105
|
+
"questions": {"refund": noul("Is a refund due?")}},
|
|
106
|
+
{"custom_id": "ticket-1002", "model": "myles-g-31b", "state": {"message": "The app crashes on login"},
|
|
107
|
+
"questions": {"team": choice("Which team?", ["billing", "returns", "technical", "other"])}},
|
|
108
|
+
]) # CreatedBatch(id="batch_...", status="queued", lines=2, estimated_cost=..., expires_at=..., data_region=...)
|
|
109
|
+
|
|
110
|
+
finished = client.batches.wait_for(batch.id) # polls 5 s, 7.5 s, 11.25 s ... up to 60 s apart
|
|
111
|
+
|
|
112
|
+
for r in client.batches.stream_results(batch.id):
|
|
113
|
+
if r.ok:
|
|
114
|
+
print(r.custom_id, r.response.answers) # a DecisionResponse, exactly as decide() returns it
|
|
115
|
+
else:
|
|
116
|
+
print(r.custom_id, r.error.get("type")) # e.g. "expired", "cancelled", "invalid_question"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Each line is a dict: the body of one `decide` request with your own `custom_id` (1 to 64 letters, digits or `_ . : -`, unique in the batch) and a `model`, which every line must name: a batch has no default model. Keys are as they are sent: `custom_id`, `model`, `state`, `questions`, and optionally `assets` and `image_budget`. Lines may name different models. Images must be inline (`Image.from_path`, `Image.from_bytes`, or a dict with `data_base64`); a batch line cannot carry file parts.
|
|
120
|
+
|
|
121
|
+
| Call | What it does |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| `batch(lines, webhook_url=None)` or `batches.create(...)` | Checks every line, sends them as one JSONL file and returns the queued batch (`CreatedBatch`) with its cost estimate. |
|
|
124
|
+
| `batches.list()` | The calling key's batches (`Batch`), oldest first. |
|
|
125
|
+
| `batches.retrieve(batch_id)` | Status (`"queued"`, `"running"`, `"done"`, `"failed"` or `"cancelled"`), `counts` (`done`, `failed`, `pending`) and times. |
|
|
126
|
+
| `batches.wait_for(batch_id, ...)` | Polls until the results can be collected. The first check is at once; the wait after it, `poll_interval` (5 seconds), grows 1.5 times a check up to `max_interval` (60 seconds), both at least 1 second; `timeout` (25 hours) raises `BatchTimeoutError`; setting `stop` (a `threading.Event`) raises `BatchWaitCancelledError`; `on_poll(batch)` sees each status. It keeps waiting through a brief outage (connection errors, 429 and 5xx other than `batch_unavailable`). Giving up never cancels the batch. |
|
|
127
|
+
| `batches.stream_results(batch_id)` | Each result line as it arrives, in input order, so a large file is never held in memory. |
|
|
128
|
+
| `batches.results(batch_id)` | Every result line in one list. |
|
|
129
|
+
| `batches.cancel(batch_id)` | Stops the batch. Lines already answered stay in the results and are billed; the rest read as cancelled and are not. |
|
|
130
|
+
|
|
131
|
+
A result is an `AnsweredBatchResult` (`ok` is `True`, `response` is a `DecisionResponse`) or a `FailedBatchResult` (`ok` is `False`, `error` is a dict saying why). Both have `custom_id`, `model` (the exact model build that answered), `status` (the HTTP status the line got, 200 when answered, or `None` for a line that never ran), `usage` (`input_tokens` is what the line is billed on) and `raw`. `BatchTimeoutError` and `BatchWaitCancelledError` carry the last status read in `.batch`.
|
|
132
|
+
|
|
133
|
+
The client checks every line before sending and raises `ValidationError` naming the line (`line 3 (custom_id ticket-1003): ...`). The server checks every line again and refuses the whole batch if any is invalid: `InvalidRequestError` with `.type` `"invalid_batch"` and each bad line in `e.body["errors"]` (`{"line", "type", "error"}`), so nothing is half-queued. Other batch errors: `RateLimitError` with `.type` `"too_many_batches"` when the key already has its limit of batches queued or running (not retried; `.retry_after` says when to try); `ConflictError` with `.type` `"batch_not_finished"` for results before the batch has ended, or `"batch_finished"` for cancelling a batch that is already done or failed; `NotFoundError` (`"batch_not_found"`) for an id the key does not own; `APIError` with `.type` `"batch_unavailable"` where the Batch API is not switched on (not retried).
|
|
134
|
+
|
|
135
|
+
A submission is retried only after a refusal that stored nothing (429 `rate_limited`, 503 `busy`), never after a lost connection or a 502, so a retry cannot submit the same batch twice. Status, results and cancel calls are retried like any other.
|
|
136
|
+
|
|
137
|
+
Batches belong to the API key that submitted them. A rolled key is a new key: collect a batch's results with the key that submitted it, before that key stops working. See the [Batch API documentation](https://www.launchables.ai/docs/api/batches) for limits, the 24-hour window, webhooks, retention and where batch data is stored.
|
|
138
|
+
|
|
90
139
|
## Key expiry
|
|
91
140
|
|
|
92
141
|
Every API key expires. The client reads the expiry date from each response, so your program can plan rotation instead of finding out when the key stops working.
|
|
@@ -140,7 +189,7 @@ All errors derive from `LaunchablesError` and carry `.status`, `.message` (the s
|
|
|
140
189
|
| `PermissionDeniedError` | 403: the key's workspace may not use this model (`.type` `"model_not_allowed"`), or the admin key's maker is no longer an owner |
|
|
141
190
|
| `RateLimitError` | 429 (`.retry_after` seconds, if sent) |
|
|
142
191
|
| `InvalidRequestError` | 400, 411, 413, 422 and other 4xx; `ValidationError` is the local pre-flight subclass. `.type` says what was wrong with an image: `"invalid_assets"`, `"unsupported_image_format"`, `"invalid_image"`, `"image_too_large"`, `"image_dimensions"`, `"too_many_images"`, `"unreferenced_asset"` or `"image_reference_truncated"` (400), or `"invalid_image_budget"` (422). |
|
|
143
|
-
| `NotFoundError`, `ConflictError` | 404 and 409 from the key management API (subclasses of `InvalidRequestError`) |
|
|
192
|
+
| `NotFoundError`, `ConflictError` | 404 and 409 from the key management API and the Batch API (subclasses of `InvalidRequestError`) |
|
|
144
193
|
| `APIError` | 5xx |
|
|
145
194
|
| `APIConnectionError` | could not reach the server (including timeouts) |
|
|
146
195
|
|
|
@@ -148,7 +197,7 @@ All errors derive from `LaunchablesError` and carry `.status`, `.message` (the s
|
|
|
148
197
|
from launchables import OutOfCreditError, RateLimitError, InvalidRequestError, LaunchablesError
|
|
149
198
|
|
|
150
199
|
try:
|
|
151
|
-
resp = client.decide(state, questions)
|
|
200
|
+
resp = client.decide(state, questions, model="myles-g-12b")
|
|
152
201
|
except OutOfCreditError as e:
|
|
153
202
|
print("workspace budget reached" if e.type == "budget_exceeded" else "top up credit")
|
|
154
203
|
except RateLimitError as e:
|
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
from ._version import __version__
|
|
4
4
|
from .admin import AdminKeys
|
|
5
|
+
from .batches import (
|
|
6
|
+
BATCH_FINISHED_STATUSES,
|
|
7
|
+
CUSTOM_ID_PATTERN,
|
|
8
|
+
AnsweredBatchResult,
|
|
9
|
+
Batch,
|
|
10
|
+
BatchCostEstimate,
|
|
11
|
+
BatchCounts,
|
|
12
|
+
Batches,
|
|
13
|
+
BatchLine,
|
|
14
|
+
BatchResult,
|
|
15
|
+
BatchTimeoutError,
|
|
16
|
+
BatchUsage,
|
|
17
|
+
BatchWaitCancelledError,
|
|
18
|
+
CreatedBatch,
|
|
19
|
+
FailedBatchResult,
|
|
20
|
+
to_jsonl,
|
|
21
|
+
)
|
|
5
22
|
from .client import DEFAULT_ADMIN_BASE_URL, DEFAULT_BASE_URL, KEY_EXPIRY_WARNING_DAYS, KeyExpiryWarning, Launchables
|
|
6
23
|
from .errors import (
|
|
7
24
|
APIConnectionError,
|
|
@@ -36,4 +53,7 @@ __all__ = [
|
|
|
36
53
|
"LaunchablesError", "AuthenticationError", "OutOfCreditError", "PermissionDeniedError",
|
|
37
54
|
"RateLimitError", "InvalidRequestError", "NotFoundError", "ConflictError",
|
|
38
55
|
"ValidationError", "APIError", "APIConnectionError",
|
|
56
|
+
"Batches", "Batch", "BatchLine", "BatchCounts", "BatchCostEstimate", "BatchUsage", "CreatedBatch", "BatchResult",
|
|
57
|
+
"AnsweredBatchResult", "FailedBatchResult", "BatchTimeoutError", "BatchWaitCancelledError",
|
|
58
|
+
"BATCH_FINISHED_STATUSES", "CUSTOM_ID_PATTERN", "to_jsonl",
|
|
39
59
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.0"
|