commoncompute 0.1.2__tar.gz → 0.3.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.
- commoncompute-0.3.0/PKG-INFO +349 -0
- commoncompute-0.3.0/README.md +314 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/__init__.py +5 -1
- commoncompute-0.3.0/commoncompute/_async_client.py +474 -0
- commoncompute-0.3.0/commoncompute/_async_tasks.py +349 -0
- commoncompute-0.3.0/commoncompute/_config.py +161 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/_transport.py +27 -43
- commoncompute-0.3.0/commoncompute/cli.py +828 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/client.py +18 -88
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/connect.py +8 -18
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/models.py +3 -3
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/tasks.py +144 -34
- {commoncompute-0.1.2 → commoncompute-0.3.0}/pyproject.toml +18 -2
- commoncompute-0.1.2/PKG-INFO +0 -216
- commoncompute-0.1.2/README.md +0 -181
- commoncompute-0.1.2/commoncompute/cli.py +0 -481
- {commoncompute-0.1.2 → commoncompute-0.3.0}/.gitignore +0 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/LICENSE +0 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/_generated_workloads.py +0 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/aws_batch.py +0 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/compat/__init__.py +0 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/compat/openai.py +0 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/errors.py +0 -0
- {commoncompute-0.1.2 → commoncompute-0.3.0}/commoncompute/py.typed +0 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: commoncompute
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Official Python SDK for Common Compute — the batch AI bill you shouldn't be paying.
|
|
5
|
+
Project-URL: Homepage, https://commoncompute.ai
|
|
6
|
+
Project-URL: Documentation, https://commoncompute.ai/docs
|
|
7
|
+
Project-URL: Source, https://github.com/Ikaikaalika/commoncomputeai
|
|
8
|
+
Project-URL: Changelog, https://commoncompute.ai/changelog
|
|
9
|
+
Author-email: Common Compute <support@commoncompute.ai>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,apple-silicon,embeddings,inference,llm,openai-compatible
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: httpx<1.0,>=0.24
|
|
26
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
27
|
+
Provides-Extra: cli
|
|
28
|
+
Requires-Dist: rich>=13; extra == 'cli'
|
|
29
|
+
Requires-Dist: typer>=0.9; extra == 'cli'
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'test'
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == 'test'
|
|
33
|
+
Requires-Dist: respx>=0.20; extra == 'test'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# Common Compute — Python SDK
|
|
37
|
+
|
|
38
|
+
The official Python SDK + CLI for [Common Compute](https://commoncompute.ai) —
|
|
39
|
+
batch AI compute without the AWS tax, on Apple Silicon hardware AWS can't offer.
|
|
40
|
+
|
|
41
|
+
One SDK call replaces the IAM roles, compute environments, and job definitions.
|
|
42
|
+
Every job returns its **price and ETA before it runs**, and you're only billed
|
|
43
|
+
for **successful** jobs — each with a verifiable receipt.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install commoncompute # core: httpx + pydantic only
|
|
47
|
+
pip install "commoncompute[cli]" # + the `commoncompute` command line
|
|
48
|
+
commoncompute login # opens the browser — no key to copy-paste
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The command is `commoncompute`. A short `cc` is **not** installed — that name
|
|
52
|
+
belongs to the system C compiler. Want the shorthand? `alias cc="commoncompute"`.
|
|
53
|
+
|
|
54
|
+
`commoncompute login` (or `commoncompute.connect()` from a script or notebook) opens an
|
|
55
|
+
approval page in your browser; click **Approve** and a fresh API key is saved
|
|
56
|
+
to `~/.config/commoncompute/credentials`, where the SDK finds it automatically.
|
|
57
|
+
Prefer explicit config? `export CC_API_KEY=cc_live_...` works everywhere and
|
|
58
|
+
takes precedence.
|
|
59
|
+
|
|
60
|
+
## The 10-minute path
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import commoncompute as cc
|
|
64
|
+
|
|
65
|
+
client = cc.Client() # reads CC_API_KEY, or the saved credentials file
|
|
66
|
+
|
|
67
|
+
job = client.submit("coreml_embed", {"input": ["what is the neural engine?"]},
|
|
68
|
+
model_id="bge-base")
|
|
69
|
+
print(job.job_id, job.locked_price_usd, job.eta_seconds) # price known BEFORE it runs
|
|
70
|
+
|
|
71
|
+
result = client.result(job) # waits, downloads, attaches the receipt
|
|
72
|
+
print(result.output)
|
|
73
|
+
print(result.receipt) # signed proof of what ran, where, for how much
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Prefer the OpenAI-style surface? `client.embeddings.create(input=[...],
|
|
77
|
+
model="bge-base")` returns vectors synchronously.
|
|
78
|
+
|
|
79
|
+
## Examples — live today
|
|
80
|
+
|
|
81
|
+
### 1. OCR / document extraction — vs AWS Textract
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
job = client.ocr.extract("https://example.com/contracts.pdf", structured=True)
|
|
85
|
+
result = client.result(job) # blocks + bounding boxes as JSON
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 2. Embeddings in bulk
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
for result in client.submit_many(
|
|
92
|
+
"coreml_embed",
|
|
93
|
+
[{"input": chunked(doc)} for doc in corpus],
|
|
94
|
+
max_concurrent=16,
|
|
95
|
+
):
|
|
96
|
+
save(result.output) # results stream as they complete
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. Background removal
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
job = client.images.remove_background("product-shot.png")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Also live: speech synthesis, image classification/detection/pose, barcode
|
|
106
|
+
reading, HEIC conversion — plus the generic `client.submit(workload_id,
|
|
107
|
+
payload)` for anything marked **live** in
|
|
108
|
+
[the catalog](https://commoncompute.ai/workloads).
|
|
109
|
+
|
|
110
|
+
### In preview
|
|
111
|
+
|
|
112
|
+
`client.translate(...)` and `client.video.transcode(...)` run on preview
|
|
113
|
+
capacity — functional, not yet GA.
|
|
114
|
+
|
|
115
|
+
### Not live yet
|
|
116
|
+
|
|
117
|
+
`client.transcription.*`, `client.rerank(...)`, `client.images.generate(...)`,
|
|
118
|
+
`client.chat.*`, and `client.build.ios_test(...)` target workloads still
|
|
119
|
+
marked *coming soon*: the API refuses them with a clear
|
|
120
|
+
`workload_not_available` error (HTTP 409) rather than queueing a job that
|
|
121
|
+
won't run. They activate automatically as those lanes go live — watch
|
|
122
|
+
[the catalog](https://commoncompute.ai/workloads).
|
|
123
|
+
|
|
124
|
+
## Price before execution, hard caps, dry runs
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
quote = client.ocr.extract("scan.pdf", dry_run=True) # price + ETA, no job
|
|
128
|
+
job = client.ocr.extract("scan.pdf", max_spend_usd=1.0) # refused if it would exceed
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Batches that stream back
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
for result in client.submit_many("vision_bgremove", images, max_concurrent=8):
|
|
135
|
+
save(result.output) # results stream as they complete
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Receipts
|
|
139
|
+
|
|
140
|
+
Every successful job carries a receipt (`job_id`, cost, provider id,
|
|
141
|
+
timestamps, input/output hashes, signature):
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
client.receipts.list()
|
|
145
|
+
csv_blob = client.receipts.export(format="csv")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Account
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
client.account.balance() # card-on-file billing state (cash only)
|
|
152
|
+
client.account.spend(days=30) # spend summary by workload
|
|
153
|
+
client.account.tier() # your volume tier + the next threshold
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Per-task prices step down automatically as your monthly usage grows —
|
|
157
|
+
your current rate is always the one a quote returns.
|
|
158
|
+
|
|
159
|
+
## Async
|
|
160
|
+
|
|
161
|
+
`AsyncClient` mirrors `Client` method-for-method — `submit`, `wait`, `result`,
|
|
162
|
+
and `submit_many` all have `await`-able twins. Use it as an async context
|
|
163
|
+
manager so the underlying HTTP pool is closed for you:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
import asyncio
|
|
167
|
+
import commoncompute as cc
|
|
168
|
+
|
|
169
|
+
async def main():
|
|
170
|
+
async with cc.AsyncClient() as client: # closes the pool on exit
|
|
171
|
+
job = await client.submit("coreml_embed", {"input": ["hello world"]})
|
|
172
|
+
print(job.job_id, job.locked_price_usd, job.eta_seconds) # price BEFORE it runs
|
|
173
|
+
|
|
174
|
+
await client.wait(job) # poll until terminal
|
|
175
|
+
result = await client.result(job) # + download output & receipt
|
|
176
|
+
print(result.output)
|
|
177
|
+
|
|
178
|
+
asyncio.run(main())
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Batches stream back the same way — `submit_many` is an async generator:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
async with cc.AsyncClient() as client:
|
|
185
|
+
async for r in client.submit_many("coreml_embed", batches, max_concurrent=8):
|
|
186
|
+
save(r.output) # each result as it completes
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The lower-level `client.jobs.*` namespace (`jobs.submit`, `jobs.wait`,
|
|
190
|
+
`jobs.get`, `jobs.events`, `jobs.download`) is async here too when you want the
|
|
191
|
+
raw request/response dicts instead of typed `Job`/`TaskResult` objects.
|
|
192
|
+
|
|
193
|
+
## CLI
|
|
194
|
+
|
|
195
|
+
`pip install "commoncompute[cli]"` installs the `commoncompute` command.
|
|
196
|
+
|
|
197
|
+
> **Why not a short `cc`?** Because `cc` is the Unix C compiler
|
|
198
|
+
> (`/usr/bin/cc`). Shipping a binary by that name puts it ahead of the real
|
|
199
|
+
> compiler on `PATH` for anyone whose Python bin directory sorts first, and
|
|
200
|
+
> every build that shells out to `cc` then fails with a CLI usage error
|
|
201
|
+
> instead of compiling. So `commoncompute` is the only command installed. If
|
|
202
|
+
> you want the shorthand, opt in yourself:
|
|
203
|
+
>
|
|
204
|
+
> ```bash
|
|
205
|
+
> alias cc="commoncompute" # add to ~/.zshrc or ~/.bashrc
|
|
206
|
+
> ```
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
commoncompute login # stores the key locally
|
|
210
|
+
commoncompute quote vision_ocr --units 5 # price + ETA, no execution
|
|
211
|
+
commoncompute submit coreml_embed --payload '{"input":["hi"]}' --wait
|
|
212
|
+
commoncompute jobs list
|
|
213
|
+
commoncompute jobs get <id>
|
|
214
|
+
commoncompute balance
|
|
215
|
+
commoncompute receipts export --format csv --out receipts.csv
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Commands
|
|
219
|
+
|
|
220
|
+
| Command | What it does |
|
|
221
|
+
|---------|--------------|
|
|
222
|
+
| `commoncompute login` / `commoncompute logout` | Store / remove the API key (browser approval, or `--api-key`) |
|
|
223
|
+
| `commoncompute whoami` | Show the account behind the current key |
|
|
224
|
+
| `commoncompute balance` | Current workspace balance |
|
|
225
|
+
| `commoncompute usage` | Spend + call-count rollups over a date range (`--start`, `--end`, `--group-by`) |
|
|
226
|
+
| `commoncompute spend` | Total spend over the last `--days` N, grouped by workload |
|
|
227
|
+
| `commoncompute tier` | Current volume tier and the next threshold |
|
|
228
|
+
| `commoncompute workloads` | List available workloads |
|
|
229
|
+
| `commoncompute models` | List models (`--workload` to filter) |
|
|
230
|
+
| `commoncompute quote` | Price + ETA for a workload without submitting (`--units`, `--priority`, `--model`) |
|
|
231
|
+
| `commoncompute submit` | Submit one job (`--payload/-f`, `--model`, `--priority`, `--wait`, `--timeout`) |
|
|
232
|
+
| `commoncompute submit-many` | Submit a JSONL file of payloads; stream one result per line as NDJSON (`--input/-i`, `--max-concurrent`, `--no-wait`) |
|
|
233
|
+
| `commoncompute chat` | One-shot chat message to a model (`--model`, `--stream/--no-stream`) |
|
|
234
|
+
| `commoncompute embed` | Embed a single string (prints the vector length) |
|
|
235
|
+
| `commoncompute playground` | Open the web playground |
|
|
236
|
+
| `commoncompute jobs list` | Recent submissions, newest first (`--limit/-n`) |
|
|
237
|
+
| `commoncompute jobs get <id>` | Fetch one job |
|
|
238
|
+
| `commoncompute jobs wait <id>` | Poll a job to a terminal state (`--timeout/-t`) |
|
|
239
|
+
| `commoncompute jobs events <id>` | Event timeline for a job |
|
|
240
|
+
| `commoncompute jobs download <id>` | Download a job's result (`--out/-o`, else stdout) |
|
|
241
|
+
| `commoncompute keys list` / `create` / `revoke` | Manage API keys |
|
|
242
|
+
| `commoncompute receipts list` | Receipts for completed jobs (`--limit/-n`) |
|
|
243
|
+
| `commoncompute receipts get <id>` | Signed receipt for a single job |
|
|
244
|
+
| `commoncompute receipts export` | Export receipts as CSV or JSON (`--format/-f`, `--out/-o`, `--limit/-n`) |
|
|
245
|
+
| `commoncompute config path` / `show` / `set` / `unset` | Inspect and edit local settings (see below) |
|
|
246
|
+
|
|
247
|
+
`commoncompute --version` (or `-V`) prints the version. Every read command that renders a
|
|
248
|
+
table also accepts **`--json`** for plain, ANSI-free machine-readable output —
|
|
249
|
+
`whoami`, `balance`, `usage`, `spend`, `tier`, `workloads`, `models`, `quote`,
|
|
250
|
+
`submit`, `jobs list/get/wait/events`, `keys list`, and `receipts list`.
|
|
251
|
+
|
|
252
|
+
### Shell completion
|
|
253
|
+
|
|
254
|
+
Typer ships completion for bash, zsh, fish, and PowerShell:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
commoncompute --install-completion # install for the current shell
|
|
258
|
+
commoncompute --show-completion # print the script to inspect or customise
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Exit codes
|
|
262
|
+
|
|
263
|
+
Scripts can branch on `commoncompute`'s exit status:
|
|
264
|
+
|
|
265
|
+
| Code | Meaning |
|
|
266
|
+
|------|---------|
|
|
267
|
+
| `0` | Success |
|
|
268
|
+
| `1` | API or runtime error (network, server, bad request) |
|
|
269
|
+
| `2` | Usage error — missing/invalid arguments, or no credentials found |
|
|
270
|
+
| `3` | The job reached a **failed** terminal state (`failed` / `dead_letter` / `cancelled`) — `commoncompute jobs wait` and `commoncompute submit --wait` only |
|
|
271
|
+
|
|
272
|
+
## Migrating from OpenAI (optional)
|
|
273
|
+
|
|
274
|
+
Existing OpenAI-based pipelines (embeddings, chat, transcription) can point
|
|
275
|
+
at Common Compute by swapping two env vars — or:
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
from commoncompute.compat import openai # sets OPENAI_BASE_URL / OPENAI_API_KEY
|
|
279
|
+
client = openai.OpenAI()
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
This is a migration path, not the recommended interface: the native client
|
|
283
|
+
returns locked prices, ETAs, and receipts that the OpenAI wire format can't
|
|
284
|
+
express.
|
|
285
|
+
|
|
286
|
+
## Errors
|
|
287
|
+
|
|
288
|
+
Typed, always:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
try:
|
|
292
|
+
client.ocr.extract("scan.pdf", max_spend_usd=0.01)
|
|
293
|
+
except cc.InsufficientFundsError: # quote exceeded the cap / no card
|
|
294
|
+
...
|
|
295
|
+
except cc.PermissionDeniedError: # key lacks scope for this workload
|
|
296
|
+
...
|
|
297
|
+
except cc.ConflictError: # idempotency-key or state conflict (409)
|
|
298
|
+
...
|
|
299
|
+
except cc.UnsupportedFormatError: # wrong file type for the workload
|
|
300
|
+
...
|
|
301
|
+
except cc.JobTimeoutError: # wait() expired; job still running
|
|
302
|
+
...
|
|
303
|
+
except cc.NetworkError: # no HTTP response after retries
|
|
304
|
+
...
|
|
305
|
+
except cc.CommonComputeError as e: # everything raises from this
|
|
306
|
+
print(e.request_id)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The full hierarchy — `AuthenticationError`, `PermissionDeniedError`,
|
|
310
|
+
`NotFoundError`, `ConflictError`, `RateLimitError`, `InsufficientFundsError`,
|
|
311
|
+
`BadRequestError`, `ValidationError`, `UnsupportedFormatError`, `NetworkError`,
|
|
312
|
+
`JobTimeoutError`, `APIError` — all subclass `CommonComputeError`.
|
|
313
|
+
|
|
314
|
+
## Configuration
|
|
315
|
+
|
|
316
|
+
Credentials and settings live under one canonical directory (override the whole
|
|
317
|
+
directory with `$CC_CONFIG_DIR`):
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
~/.config/commoncompute/credentials # the API key (single line)
|
|
321
|
+
~/.config/commoncompute/config # settings: base_url, org (key = value)
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Two **legacy** locations are still read (never written) so older installs keep
|
|
325
|
+
working: `~/.commoncompute/config` and `~/.commoncompute/credentials`.
|
|
326
|
+
|
|
327
|
+
Environment variables take precedence over both files at runtime:
|
|
328
|
+
|
|
329
|
+
| Setting | Env var | Resolution order (first match wins) |
|
|
330
|
+
|----------|-----------------|-------------------------------------|
|
|
331
|
+
| API key | `CC_API_KEY` | env → `~/.config/commoncompute/credentials` → `~/.commoncompute/config` → `~/.commoncompute/credentials` |
|
|
332
|
+
| Base URL | `CC_BASE_URL` | env → `config` file `base_url` → `https://api.commoncompute.ai` |
|
|
333
|
+
| Org id | `CC_ORG` | env → `config` file `org` → default workspace |
|
|
334
|
+
| Config dir | `CC_CONFIG_DIR` | overrides the `~/.config/commoncompute` location above |
|
|
335
|
+
|
|
336
|
+
Inspect and edit the local config from the CLI:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
commoncompute config path # print the config directory
|
|
340
|
+
commoncompute config show # settings + active credential source (key masked)
|
|
341
|
+
commoncompute config set base_url https://api.commoncompute.ai
|
|
342
|
+
commoncompute config set org my-workspace
|
|
343
|
+
commoncompute config unset org
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Env vars still win at runtime — `commoncompute config set` writes the file, but
|
|
347
|
+
`CC_BASE_URL` / `CC_ORG` override it for a given process.
|
|
348
|
+
|
|
349
|
+
Python 3.9+. Core dependencies: `httpx`, `pydantic`. MIT license.
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Common Compute — Python SDK
|
|
2
|
+
|
|
3
|
+
The official Python SDK + CLI for [Common Compute](https://commoncompute.ai) —
|
|
4
|
+
batch AI compute without the AWS tax, on Apple Silicon hardware AWS can't offer.
|
|
5
|
+
|
|
6
|
+
One SDK call replaces the IAM roles, compute environments, and job definitions.
|
|
7
|
+
Every job returns its **price and ETA before it runs**, and you're only billed
|
|
8
|
+
for **successful** jobs — each with a verifiable receipt.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install commoncompute # core: httpx + pydantic only
|
|
12
|
+
pip install "commoncompute[cli]" # + the `commoncompute` command line
|
|
13
|
+
commoncompute login # opens the browser — no key to copy-paste
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The command is `commoncompute`. A short `cc` is **not** installed — that name
|
|
17
|
+
belongs to the system C compiler. Want the shorthand? `alias cc="commoncompute"`.
|
|
18
|
+
|
|
19
|
+
`commoncompute login` (or `commoncompute.connect()` from a script or notebook) opens an
|
|
20
|
+
approval page in your browser; click **Approve** and a fresh API key is saved
|
|
21
|
+
to `~/.config/commoncompute/credentials`, where the SDK finds it automatically.
|
|
22
|
+
Prefer explicit config? `export CC_API_KEY=cc_live_...` works everywhere and
|
|
23
|
+
takes precedence.
|
|
24
|
+
|
|
25
|
+
## The 10-minute path
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import commoncompute as cc
|
|
29
|
+
|
|
30
|
+
client = cc.Client() # reads CC_API_KEY, or the saved credentials file
|
|
31
|
+
|
|
32
|
+
job = client.submit("coreml_embed", {"input": ["what is the neural engine?"]},
|
|
33
|
+
model_id="bge-base")
|
|
34
|
+
print(job.job_id, job.locked_price_usd, job.eta_seconds) # price known BEFORE it runs
|
|
35
|
+
|
|
36
|
+
result = client.result(job) # waits, downloads, attaches the receipt
|
|
37
|
+
print(result.output)
|
|
38
|
+
print(result.receipt) # signed proof of what ran, where, for how much
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Prefer the OpenAI-style surface? `client.embeddings.create(input=[...],
|
|
42
|
+
model="bge-base")` returns vectors synchronously.
|
|
43
|
+
|
|
44
|
+
## Examples — live today
|
|
45
|
+
|
|
46
|
+
### 1. OCR / document extraction — vs AWS Textract
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
job = client.ocr.extract("https://example.com/contracts.pdf", structured=True)
|
|
50
|
+
result = client.result(job) # blocks + bounding boxes as JSON
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Embeddings in bulk
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
for result in client.submit_many(
|
|
57
|
+
"coreml_embed",
|
|
58
|
+
[{"input": chunked(doc)} for doc in corpus],
|
|
59
|
+
max_concurrent=16,
|
|
60
|
+
):
|
|
61
|
+
save(result.output) # results stream as they complete
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. Background removal
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
job = client.images.remove_background("product-shot.png")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Also live: speech synthesis, image classification/detection/pose, barcode
|
|
71
|
+
reading, HEIC conversion — plus the generic `client.submit(workload_id,
|
|
72
|
+
payload)` for anything marked **live** in
|
|
73
|
+
[the catalog](https://commoncompute.ai/workloads).
|
|
74
|
+
|
|
75
|
+
### In preview
|
|
76
|
+
|
|
77
|
+
`client.translate(...)` and `client.video.transcode(...)` run on preview
|
|
78
|
+
capacity — functional, not yet GA.
|
|
79
|
+
|
|
80
|
+
### Not live yet
|
|
81
|
+
|
|
82
|
+
`client.transcription.*`, `client.rerank(...)`, `client.images.generate(...)`,
|
|
83
|
+
`client.chat.*`, and `client.build.ios_test(...)` target workloads still
|
|
84
|
+
marked *coming soon*: the API refuses them with a clear
|
|
85
|
+
`workload_not_available` error (HTTP 409) rather than queueing a job that
|
|
86
|
+
won't run. They activate automatically as those lanes go live — watch
|
|
87
|
+
[the catalog](https://commoncompute.ai/workloads).
|
|
88
|
+
|
|
89
|
+
## Price before execution, hard caps, dry runs
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
quote = client.ocr.extract("scan.pdf", dry_run=True) # price + ETA, no job
|
|
93
|
+
job = client.ocr.extract("scan.pdf", max_spend_usd=1.0) # refused if it would exceed
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Batches that stream back
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
for result in client.submit_many("vision_bgremove", images, max_concurrent=8):
|
|
100
|
+
save(result.output) # results stream as they complete
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Receipts
|
|
104
|
+
|
|
105
|
+
Every successful job carries a receipt (`job_id`, cost, provider id,
|
|
106
|
+
timestamps, input/output hashes, signature):
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
client.receipts.list()
|
|
110
|
+
csv_blob = client.receipts.export(format="csv")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Account
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
client.account.balance() # card-on-file billing state (cash only)
|
|
117
|
+
client.account.spend(days=30) # spend summary by workload
|
|
118
|
+
client.account.tier() # your volume tier + the next threshold
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Per-task prices step down automatically as your monthly usage grows —
|
|
122
|
+
your current rate is always the one a quote returns.
|
|
123
|
+
|
|
124
|
+
## Async
|
|
125
|
+
|
|
126
|
+
`AsyncClient` mirrors `Client` method-for-method — `submit`, `wait`, `result`,
|
|
127
|
+
and `submit_many` all have `await`-able twins. Use it as an async context
|
|
128
|
+
manager so the underlying HTTP pool is closed for you:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import asyncio
|
|
132
|
+
import commoncompute as cc
|
|
133
|
+
|
|
134
|
+
async def main():
|
|
135
|
+
async with cc.AsyncClient() as client: # closes the pool on exit
|
|
136
|
+
job = await client.submit("coreml_embed", {"input": ["hello world"]})
|
|
137
|
+
print(job.job_id, job.locked_price_usd, job.eta_seconds) # price BEFORE it runs
|
|
138
|
+
|
|
139
|
+
await client.wait(job) # poll until terminal
|
|
140
|
+
result = await client.result(job) # + download output & receipt
|
|
141
|
+
print(result.output)
|
|
142
|
+
|
|
143
|
+
asyncio.run(main())
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Batches stream back the same way — `submit_many` is an async generator:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
async with cc.AsyncClient() as client:
|
|
150
|
+
async for r in client.submit_many("coreml_embed", batches, max_concurrent=8):
|
|
151
|
+
save(r.output) # each result as it completes
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The lower-level `client.jobs.*` namespace (`jobs.submit`, `jobs.wait`,
|
|
155
|
+
`jobs.get`, `jobs.events`, `jobs.download`) is async here too when you want the
|
|
156
|
+
raw request/response dicts instead of typed `Job`/`TaskResult` objects.
|
|
157
|
+
|
|
158
|
+
## CLI
|
|
159
|
+
|
|
160
|
+
`pip install "commoncompute[cli]"` installs the `commoncompute` command.
|
|
161
|
+
|
|
162
|
+
> **Why not a short `cc`?** Because `cc` is the Unix C compiler
|
|
163
|
+
> (`/usr/bin/cc`). Shipping a binary by that name puts it ahead of the real
|
|
164
|
+
> compiler on `PATH` for anyone whose Python bin directory sorts first, and
|
|
165
|
+
> every build that shells out to `cc` then fails with a CLI usage error
|
|
166
|
+
> instead of compiling. So `commoncompute` is the only command installed. If
|
|
167
|
+
> you want the shorthand, opt in yourself:
|
|
168
|
+
>
|
|
169
|
+
> ```bash
|
|
170
|
+
> alias cc="commoncompute" # add to ~/.zshrc or ~/.bashrc
|
|
171
|
+
> ```
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
commoncompute login # stores the key locally
|
|
175
|
+
commoncompute quote vision_ocr --units 5 # price + ETA, no execution
|
|
176
|
+
commoncompute submit coreml_embed --payload '{"input":["hi"]}' --wait
|
|
177
|
+
commoncompute jobs list
|
|
178
|
+
commoncompute jobs get <id>
|
|
179
|
+
commoncompute balance
|
|
180
|
+
commoncompute receipts export --format csv --out receipts.csv
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Commands
|
|
184
|
+
|
|
185
|
+
| Command | What it does |
|
|
186
|
+
|---------|--------------|
|
|
187
|
+
| `commoncompute login` / `commoncompute logout` | Store / remove the API key (browser approval, or `--api-key`) |
|
|
188
|
+
| `commoncompute whoami` | Show the account behind the current key |
|
|
189
|
+
| `commoncompute balance` | Current workspace balance |
|
|
190
|
+
| `commoncompute usage` | Spend + call-count rollups over a date range (`--start`, `--end`, `--group-by`) |
|
|
191
|
+
| `commoncompute spend` | Total spend over the last `--days` N, grouped by workload |
|
|
192
|
+
| `commoncompute tier` | Current volume tier and the next threshold |
|
|
193
|
+
| `commoncompute workloads` | List available workloads |
|
|
194
|
+
| `commoncompute models` | List models (`--workload` to filter) |
|
|
195
|
+
| `commoncompute quote` | Price + ETA for a workload without submitting (`--units`, `--priority`, `--model`) |
|
|
196
|
+
| `commoncompute submit` | Submit one job (`--payload/-f`, `--model`, `--priority`, `--wait`, `--timeout`) |
|
|
197
|
+
| `commoncompute submit-many` | Submit a JSONL file of payloads; stream one result per line as NDJSON (`--input/-i`, `--max-concurrent`, `--no-wait`) |
|
|
198
|
+
| `commoncompute chat` | One-shot chat message to a model (`--model`, `--stream/--no-stream`) |
|
|
199
|
+
| `commoncompute embed` | Embed a single string (prints the vector length) |
|
|
200
|
+
| `commoncompute playground` | Open the web playground |
|
|
201
|
+
| `commoncompute jobs list` | Recent submissions, newest first (`--limit/-n`) |
|
|
202
|
+
| `commoncompute jobs get <id>` | Fetch one job |
|
|
203
|
+
| `commoncompute jobs wait <id>` | Poll a job to a terminal state (`--timeout/-t`) |
|
|
204
|
+
| `commoncompute jobs events <id>` | Event timeline for a job |
|
|
205
|
+
| `commoncompute jobs download <id>` | Download a job's result (`--out/-o`, else stdout) |
|
|
206
|
+
| `commoncompute keys list` / `create` / `revoke` | Manage API keys |
|
|
207
|
+
| `commoncompute receipts list` | Receipts for completed jobs (`--limit/-n`) |
|
|
208
|
+
| `commoncompute receipts get <id>` | Signed receipt for a single job |
|
|
209
|
+
| `commoncompute receipts export` | Export receipts as CSV or JSON (`--format/-f`, `--out/-o`, `--limit/-n`) |
|
|
210
|
+
| `commoncompute config path` / `show` / `set` / `unset` | Inspect and edit local settings (see below) |
|
|
211
|
+
|
|
212
|
+
`commoncompute --version` (or `-V`) prints the version. Every read command that renders a
|
|
213
|
+
table also accepts **`--json`** for plain, ANSI-free machine-readable output —
|
|
214
|
+
`whoami`, `balance`, `usage`, `spend`, `tier`, `workloads`, `models`, `quote`,
|
|
215
|
+
`submit`, `jobs list/get/wait/events`, `keys list`, and `receipts list`.
|
|
216
|
+
|
|
217
|
+
### Shell completion
|
|
218
|
+
|
|
219
|
+
Typer ships completion for bash, zsh, fish, and PowerShell:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
commoncompute --install-completion # install for the current shell
|
|
223
|
+
commoncompute --show-completion # print the script to inspect or customise
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Exit codes
|
|
227
|
+
|
|
228
|
+
Scripts can branch on `commoncompute`'s exit status:
|
|
229
|
+
|
|
230
|
+
| Code | Meaning |
|
|
231
|
+
|------|---------|
|
|
232
|
+
| `0` | Success |
|
|
233
|
+
| `1` | API or runtime error (network, server, bad request) |
|
|
234
|
+
| `2` | Usage error — missing/invalid arguments, or no credentials found |
|
|
235
|
+
| `3` | The job reached a **failed** terminal state (`failed` / `dead_letter` / `cancelled`) — `commoncompute jobs wait` and `commoncompute submit --wait` only |
|
|
236
|
+
|
|
237
|
+
## Migrating from OpenAI (optional)
|
|
238
|
+
|
|
239
|
+
Existing OpenAI-based pipelines (embeddings, chat, transcription) can point
|
|
240
|
+
at Common Compute by swapping two env vars — or:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
from commoncompute.compat import openai # sets OPENAI_BASE_URL / OPENAI_API_KEY
|
|
244
|
+
client = openai.OpenAI()
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
This is a migration path, not the recommended interface: the native client
|
|
248
|
+
returns locked prices, ETAs, and receipts that the OpenAI wire format can't
|
|
249
|
+
express.
|
|
250
|
+
|
|
251
|
+
## Errors
|
|
252
|
+
|
|
253
|
+
Typed, always:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
try:
|
|
257
|
+
client.ocr.extract("scan.pdf", max_spend_usd=0.01)
|
|
258
|
+
except cc.InsufficientFundsError: # quote exceeded the cap / no card
|
|
259
|
+
...
|
|
260
|
+
except cc.PermissionDeniedError: # key lacks scope for this workload
|
|
261
|
+
...
|
|
262
|
+
except cc.ConflictError: # idempotency-key or state conflict (409)
|
|
263
|
+
...
|
|
264
|
+
except cc.UnsupportedFormatError: # wrong file type for the workload
|
|
265
|
+
...
|
|
266
|
+
except cc.JobTimeoutError: # wait() expired; job still running
|
|
267
|
+
...
|
|
268
|
+
except cc.NetworkError: # no HTTP response after retries
|
|
269
|
+
...
|
|
270
|
+
except cc.CommonComputeError as e: # everything raises from this
|
|
271
|
+
print(e.request_id)
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The full hierarchy — `AuthenticationError`, `PermissionDeniedError`,
|
|
275
|
+
`NotFoundError`, `ConflictError`, `RateLimitError`, `InsufficientFundsError`,
|
|
276
|
+
`BadRequestError`, `ValidationError`, `UnsupportedFormatError`, `NetworkError`,
|
|
277
|
+
`JobTimeoutError`, `APIError` — all subclass `CommonComputeError`.
|
|
278
|
+
|
|
279
|
+
## Configuration
|
|
280
|
+
|
|
281
|
+
Credentials and settings live under one canonical directory (override the whole
|
|
282
|
+
directory with `$CC_CONFIG_DIR`):
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
~/.config/commoncompute/credentials # the API key (single line)
|
|
286
|
+
~/.config/commoncompute/config # settings: base_url, org (key = value)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Two **legacy** locations are still read (never written) so older installs keep
|
|
290
|
+
working: `~/.commoncompute/config` and `~/.commoncompute/credentials`.
|
|
291
|
+
|
|
292
|
+
Environment variables take precedence over both files at runtime:
|
|
293
|
+
|
|
294
|
+
| Setting | Env var | Resolution order (first match wins) |
|
|
295
|
+
|----------|-----------------|-------------------------------------|
|
|
296
|
+
| API key | `CC_API_KEY` | env → `~/.config/commoncompute/credentials` → `~/.commoncompute/config` → `~/.commoncompute/credentials` |
|
|
297
|
+
| Base URL | `CC_BASE_URL` | env → `config` file `base_url` → `https://api.commoncompute.ai` |
|
|
298
|
+
| Org id | `CC_ORG` | env → `config` file `org` → default workspace |
|
|
299
|
+
| Config dir | `CC_CONFIG_DIR` | overrides the `~/.config/commoncompute` location above |
|
|
300
|
+
|
|
301
|
+
Inspect and edit the local config from the CLI:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
commoncompute config path # print the config directory
|
|
305
|
+
commoncompute config show # settings + active credential source (key masked)
|
|
306
|
+
commoncompute config set base_url https://api.commoncompute.ai
|
|
307
|
+
commoncompute config set org my-workspace
|
|
308
|
+
commoncompute config unset org
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Env vars still win at runtime — `commoncompute config set` writes the file, but
|
|
312
|
+
`CC_BASE_URL` / `CC_ORG` override it for a given process.
|
|
313
|
+
|
|
314
|
+
Python 3.9+. Core dependencies: `httpx`, `pydantic`. MIT license.
|
|
@@ -30,13 +30,15 @@ in the catalog — the API declines them with `workload_not_available`
|
|
|
30
30
|
# `commoncompute.__version__` at import time for the User-Agent header,
|
|
31
31
|
# and the previous ordering (`__version__` defined at the bottom) made
|
|
32
32
|
# every clean install of the SDK fail with a circular ImportError.
|
|
33
|
-
__version__ = "0.
|
|
33
|
+
__version__ = "0.3.0"
|
|
34
34
|
|
|
35
35
|
from .client import Client, AsyncClient
|
|
36
36
|
from .errors import (
|
|
37
37
|
CommonComputeError,
|
|
38
38
|
AuthenticationError,
|
|
39
|
+
PermissionDeniedError,
|
|
39
40
|
NotFoundError,
|
|
41
|
+
ConflictError,
|
|
40
42
|
RateLimitError,
|
|
41
43
|
InsufficientFundsError,
|
|
42
44
|
InsufficientCreditsError, # legacy alias of InsufficientFundsError
|
|
@@ -58,7 +60,9 @@ __all__ = [
|
|
|
58
60
|
"login",
|
|
59
61
|
"CommonComputeError",
|
|
60
62
|
"AuthenticationError",
|
|
63
|
+
"PermissionDeniedError",
|
|
61
64
|
"NotFoundError",
|
|
65
|
+
"ConflictError",
|
|
62
66
|
"RateLimitError",
|
|
63
67
|
"InsufficientFundsError",
|
|
64
68
|
"InsufficientCreditsError",
|