Metadata-Version: 2.4
Name: jinko-sdk
Version: 1.2.0
Summary: Official typed Python SDK for the Jinkō clinical trial simulation platform (jinko.ai) — script models, trials, virtual populations, and results.
Author-email: NovaInSilico <oss@novainsilico.ai>
License-Expression: MIT
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic>=2.13.4
Dynamic: license-file

# Jinkō Python SDK

[Jinkō](https://www.jinko.ai/) is a complete solution for **clinical trial simulation and protocol design optimization**, developed by [Nova In Silico](https://novainsilico.ai). It combines mechanistic ("white-box") modeling, virtual populations, in-silico trials, and analytics in a collaborative platform used by modelers, scientists, and trial managers to accelerate drug development and de-risk clinical decisions.

The **Jinkō Python SDK** is the official, typed Python client for the Jinkō API. It lets you script, automate, and integrate Jinkō workflows — from browsing project items and editing computational models, to running trials on virtual populations and retrieving simulation results — directly from Python.

## Main features

- **QSP model development** — Create, edit, and version quantitative systems pharmacology models with typed APIs for parameters, events, reactions, and compartments
- **Virtual population management** — Work with patient populations and generators for trial simulation at scale  
- **In-silico trial orchestration** — Design protocols, run simulations, and analyze outcomes to optimize trial designs before clinical execution
- **Collaborative project navigation** — Browse folders, search across model libraries, and manage versioned assets in team environments
- **Results analytics** — Extract simulation summaries, tabular data, and visualizations with optional pandas integration
- **Programmatic workflows** — Automate repetitive modeling tasks, batch operations, and integrate with existing R&D pipelines

Learn more about the platform at [doc.jinko.ai](https://doc.jinko.ai).

## Installation

```bash
pip install jinko-sdk
```

Requirements:
- Python 3.12+
- A Jinko API key
- A target Jinko project id

## Setup and authentication

The SDK reads configuration from environment variables by default:

```bash
export JINKO_API_KEY="..."
export JINKO_PROJECT_ID="..."
export JINKO_BASE_URL="https://api.jinko.ai"  # optional
```

For local developer convenience, keep secrets in a local `.env` and load them in your shell (for example with `direnv allow` if you use direnv).

You can also pass values explicitly:

```python
from jinko import JinkoClient

client = JinkoClient(
    api_key="...",
    project_id="...",
    base_url="https://api.jinko.ai",  # optional
    timeout=30.0,
)
```

Validate credentials early:

```python
check = client.auth_check()
print(check.status, check.user_email)
```

## Quickstart

```python
from jinko import JinkoClient

client = JinkoClient()

model = client.get_model("cm-...")
print(model.name)

model.rename("Retuned PK model")
model.components.get_parameter("k_clearance").set_formula("CL / V")

trial = client.create_trial(model, name=f"{model.name} - smoke run")
trial.run()
trial.wait_until_completed(timeout=600, poll_interval=5.0)

summary = trial.results.summary()
print(summary.get("status"))
```

## Core concepts

- `JinkoClient`: primary user-facing entrypoint
- Client direct methods: `client.get_model(...)`, `client.list_trials(...)`, `client.create_trial(...)`, ...
- Domain wrappers (`Model`, `Trial`, `Vpop`, ...): typed objects with behavior methods (for example `model.rename(...)`, `trial.run()`)
- `types.ProjectItem`: common typed metadata envelope (`sid`, `type`, `core_id`, folders, version, ...)
- `Page[T]`: paginated `list()` result (`items`, `next_cursor`, `has_next`)
- `iter()`: auto-paginated iterator for bulk traversal

Most user-facing resources follow this pattern:
- `list_<types>(...) -> Page[T]`
- `iter_<types>(...) -> Iterator[T]`
- `get_<type>(sid, revision=None)`
- `create_<type>(...)` or `create_raw_<type>(...)` when available
- `delete(sid)`
- rich object methods on returned items (`.versions`, `rename`, `set_description`, `run`, `wait_until_completed`, ...)

The intended SDK usage is:
- enter through `client.<direct_method>(...)`
- continue through the typed object returned by that method
- use object-level services only when explicitly exposed (currently `model.components`)

## Public client surface

### Project-wide services

- `client.folders`: folder CRUD and folder-tree exploration
- `client.raw_request(...)`: authenticated low-level HTTP escape hatch
- `client.delete(sid)`: delete any project item by SID

### Typed project item methods

- `client.list_models(...)`, `client.iter_models(...)`, `client.get_model(...)`
- `client.list_trials(...)`, `client.iter_trials(...)`, `client.get_trial(...)`
- `client.list_calibrations(...)`, `client.iter_calibrations(...)`, `client.get_calibration(...)`
- `client.list_output_sets(...)`, `client.iter_output_sets(...)`, `client.get_output_set(...)`
- `client.list_protocol_designs(...)`, `client.iter_protocol_designs(...)`, `client.get_protocol_design(...)`
- `client.list_scorings(...)`, `client.iter_scorings(...)`, `client.get_scoring(...)`
- `client.list_vpops(...)`, `client.iter_vpops(...)`, `client.get_vpop(...)`
- `client.list_vpop_generators(...)`, `client.iter_vpop_generators(...)`, `client.get_vpop_generator(...)`
- `client.list_assertions(...)`, `client.iter_assertions(...)`, `client.get_assertion(...)`
- `client.list_data_tables(...)`, `client.iter_data_tables(...)`, `client.get_data_table(...)`
- `client.list_documents(...)`, `client.iter_documents(...)`, `client.get_document(...)`
- `client.list_raw_files(...)`, `client.iter_raw_files(...)`, `client.get_raw_file(...)`
- `client.list_references(...)`, `client.iter_references(...)`, `client.get_reference(...)`
- `client.list_subsampling_designs(...)`, `client.iter_subsampling_designs(...)`, `client.get_subsampling_design(...)`
- `client.list_trial_visualizations(...)`, `client.iter_trial_visualizations(...)`, `client.get_trial_visualization(...)`

## Exploring a project

### 1) Start broad: project-items search

```python
models_page = client.list_models(name="PK")
for model in models_page:
    print(model.sid, model.type, model.name)
```

Use `iter()` when you want all pages:

```python
all_trial_sids = [trial.sid for trial in client.iter_trials()]
```

### 2) Explore folders and folder trees

```python
print(client.folders.tree())

root = client.folders.get_by_name("Program A", exact_match_only=True)
if root:
    print(client.folders.tree(root=root, max_depth=2, include_project_items=True))
```

You can also list by folder:

```python
modeling_folder = client.folders.get_by_name("Modeling", exact_match_only=True)
if modeling_folder:
    models = client.list_models(folder=modeling_folder, limit=25)
```

### 3) Fetch typed resources and inspect content

```python
model = client.get_model("cm-...")
content = model.content()  # typed model interface
print(content.model.modelName)
```

## Versioned resources

By default, `get(sid)` reads the latest revision.

```python
latest = client.get_model("cm-...")
older = client.get_model("cm-...", revision=3)
```

List version metadata and label snapshots:

```python
versions_page = latest.versions.list(only_labeled=False, first=20)
for version in versions_page:
    print(version.revision, version.label, version.is_latest)

latest.versions.label(revision=3, label="baseline")
latest.versions.unlabel(revision=3)
```

## Models and components

`Model.components` is the ergonomic typed API for editing components.

```python
model = client.get_model("cm-...")

# Typed read
k_clearance = model.components.get_parameter("k_clearance")

# Immediate multi-field update in one API call
k_clearance.update(formula="CL / V", unit="L/h", description="retuned clearance")

# Typed create (immediate commit)
model.components.create_parameter(id="k_abs", formula=1.2, unit="1/h")

# Event updates are provided as a component->value mapping
model.components.create_event(
    id="dose_start",
    updates={"Dose": 100},
    condition_trigger="t >= 0",
)

# Reaction stoichiometry is also a mapping
model.components.create_mass_action_reaction(
    id="binding",
    reactants={"Drug": 1, "Target": 1},
    products={"Complex": 1},
    k_plus="kon",
    k_minus="koff",
)
```

When changing several components, batch commits avoid one-request-per-change:

```python
with model.components.batch(version_name="retune") as batch:
    batch.edit_parameter("k_clearance").set_formula("CL2 / V")
    batch.create_parameter(id="k_new", formula=0.8, unit="1/h")

with model.components.batch(version_name="event and kinetics") as batch:
    batch.edit_event("dose_start").set_updates({"Dose": 120})
    batch.edit_reaction("binding").set_general_kinetics(
        reactants={"Drug": 1, "Target": 1},
        products={"Complex": 1},
        rate="kon * Drug * Target - koff * Complex",
    )
```

For unsupported modeling endpoints, use `client.raw_request(...)` rather than private object internals.

## Trials and result retrieval

Create a trial from typed resources, run it, then consume results:

```python
model = client.get_model("cm-...")
vpop = client.get_vpop("vp-...")
protocol = client.get_protocol_design("pd-...")

trial = client.create_trial(
    model,
    vpop=vpop,
    protocol=protocol,
    name="model-vpop-protocol run",
)
trial.run()
trial.wait_until_completed(timeout=1800)

summary = trial.results.summary()
scalars_csv = trial.results.scalars(["AUC", "Cmax"])  # TabularDownload

# Optional convenience if pandas is installed in your environment
df = scalars_csv.to_dataframe()
print(df.head())
```

## Pagination patterns

`list()` returns a `Page[T]` with cursor metadata:

```python
page = client.list_models(limit=20)
print(len(page), page.has_next, page.next_cursor)

if page.has_next:
    next_page = client.list_models(limit=20, after=page.next_cursor)
```

Use `iter()` when you want to consume all pages seamlessly.

## Raw API escape hatch

Use `client.raw_request(...)` when an endpoint is not yet wrapped by a typed service.
It reuses SDK authentication, project scoping, transport, and error handling.

```python
payload = client.raw_request(
    "GET",
    "/app/v1/auth/check",
)
print(payload)
```

Example with params + JSON body:

```python
result = client.raw_request(
    "POST",
    "/app/v1/project-item",
    params={"first": 10},
    json_body={"text": "tumor", "type": "Trial"},
    headers={"X-My-Header": "value"},
)
```

Use this path for uncovered routes. Prefer typed SDK methods when available.

## Error handling

The SDK raises typed exceptions from `jinko`, including:
- `ConfigurationError`
- `AuthenticationError`, `AuthorizationError`
- `NotFoundError`, `ValidationError`, `ConflictError`
- `RateLimitError`, `ServerError`, `TransportError`

Minimal example:

```python
from jinko import JinkoClient, NotFoundError

client = JinkoClient()

try:
    client.get_model("cm-does-not-exist")
except NotFoundError as exc:
    print(f"Model not found: {exc}")
```

## Development tests

See `tests/README.md`.


## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

## Contact & references

For support or inquiries, please contact us at [oss@jinko.ai](mailto:oss@jinko.ai)

- [Jinkō](https://www.jinko.ai/)
- [Nova In Silico](https://novainsilico.ai)
