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.1.tar.gz (30.8 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.1-py3-none-any.whl (29.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ucin-0.1.1.tar.gz
  • Upload date:
  • Size: 30.8 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.1.tar.gz
Algorithm Hash digest
SHA256 57862b370f666befc04cb5cd51a18fb09a790e45acb7a79f6650b90d412d302f
MD5 3f82bae22f41df8edf01e3d15fd3c233
BLAKE2b-256 5aff9cb8c8fc96c11f5b5c463d6e85e7e1bfb62ca11a67393e37acde09902b2d

See more details on using hashes here.

Provenance

The following attestation bundles were made for ucin-0.1.1.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.1-py3-none-any.whl.

File metadata

  • Download URL: ucin-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 29.4 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c0bc9f54459ba6b0eeed2e852706e1582323c53dad4dd6798704da16601942fb
MD5 a0df211d1859423b42eaa1360a48ef39
BLAKE2b-256 71594b98d100b5199491a6c31edbd4ef7ff583a55a3a73f54d1ec86e9e515d3a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ucin-0.1.1-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