Metadata-Version: 2.4
Name: bios-sdk
Version: 0.1.0
Summary: Official Python SDK for the BIOS training and deployment platform API
Project-URL: Homepage, https://bios.us.com
Project-URL: Documentation, https://bios.us.com/docs
Author-email: BIOS <bios@us.inc>
License-Expression: MIT
License-File: LICENSE
Keywords: bios,deployment,fine-tuning,inference,llm,machine-learning,sdk
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: requests<3,>=2.33.0
Requires-Dist: urllib3<3,>=2.7.0
Description-Content-Type: text/markdown

# bios

Official Python SDK for the [BIOS](https://bios.us.com) fine-tuning platform API.

## Installation

```bash
pip install bios
```

## Quick Start

```python
from bios import BiOS

client = BiOS(api_key="bios-...")

# Search for models
result = client.models.search(query="llama", type="llm", limit=5)
for model in result["models"]:
    print(f"{model['id']} -- {model['totalParams']}B params")

# Create a training job
job = client.training.create(
    idempotency_key="training-create-20260711-0001",
    model="meta-llama/Llama-3.1-8B-Instruct",
    dataset_ids=["ds_abc123", "ds_def456"],
    method="sft",
    adapter="lora",
    epochs=3,
    learning_rate=2e-4,
    lora_rank=16,
)
print(f"Job {job['id']} created -- status: {job['status']}")
```

## Authentication

The SDK supports two authentication methods:

**API Key** (recommended). Keys start with `bios-` (legacy `usf-` keys stay
valid):
```python
client = BiOS(api_key="bios-...")
```

**Environment variables**. When `api_key` or `base_url` is omitted, the SDK
reads `BIOS_API_KEY` and `BIOS_BASE_URL` from the environment:
```bash
export BIOS_API_KEY=bios-...
export BIOS_BASE_URL=https://api.usbios.ai   # optional; this is the default
```
```python
client = BiOS()  # uses BIOS_API_KEY / BIOS_BASE_URL
```

**JWT Access Token**:
```python
client = BiOS(
    access_token="eyJhbG...",
    org_id="org_abc123",
)
```

## Configuration

| Parameter | Type | Default | Description |
|---|---|---|---|
| `api_key` | `str` | `BIOS_API_KEY` env var | API key for authentication (prefix: `bios-`; legacy `usf-` keys stay valid) |
| `access_token` | `str` | `None` | JWT access token (alternative to API key) |
| `org_id` | `str` | `None` | Organization ID (required for JWT auth) |
| `workspace_id` | `str` | `None` | Workspace ID (optional, overrides key default) |
| `base_url` | `str` | `BIOS_BASE_URL` env var, then `https://api.usbios.ai` | Canonical production hostname (release-gated; this documentation does not assert current availability). During prelaunch/dev, pass `https://api-dev.usbios.ai` explicitly. |
| `timeout` | `float` | `30.0` | Request timeout in seconds |
| `inference_key` | `str` | `None` | Optional per-deployment key used only by `client.inference` |
| `inference_base_url` | `str` | `base_url` | Explicit dev or production inference hostname |
| `inference_timeout` | `float` | `900.0` | End-to-end inference/stream timeout in seconds |

## Resources

### Inference

Inference keys are separate from control-plane API keys. Streaming yields each
OpenAI SSE chunk as a dictionary and closes the upstream response when the
iterator is closed. Calls are never retried automatically; if your application
chooses to retry, reuse the same `idempotency_key`. The header is propagated,
but the SDK does not claim server-side replay/deduplication unless the endpoint
returns an explicit replay acknowledgement.

```python
client = BiOS(
    api_key="bios-control-plane-key",
    inference_key="sk-bios-deployment-key",
    # Use https://api-dev.usbios.ai explicitly during dev.
)

tools = [{
    "type": "function",
    "function": {
        "name": "lookup",
        "parameters": {"type": "object", "properties": {"id": {"type": "integer"}}},
    },
}]

stream = client.inference.stream_chat_completions(
    messages=[{"role": "user", "content": "Look up record 42"}],
    tools=tools,
    idempotency_key="chat-42-attempt-1",
)
try:
    for chunk in stream:
        print(chunk)
finally:
    stream.close()  # cancels/disconnects an unfinished generation
```

### Models

Search the HuggingFace model catalog, fetch training configs, and check adapter compatibility.

```python
# Search models
result = client.models.search(query="llama", type="llm", limit=10)

# Get model config
config = client.models.get_config("meta-llama/Llama-3.1-8B")
print(f"{config['totalParams']}B params, MoE: {config['isMoE']}")

# Check adapter compatibility
compat = client.models.get_adapter_compatibility(
    model_type="llama",
    training_method="rlhf",
    rlhf_algorithm="dpo",
)
usable = [a for a in compat["adapters"] if a["compatible"]]
print(f"{len(usable)} compatible adapters")
```

### Datasets

Upload, import, preview, and manage training datasets.

```python
# List datasets
datasets = client.datasets.list()

# Upload a dataset
uploaded = client.datasets.upload(
    file_path="./training_data.jsonl",
    name="My SFT Dataset",
)
print(f"Uploaded: {uploaded['id']}")

# Import from HuggingFace
imported = client.datasets.import_from_huggingface(
    repo_id="databricks/dolly-15k",
    integration_id="int_abc123",
    name="Dolly 15k",
)

# Preview dataset rows
preview = client.datasets.preview("ds_abc123", page=1, page_size=5)
print(preview["columns"])

# Validate before uploading
result = client.datasets.validate("./data.jsonl")
if result["format_valid"]:
    print(f"Valid {result['detected_format']} with {result['num_samples']} rows")
else:
    print("Errors:", result["validation_errors"])

# Search HuggingFace Hub
hub_results = client.datasets.search_hub(query="code instruct")

# Preview a Hub dataset
hub_preview = client.datasets.preview_hub(
    dataset_id="databricks/dolly-15k",
    split="train",
    limit=5,
)

# Get format specs
specs = client.datasets.get_format_specs()

# Get storage usage
usage = client.datasets.get_storage_usage()

# Delete a dataset
client.datasets.delete("ds_abc123")
```

### Training

Create, monitor, stop, and resume fine-tuning jobs.

```python
request = {
    "idempotency_key": "training-create-20260711-0001",
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "dataset_ids": ["ds_abc123", "ds_def456"],
    "method": "sft",
    "adapter": "lora",
    "epochs": 3,
    "learning_rate": 2e-4,
    "lora_rank": 16,
    "lora_alpha": 32,
    "gpu_type": "A100_80GB",
    "gpu_count": 1,
    "gpu_priorities": [
        {"gpu_type": "A100_80GB", "gpu_count": 1},
        {"gpu_type": "H100_80GB", "gpu_count": 1},
        {"gpu_type": "L40S", "gpu_count": 1},
    ],
    "queue_if_unavailable": True,
    "queue_deadline": "2026-07-18T00:00:00Z",
    "max_price_hour_cents": 500,
}

# Side-effect-free validation, canonical sizing, live stock and alternatives
check = client.training.preflight(request)
print(check["request_hash"], check.get("recommended"), check["queue_eligible"])

# Create the paid job only after reviewing preflight
job = client.training.create(**request)

# List jobs
jobs = client.training.list(status="running")
page = client.training.list_page(limit=50, offset=0)

# Get job details
job = client.training.get("job_abc123")
print(f"Status: {job['status']}, Progress: {job.get('progress', 0)}%")

# Get metrics
metrics = client.training.get_metrics("job_abc123")
for point in metrics["metrics"]:
    print(point["step"], point.get("loss"))

# Get checkpoints
checkpoints = client.training.get_checkpoints("job_abc123")
for cp in checkpoints:
    print(f"{cp['name']}: {cp['size_bytes']} bytes")

# Get logs
logs = client.training.get_logs("job_abc123")
for entry in logs["logs"]:
    print(entry["level"], entry["message"])

# Stop a job
client.training.stop("job_abc123", keep_data=True)

# Resume a stopped job
client.training.resume("job_abc123", idempotency_key="training-resume-20260711-0001")

# Delete a checkpoint
client.training.delete_checkpoint("job_abc123", "cp_xyz789")
```

### Wallet

View wallet balance and transaction history.

```python
# Get balance
balance = client.wallet.get_balance()
print(f"Balance: ${balance['balance_cents'] / 100:.2f}")
print(f"Available: ${balance['available_cents'] / 100:.2f}")

# List transactions
txns = client.wallet.get_transactions(limit=20)
for t in txns:
    print(f"{t['type']}: ${t['amount_cents'] / 100:.2f} -- {t.get('description')}")

# Get pricing
pricing = client.wallet.get_pricing()
```

### Inference deployment management

The same `client.inference` resource that makes OpenAI-compatible chat calls also
manages model-serving deployments (create, monitor, stop, and delete).

```python
request = {
    "name": "llama-api",
    "source_type": "hf_model",
    "hf_model_id": "meta-llama/Llama-3.1-8B-Instruct",
    "gpu_type": "H100_80GB",
    "gpu_count": 1,
    "allow_capacity_queue": True,
    "max_price_hour_cents": 500,
}

# No wallet mutation and no GPU allocation.
check = client.inference.preflight(request)
print(check["selected_gpu"], check["alternatives"], check["billing"])

deployment = client.inference.create(request, idempotency_key="deploy-create-20260711-0001")
print(deployment["inference_key"])  # returned once; store securely

status = client.inference.status(deployment["id"])
print(status["status"], status.get("status_reason"), status.get("queue_expires_at"), status.get("wallet_authorization_status"))
# status stays "failed" for existing filters when status_reason is "queue_expired".
notification_history = client.inference.notifications(deployment["id"])
print([(row["event_type"], row["state"], row["attempt_count"]) for row in notification_history])

# Bounded newest-first listing; keep filters unchanged while using next_cursor.
page = client.inference.list_page(limit=100, status="running", search="llama")
if page["has_more"]:
    older = client.inference.list_page(
        limit=100, status="running", search="llama", cursor=page["next_cursor"]
    )
    print(older["deployments"])
# Lazy traversal avoids one unbounded response.
for item in client.inference.iter_all(status="running"):
    print(item["name"])

client.inference.stop(deployment["id"])
client.inference.delete(deployment["id"])
```

### GPU

View GPU pricing and get hardware recommendations.

```python
# Get all GPU pricing
pricing = client.gpu.get_pricing()
for gpu in pricing["gpus"]:
    print(f"{gpu['display_name']}: {gpu['price_display']} -- {gpu['vram_gb']}GB VRAM")

# Authoritative model-aware choices, live stock, total pricing, and alternatives
options = client.gpu.get_options(
    "meta-llama/Llama-3.1-8B-Instruct",
    train_type="qlora",
    method="sft",
)

# Get recommended GPU for a model
rec = client.gpu.get_recommended("meta-llama/Llama-3.1-8B-Instruct")
if rec:
    print(f"Recommended: {rec['display_name']} x{rec['recommended_count']}")
    print(f"Total VRAM: {rec['total_vram_gb']}GB")
    print(f"Cost: ${rec['estimated_cost_per_hour_cents'] / 100:.2f}/hr")
    print(f"Reason: {rec['reason']}")
```

### Introspect

Discover the permissions and scope of your API key.

```python
info = client.introspect()
print(f"Org: {info['org']['name']}")
print(f"Scopes: {', '.join(info['scopes'])}")
print(f"Allowed tools: {len(info['allowed_mcp_tools'])}")
```

## Error Handling

All API errors raise `ApiError` with `status`, `code`, `request_id`, and `message` attributes.

```python
from bios import BiOS, ApiError

client = BiOS(api_key="bios-...")

try:
    job = client.training.get("bad_id")
except ApiError as e:
    if e.status == 404:
        print("Job not found")
    elif e.status == 401:
        print("Invalid API key")
    elif e.status == 403:
        print("Insufficient permissions")
    else:
        print(f"API error {e.status}: {e.message}")
        if e.request_id:
            print(f"Request ID: {e.request_id}")
```

## Python Version Support

- Python 3.10+

## License

MIT
