Native Python SDK for the UCIN Network Factory — import ucin, wrap a function, pick a tier.
Project description
UCIN SDK — import ucin
Native Python integration for the UCIN Network Factory. Wrap a function, pick a tier, get a deterministic SLA quote, accept the receipt, and the function runs on the network — its return value comes back inline.
import ucin
@ucin.workload(tier="flex")
def fine_tune_llama(dataset_path):
import torch
# ... standard PyTorch training loop ...
return {"final_loss": 0.21}
result = fine_tune_llama("s3://my-bucket/data") # quote → [Y/n] → run remotely
print(result["final_loss"])
Running python train.py does not execute the function locally. The SDK
packages the call, asks the control plane for a quote, and pauses with the
interactive receipt:
╔════════════════════════════════════════════════════════════╗
║ UCIN SLA QUOTE: Flex Tier ║
╠════════════════════════════════════════════════════════════╣
║ Workload classified : training (14.00 SCU) ║
║ Profiler confidence : 91% ║
║ Estimated completion : 3h 15m ║
║ SLA guarantee (≤) : 4h 00m ║
║ Target hardware : H100 ║
║ Total guaranteed cost : ₹540.00 ║
╚════════════════════════════════════════════════════════════╝
Accept this quote and deploy to Network Factory? [Y/n]:
Why this maps cleanly onto the existing platform
Every number on the receipt is the control plane's, not the SDK's. The SDK is a thin client — all pricing, profiling, SLA and billing logic already exists server-side:
| Receipt field | Source in the codebase |
|---|---|
| Workload + SCU | auto_profiler.WorkloadProfiler.profile_workload |
| Confidence | auto_profiler._reconcile |
| Estimated completion | customer_api._build_response_fields (etc_hrs) |
| SLA guarantee (≤) | customer_api._apply_churn (etc_max_hrs) |
| Total guaranteed cost | EstimateResponse.quoted_price_inr — a hard wallet pre-auth |
| Budget hard-stop | gateway_api.py NF-7: billing clamps at ceiling_scu, complete_at_ceiling |
| "never billed over quote" | main.py heartbeat loop: force-fail removed; UCIN absorbs overruns |
The flow uses three real endpoints, all Bearer-authenticated under /customer:
POST /jobs/audit— the pre-acceptance gauntlet (security scan, profile, capacity, balance). Returns the quote and theaudit_tokendeploy requires in production.POST /jobs/deploy— pre-authorisesquoted_price_inragainst the wallet and queues theJob. The SOR (sor.dispatch_loop) places it.GET /jobs/{id}/GET /jobs/{id}/results— poll to completion, then pull the result artifact.
The one real architectural gap (and how this bridges it)
The platform runs container images, and auto_profiler classifies a job by
substring-matching the image URL and run_command ("torchrun", "vllm",
":latest") — there is no AST analysis today, despite the original vision.
So @ucin.workload cannot "just send the function." The bridge (packaging.py
runner.py):
- Client side:
cloudpickle(func, args, kwargs)+ a capturedpip freeze→ uploaded viaPOST /storage/presign. A syntheticrun_commandcarries both the payload key and keywords (torchrun train.py …) so the existing heuristic profiler classifies the workload with no server change. - Container side: a generic runner image (
ucin/pyrunner) whose CMD isucin-run. It fetches the payload, tops up deps, calls the function, and writes the return value toUCIN_CHECKPOINT_DIR/ucin-result.pkl— whichentrypoint_wrapper.py's existing checkpoint upload pipeline already ships to object storage (AWS S3 Mumbai or E2E — S3-compatible, India-resident).
This reuses the preemption shield, checkpoint/resume, cooperative ceiling, and billing untouched.
Budget & time guarantee — tasks can never overrun
Set a hard ceiling and the SDK guarantees you never pay above it:
@ucin.workload(tier="flex", max_budget_inr=500) # ₹ cap
def train(path): ...
@ucin.workload(tier="flex", max_runtime_hrs=4) # wall-clock cap
def train(path): ...
How it's enforced, end to end:
- SDK converts
max_budget_inr→ the exact runtime ceiling using the tier rates fromGET /customer/config, audits, and re-audits until the server's ownquoted_price_inr≤ your budget (the quote is linear in runtime, so one Newton step lands it). It refuses to deploy otherwise. - Control plane pins that as
estimated_scuat 0.95 confidence (_apply_runtime_ceiling) →quoted_price_inris exact, and the wallet is pre-authorised for exactly that. - Gateway computes
ceiling_scu = (quoted − base_fee) / rateand, on every billing tick, clampsscu_consumedat the ceiling and returnscomplete_at_ceiling: true— the job is finalised at that boundary (gateway_api.pyNF-7).actual_cost ≤ quoted_priceis an invariant. - Container receives
UCIN_MAX_SCU;entrypoint_wrapper.py's ceiling watcher SIGTERMs the workload for a graceful checkpoint at 90%, before the hard clamp — so the stop happens at a checkpoint, not mid-step.
Completion vs. truncation. The cap guarantees spend, not that the work
finishes. The SDK fetches the platform's uncapped estimate too; if your cap is
below it, the receipt warns the job will stop at the ceiling unfinished, and in
non-interactive mode run() raises BudgetError unless you pass
allow_truncation=True. To guarantee completion within budget, set a cap at
or above the estimate (the receipt shows both numbers).
Install & authenticate
pip install ucin
ucin login # prompts for email + password; token saved to ~/.ucin
ucin whoami # confirm the signed-in account
After ucin login, every import ucin script picks up the token automatically —
no env vars to manage. Programmatic alternative:
import ucin
ucin.login("dev@startup.ai", "hunter2") # or totp_code=... if MFA is on
Token precedence: configure() / UCIN_API_TOKEN → persisted ucin login
(~/.ucin/credentials.json, keyed by control-plane URL, written 0600).
Configuration
ucin.configure(base_url="https://api.ucin.in", api_token="ey...")
or via env: UCIN_BASE_URL, UCIN_API_TOKEN (from POST /auth/login),
UCIN_AUTO_YES=true (skip the prompt in CI), UCIN_RUNNER_IMAGE,
UCIN_CONFIG_DIR (override the ~/.ucin credential dir).
Wallet & funding
Deploy places a hard wallet pre-authorisation equal to the quoted price, so you need a funded wallet. Check and top up from the SDK:
ucin.wallet() # {"user_id": ..., "wallet_balance_inr": 250.0}
ucin.topup(500) # opens a Cashfree top-up order (sandbox/production)
or the CLI:
ucin wallet # show balance
ucin topup 500 # create a Cashfree order; complete it in the web app
topup() returns a Cashfree payment_session_id; the card/UPI payment is
completed via the hosted checkout in the UCIN web app, after which the wallet is
credited (webhook / payment-success). A deploy with insufficient balance raises
InsufficientBalance pointing you to ucin topup. On completion, the unused
portion of the hold (quote − actual_cost) is credited back automatically.
Escape hatches
fine_tune_llama.local(path) # run in-process, bypass UCIN (dev/debug)
fine_tune_llama.estimate(path) # quote only — no deploy, no prompt
fine_tune_llama.submit(path) # deploy after receipt, return job handle (non-blocking)
Decorator options
@ucin.workload(
tier="flex", # priority | core | flex
workload_hint="training", # training | inference | batch | auto (profiler signal)
state_lock="Maharashtra", # required for core tier (data residency)
express=True, # CPTV express routing → queue_priority=0
gpu_count=2, # parallelism width (e.g. DDP), NOT a GPU model
# — the Smart Order Router picks the hardware
max_budget_inr=500, # hard ₹ ceiling — never billed above this
max_runtime_hrs=4, # OR a hard wall-clock ceiling
allow_truncation=False, # non-interactive: forbid caps below the estimate
requirements=["torch==2.3.0"],
)
def job(...): ...
Server-side work still required to make this fully live
The three integration points are now wired end to end (control plane + data plane + image):
- ✅ Result artifacts in
/jobs/{id}/results. On clean completion the wrapper (entrypoint_wrapper._persist_outputs_on_success) uploadsCHECKPOINT_DIRand reports the key; the CP status handler persists it toJob.checkpoint_id(gateway_api.py,JobStatusUpdateRequest.checkpoint_r2_key);/customer/jobs/{id}/resultslabels itucin-result.pklwithsource_path="checkpoint/ucin-result.pkl", and the SDK extracts the return value from the tarball. Verified by a round-trip test. - ✅ Publish & pin the
ucin/pyrunnerimage. Seesdk/runner-image/(Dockerfile+build_and_push.sh). The script prints the immutable@sha256digest to set asUCIN_RUNNER_IMAGE/_DEFAULT_RUNNER_IMAGE. (Operator action: run the script against your registry to publish.) - ✅ Inject
UCIN_PAYLOAD_URLat dispatch. The SDK stages the payload via/customer/storage/presign(user_id derived from the JWT) and carries the object key asUCIN_PAYLOAD_KEY;gateway_api._build_job_payloadmints a fresh presigned GET intoUCIN_PAYLOAD_URLat poll time (so it can't expire in the queue).
Still optional / future:
- Real AST profiling. To honour the original vision, add an
ast-based classifier path toauto_profilerkeyed off an SDK-supplied source digest, instead of the syntheticrun_commandkeywords. The viral loops (FinOps-in-CI quote-on-PR, quote sharing) then build on/jobs/estimate, which is already side-effect-free and safe to call in CI.
Layout
sdk/
pyproject.toml # `ucin` dist; `ucin` + `ucin-run` console scripts
README.md
runner-image/ # the ucin/pyrunner image (Dockerfile + build_and_push.sh)
examples/quickstart.py # copy-paste first run
tests/test_flow.py # mocked round-trip regression suite
ucin/
__init__.py # public surface: workload, login, configure, UcinClient
workload.py # @ucin.workload decorator + escape hatches
client.py # control-plane HTTP client + quote→execute flow
auth.py # login / whoami / logout (POST /auth/login)
credentials.py # ~/.ucin token store (per base_url)
cli.py # `ucin login|whoami|logout|config`
packaging.py # function → (cloudpickle payload, profiler run_command)
receipt.py # the interactive SLA-quote box + [Y/n] prompt
config.py # env / configure() resolution
errors.py # QuoteDeclined, InsufficientBalance, JobFailed, ...
runner.py # container-side `ucin-run` entrypoint
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ucin-0.1.3.tar.gz.
File metadata
- Download URL: ucin-0.1.3.tar.gz
- Upload date:
- Size: 58.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dda647ab3038a8c92a3659497da8c8d87d6ccd742a28d6eac952ed12d32bcc00
|
|
| MD5 |
6403c49dc2c75cb9a7bb05a59591dbb2
|
|
| BLAKE2b-256 |
6f31d951a6dfc59fda1b69655912b07992bc48f95bde478d2527b95aab0f981d
|
Provenance
The following attestation bundles were made for ucin-0.1.3.tar.gz:
Publisher:
sdk-publish.yml on harshsarup/ucin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ucin-0.1.3.tar.gz -
Subject digest:
dda647ab3038a8c92a3659497da8c8d87d6ccd742a28d6eac952ed12d32bcc00 - Sigstore transparency entry: 2202626269
- Sigstore integration time:
-
Permalink:
harshsarup/ucin@c465d399e4d7e4860c3bc100f56a42c8cccb28e4 -
Branch / Tag:
refs/tags/sdk-v0.1.3 - Owner: https://github.com/harshsarup
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
sdk-publish.yml@c465d399e4d7e4860c3bc100f56a42c8cccb28e4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ucin-0.1.3-py3-none-any.whl.
File metadata
- Download URL: ucin-0.1.3-py3-none-any.whl
- Upload date:
- Size: 54.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
261c7967bb1de307fd70073770247b0d4f5b035eb0ea12676e4a31d60319509f
|
|
| MD5 |
860dc392079f0bd2f09a3849878a40f0
|
|
| BLAKE2b-256 |
81b4bcdb0449cebee1362876f8c050380ecd6da514b71cfb7d1da3598c0a0824
|
Provenance
The following attestation bundles were made for ucin-0.1.3-py3-none-any.whl:
Publisher:
sdk-publish.yml on harshsarup/ucin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ucin-0.1.3-py3-none-any.whl -
Subject digest:
261c7967bb1de307fd70073770247b0d4f5b035eb0ea12676e4a31d60319509f - Sigstore transparency entry: 2202626325
- Sigstore integration time:
-
Permalink:
harshsarup/ucin@c465d399e4d7e4860c3bc100f56a42c8cccb28e4 -
Branch / Tag:
refs/tags/sdk-v0.1.3 - Owner: https://github.com/harshsarup
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
sdk-publish.yml@c465d399e4d7e4860c3bc100f56a42c8cccb28e4 -
Trigger Event:
push
-
Statement type: