Metadata-Version: 2.4
Name: ridescanapi
Version: 1.2.4
Summary: Official Python SDK for the RideScan Safety Layer API
Author-email: RideScan Engineering <support@ridescan.ai>
License-Expression: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.7
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.25.0

# RideScan Python SDK

The official Python client for the **RideScan Safety Layer API**. This SDK allows developers to programmatically manage robots, missions, and file uploads, and retrieve risk scores directly from their Python applications.

## Installation

```bash
pip install ridescanapi
```
##  Getting Started

### 1. Obtain your API Key

To use this SDK, you must have a valid API Key.

1. Go to the **RideScan Developer Console**.
2. **Create an account** or Log in.
3. Navigate to the **API Keys** section in your dashboard.
4. Click **Generate New Key**.
5. Copy the key (it starts with `rsk_...`).

### 2. Initialize the Client

You can use the client as a context manager (recommended) to automatically handle session closing, or as a standard object.

**Using Context Manager (Recommended):**

```python
from ridescanapi import RideScanClient

API_KEY = "rsk_your_api_key_here"

with RideScanClient(api_key=API_KEY) as client:
    # Your code here
    robots = client.get_robots()
    print(robots)

```

**Using Standard Initialization:**

```python
client = RideScanClient(api_key=API_KEY)
try:
    robots = client.get_robots()
finally:
    client.session.close() # Always close the session manually

```

---

## API Reference

###  Robot Resources

#### `create_robot(name, robot_type)`

Registers a new robot in the system.

* **Arguments:**
* `name` (str): A friendly name for the robot (e.g., "Warehouse-Spot-01").
* `robot_type` (str or int): The type identifier (e.g., `"SPOT"`, `"UR6"`).


* **Returns:** `dict`
```json
{
  "robot_id": "123e4567-e89b-12d3-a456-426614174000",
  "robot_name": "Warehouse-Spot-01",
  "message": "Robot created"
}

```



#### `get_robots(robot_id=None, name=None, robot_type=None)`

Search for robots matching specific criteria. If no arguments are provided, returns all robots.

* **Arguments:**
* `robot_id` (str, optional): Search by specific UUID.
* `name` (str, optional): Filter by name.
* `robot_type` (str, optional): Filter by type.


* **Returns:** `List[dict]`

#### `edit_robot(robot_id, new_name=None, new_type=None)`

Updates a robot's details.

* **Arguments:**
* `robot_id` (str): The UUID of the robot to update.
* `new_name` (str, optional): New name.
* `new_type` (str or int, optional): New type.


* **Returns:** `dict` (Updated robot object).

#### `delete_robot(robot_id)`

Permanently deletes a robot and **all** associated missions and files.

* **Arguments:** `robot_id` (str).
* **Returns:** `dict` (`{"message": "Robot deleted"}`).

---

###  Mission Resources

#### `create_mission(robot_id, mission_name)`

Creates a new mission scope under a specific robot.

* **Arguments:**
* `robot_id` (str): The UUID of the parent robot.
* `mission_name` (str): Descriptive name (e.g., "Calibration-Run-Jan").


* **Returns:** `dict` containing `mission_id`.

#### `get_missions(robot_id=None, mission_id=None, mission_name=None, ...)`

Search for missions.

* **Arguments:**
* `robot_id` (str, optional): Filter by robot.
* `mission_id` (str, optional): Filter by mission UUID.
* `mission_name` (str, optional): Filter by name.
* `start_time` / `end_time` (str, optional): Filter by date range (ISO format).


* **Returns:** `List[dict]`.

#### `edit_mission(robot_id, mission_id, new_name)`

Renames an existing mission.

* **Arguments:** `robot_id`, `mission_id`, `new_name`.
* **Returns:** `dict` (Updated mission object).

#### `delete_mission(robot_id, mission_id)`

Permanently deletes a mission.

* **Arguments:** `robot_id`, `mission_id`.
* **Returns:** `dict`.

---

###  File Resources

#### `upload_files(robot_id, mission_id, file_paths, file_type='calib_file')`

Bulk uploads files (.bag, .csv, .zip) to the server. This handles large file streaming automatically.

