Skip to main content

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:

  1. POST /jobs/audit — the pre-acceptance gauntlet (security scan, profile, capacity, balance). Returns the quote and the audit_token deploy requires in production.
  2. POST /jobs/deploy — pre-authorises quoted_price_inr against the wallet and queues the Job. The SOR (sor.dispatch_loop) places it.
  3. 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 captured pip freeze → uploaded via POST /storage/presign. A synthetic run_command carries 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 is ucin-run. It fetches the payload, tops up deps, calls the function, and writes the return value to UCIN_CHECKPOINT_DIR/ucin-result.pkl — which entrypoint_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:

  1. SDK converts max_budget_inr → the exact runtime ceiling using the tier rates from GET /customer/config, audits, and re-audits until the server's own quoted_price_inr ≤ your budget (the quote is linear in runtime, so one Newton step lands it). It refuses to deploy otherwise.
  2. Control plane pins that as estimated_scu at 0.95 confidence (_apply_runtime_ceiling) → quoted_price_inr is exact, and the wallet is pre-authorised for exactly that.
  3. Gateway computes ceiling_scu = (quoted − base_fee) / rate and, on every billing tick, clamps scu_consumed at the ceiling and returns complete_at_ceiling: true — the job is finalised at that boundary (gateway_api.py NF-7). actual_cost ≤ quoted_price is an invariant.
  4. 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.network", 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).

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):

  1. Result artifacts in /jobs/{id}/results. On clean completion the wrapper (entrypoint_wrapper._persist_outputs_on_success) uploads CHECKPOINT_DIR and reports the key; the CP status handler persists it to Job.checkpoint_id (gateway_api.py, JobStatusUpdateRequest.checkpoint_r2_key); /customer/jobs/{id}/results labels it ucin-result.pkl with source_path="checkpoint/ucin-result.pkl", and the SDK extracts the return value from the tarball. Verified by a round-trip test.
  2. Publish & pin the ucin/pyrunner image. See sdk/runner-image/ (Dockerfile + build_and_push.sh). The script prints the immutable @sha256 digest to set as UCIN_RUNNER_IMAGE / _DEFAULT_RUNNER_IMAGE. (Operator action: run the script against your registry to publish.)
  3. Inject UCIN_PAYLOAD_URL at dispatch. The SDK stages the payload via /customer/storage/presign (user_id derived from the JWT) and carries the object key as UCIN_PAYLOAD_KEY; gateway_api._build_job_payload mints a fresh presigned GET into UCIN_PAYLOAD_URL at poll time (so it can't expire in the queue).

Still optional / future:

  1. Real AST profiling. To honour the original vision, add an ast-based classifier path to auto_profiler keyed off an SDK-supplied source digest, instead of the synthetic run_command keywords. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ucin-0.1.0.tar.gz (30.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ucin-0.1.0-py3-none-any.whl (29.0 kB view details)

Uploaded Python 3

File details

Details for the file ucin-0.1.0.tar.gz.

File metadata

  • Download URL: ucin-0.1.0.tar.gz
  • Upload date:
  • Size: 30.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ucin-0.1.0.tar.gz
Algorithm Hash digest
SHA256 eced58079855b5370bd1fa05b8c6c1d23d188ecc35ac210aff864ffd8314f2f9
MD5 cacf03a14710e18fef3936cfaeb439a2
BLAKE2b-256 532e3850fe545fb3579302dafca27cc51dce8caece77afba136ea21d9e06cc79

See more details on using hashes here.

Provenance

The following attestation bundles were made for ucin-0.1.0.tar.gz:

Publisher: sdk-publish.yml on harshsarup/ucin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ucin-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ucin-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 29.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ucin-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9c62748cb9c83329867759b286464f54b5947e4e671ea8e6862bc76489805611
MD5 b35972ee262a8e4d220c4b09f630ce00
BLAKE2b-256 11572adff1d73d67e5def626b706e68345bb78920d37fdfe40d818b95b47b497

See more details on using hashes here.

Provenance

The following attestation bundles were made for ucin-0.1.0-py3-none-any.whl:

Publisher: sdk-publish.yml on harshsarup/ucin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page