* **Arguments:**
* `robot_id` (str): Robot UUID.
* `mission_id` (str): Mission UUID.
* `file_paths` (List[str]): List of **absolute local paths** to the files.
* `file_type` (str):
* `'calib_file'` (Default) - Use for model training/calibration.
* `'process_file'` - Use for inference/risk analysis.




* **Returns:** `dict`
```json
{
  "success": true,
  "uploaded_files": ["uuid_day1.bag", "uuid_day2.bag"],
  "failed_files": []
}

```



#### `list_files(robot_id, mission_id)`

Lists all files uploaded for a specific mission.

* **Arguments:** `robot_id`, `mission_id`.
* **Returns:** `List[dict]` containing `unique_filename`, `original_filename`, `file_size`, etc.

#### `delete_file(robot_id, mission_id, unique_filename)`

Deletes a specific file from storage and database.

* **Arguments:**
* `robot_id` (str): Robot UUID.
* `mission_id` (str): Mission UUID.
* `unique_filename` (str): The unique ID returned by `list_files` (e.g., `abc123_data.csv`).


* **Returns:** `dict`.

---

###  Model & Inference Resources

#### `calibrate_model(robot_id, mission_id, epochs=100, robot_type="SPOT", retrain=False, ...)`

Triggers an asynchronous Kubernetes job to train a model using uploaded **calibration files**.

* **Arguments:**
* `robot_id` (str): Robot UUID.
* `mission_id` (str): Mission UUID.
* `epochs` (int): Training duration (Default: 100).
* `robot_type` (str or int): Robot type identifier (Default: `"SPOT"`).
* `retrain` (bool): Force re-training if a model already exists (Default: `False`).
* `file_names` (List[str], optional): specific subset of unique filenames to use. If `None`, uses all uploaded calibration files.


* **Returns:** `dict` indicating the task was queued.
```json
{
  "message": "Training task queued",
  "details": {"task_id": "..."}
}

```



#### `run_inference(robot_id, mission_id, file_names=None, device='cpu')`

Runs risk analysis on uploaded **inference files** using the trained model.

* **Arguments:**
* `robot_id` (str): Robot UUID.
* `mission_id` (str): Mission UUID.
* `device` (str): Compute device (`'cpu'` or `'cuda'`).
* `file_names` (List[str], optional): specific subset of unique filenames to analyze. If `None`, uses all available files.


* **Returns:** `dict` (Inference results).

#### `get_model_status(mission_id)`

Checks the status of calibration or inference tasks.

* **Arguments:** `mission_id`.
* **Returns:** `dict`
```json
{
  "calibration_status": "Training_Completed",
  "inference_status": "processing_completed",
  "epochs": 100,
  "upload_time": "..."
}

```



---

## Error Handling

The SDK raises specific exceptions from `ridescanapi.exceptions` to help you handle errors gracefully.

| Exception Class | HTTP Code | Description |
| --- | --- | --- |
| `AuthenticationError` | 401 | Invalid API Key. Check your dashboard. |
| `ValidationError` | 400 | Missing arguments, invalid file types, or malformed requests. |
| `ResourceNotFoundError` | 404 | Robot, Mission, or File ID does not exist. |
| `ConflictError` | 409 | Resource already exists (e.g., creating a robot with a duplicate ID). |
| `ServerError` | 500+ | Internal backend issue. |
| `RideScanError` | - | Generic base exception for other errors. |

**Example Usage:**

```python
from ridescanapi.exceptions import ResourceNotFoundError, ValidationError

try:
    client.delete_robot("invalid-id")
except ResourceNotFoundError:
    print("Robot not found!")
except ValidationError as e:
    print(f"Invalid input: {e}")

```

---

## Enums & Values

### `robot_type`

Used in `create_robot` and `calibrate_model`.

* `"SPOT"` (Boston Dynamics Spot)
* `"UR6"` 

### `file_type`

Used in `upload_files`.

* `"calib_file"`: Files used to train/calibrate the model.
* `"process_file"`: Files used for inference/risk assessment.

### `device`

Used in `run_inference`.

* `"cpu"` (Default)
* `"cuda"` (GPU - Requires backend support)
