Metadata-Version: 2.4
Name: zededa-eip-client
Version: 3.2.0
Summary: Zededa Edge Intelligence Platform client for authentication and MLflow integration
Author-email: Zededa <support@zededa.com>
License: Apache-2.0
Project-URL: Homepage, https://github.com/zededa/edgeai
Project-URL: Issues, https://github.com/zededa/edgeai/issues
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31.0
Dynamic: license-file

# Zededa Edge Intelligence Platform Client

The `zededa-eip-client` package provides both a pluggable command-line interface and a Python library for authenticating with the Zededa Edge Intelligence Platform backend and preparing ML tooling environments.

## Highlights

- 🧩 **Modular command registry** – each CLI sub-command lives in its own module under `zededa_eip_client.commands`, making it easy to add new commands such as `catalog` or `model` without touching existing code.
- 🧠 **Typed service layer** – shared HTTP, authentication, catalog, and storage logic is encapsulated under `zededa_eip_client.services`, so workflows reuse the same battle-tested primitives.
- 🔐 **Secure OAuth login** – browser-based authentication with automatic callback port discovery and detailed debug logging when needed.
- ⚙️ **Environment bootstrap** – exports MLflow and MinIO credentials into your shell and keeps helpers available for Python embedding.

## Installation

```bash
pip install zededa-eip-client
```

To work from source:

```bash
git clone https://github.com/zededa/edgeai.git
cd edgeai/tmp/edgeai-sdk
pip install -e .
```

## CLI Usage

Every action is exposed as a sub-command. Common workflows include `login`, `catalog`, `set-catalog-context`, `external-providers`, `import-jobs`, and benchmark-related commands.

```bash
# Interactive OAuth login with optional catalog selection
zededa-eip login

# Login for a specific organization using the default backend
zededa-eip login --organization-id zededa

# Non-interactive login with credentials
zededa-eip login --email user@example.com --prompt-password

# Override backend URL and enable debug logging
EDGEAI_SERVICE_URL=https://custom.backend.local \
  zededa-eip login --debug
```

After a successful login the CLI launches a child shell with the relevant environment variables applied. Exit that shell to return to your previous context.

### Catalog Management

List available catalogs and switch between catalogs with an authenticated shell session:

```bash
# List all available catalogs
zededa-eip catalog --list

# Switch to a catalog and launch authenticated shell (recommended)
zededa-eip set-catalog-context zededa

# List catalogs with custom backend URL and debug logging  
EDGEAI_SERVICE_URL=https://custom.backend.local \
  zededa-eip catalog --list --debug

# Switch to catalog with debug logging
zededa-eip set-catalog-context production --debug

# Override service URL for one-time use
zededa-eip set-catalog-context staging --service-url https://staging.backend.com
```

The catalog list shows all catalogs you have access to, highlighting your current catalog. The `set-catalog-context` command switches to a catalog and launches an authenticated shell session with all required environment variables set, similar to the login command.

### Available Options

#### Login Command
```
zededa-eip login [-h]
                    [--organization-id ORGANIZATION_ID]
                    [--email EMAIL]
                    [--password PASSWORD]
                    [--prompt-password]
                    [--service-url SERVICE_URL]
                    [--debug]
```

#### Catalog Listing Command
```
zededa-eip catalog [-h]
                      [--list]
                      [--service-url SERVICE_URL]
                      [--debug]
```

#### Set Catalog Context Command
```
zededa-eip set-catalog-context [-h]
                                  catalog
                                  [--service-url SERVICE_URL]
                                  [--debug]
```

## Python Usage

Use the high-level client, the module helpers, or the command workflow directly:

### Authentication
```python
from zededa_eip_client.client import ZededaEIPClient

client = ZededaEIPClient()
creds = client.login(organization_id="zededa")
print(creds["environment"]["MLFLOW_TRACKING_URI"])
```

### Catalog Management
```python
from zededa_eip_client.client import ZededaEIPClient

# Using the client
client = ZededaEIPClient()

# List available catalogs (prints formatted output by default)
client.list_catalogs()
# Output:
# Available Catalogs:
# ==================
#  1. demo1
#  2. demo2 (current)
#  3. zededa
#  4. production
#  5. staging
#
# Total: 5 catalogs
# Current catalog: demo2
# User: alice@company.com

# Get catalog data as dictionary (formatted=False)
catalogs = client.list_catalogs(formatted=False)
print(f"Available catalogs: {catalogs['available_catalogs']}")

# Switch to a catalog
creds = client.switch_catalog("production")

# Or using the module-level convenience functions
from zededa_eip_client import list_catalogs, switch_catalog

# List catalogs (formatted output)
list_catalogs()

# Get catalog data as dictionary
catalogs = list_catalogs(formatted=False)

# Switch catalog
creds = switch_catalog("production")
```

### Direct Command Usage
Call the command workflows directly if you need finer-grained control:

```python
from zededa_eip_client.commands.login import execute_login
from zededa_eip_client.commands.catalogs import execute_catalog_switch, execute_catalog_list

# Login
credentials = execute_login("zededa", debug=True)

# List catalogs
catalog_info = execute_catalog_list(debug=True)

# Switch catalogs (updates environment variables only)
credentials = execute_catalog_switch("production", debug=True)
```

Environment variables can be cleared programmatically via `zededa_eip_client.client.logout()` or `zededa_eip_client.environment.clear_environment()`.

## External Providers and Model Import

Manage external model sources and import models into your catalogs.

For external provider operations, the CLI uses provider names. In the Python client, `get_external_provider()` uses a provider ID, while `get_external_provider_by_name()`, `update_external_provider()`, `test_provider_connection()`, `browse_provider()`, and `delete_external_provider()` operate on provider names.

### CLI Usage

#### External Provider Management

```bash
# List all external providers
zededa-eip external-providers list

# Create a new HuggingFace provider
zededa-eip external-providers create \
  --name "My HuggingFace" \
  --type huggingface \
  --url "https://huggingface.co" \
  --config '{"token": "hf_xxxxx"}'

# Get provider details (use provider name)
zededa-eip external-providers get "My HuggingFace"

# Update provider configuration (use provider name)
zededa-eip external-providers update "My HuggingFace" \
  --name "Updated HuggingFace" \
  --config '{"token": "new_token"}'

# Test provider connection (use provider name)
zededa-eip external-providers test-connection "My HuggingFace"

# Browse provider models (use provider name)
zededa-eip external-providers browse "My HuggingFace" \
  --search "yolo" \
  --path "/models"

# Create a Qualcomm AI Hub provider (no credentials required)
zededa-eip external-providers create \
  --name "qualcomm-aihub" \
  --type qualcomm_aihub \
  --description "Qualcomm AI Hub model catalog"

# Test Qualcomm AI Hub catalog access
zededa-eip external-providers test-connection "qualcomm-aihub"

# Browse Qualcomm AI Hub models
zededa-eip external-providers browse "qualcomm-aihub" \
  --search "yolo"

# Delete a provider (use provider name)
zededa-eip external-providers delete "My HuggingFace"
```

Model variant discovery for Qualcomm AI Hub is currently exposed through the Python client with `client.list_model_versions(provider_name, model_path)`.

#### Import Job Management

```bash
# RECOMMENDED: Upload files from your local machine
zededa-eip import-jobs upload \
  --provider-name "local-provider" \
  --organization-id my-organization \
  --model-name "My Local Model" \
  --files /path/to/model.pt /path/to/config.json \
  --model-version "1.0" \
  --metadata '{"framework": "pytorch", "task": "classification"}' \
  --wait

# Upload a single file
zededa-eip import-jobs upload \
  --provider-name "local-provider" \
  --organization-id my-organization \
  --model-name "My Model" \
  --files /home/user/models/model.pt \
  --wait

# Create an import job from external provider (returns immediately)
zededa-eip import-jobs create \
  --provider-name "My HuggingFace" \
  --model-identifier "user/model-name" \
  --model-name "My Imported Model" \
  --model-version "1.0"

# Create and wait for completion
zededa-eip import-jobs create \
  --provider-name "My HuggingFace" \
  --model-identifier "user/model-name" \
  --wait

# Import the default Qualcomm AI Hub ONNX variant
zededa-eip import-jobs create \
  --provider-name "qualcomm-aihub" \
  --model-identifier "yolov8_det" \
  --model-name "qualcomm-yolov8"

# Import a Qualcomm AI Hub model with device and precision selection
zededa-eip import-jobs create \
  --provider-name "qualcomm-aihub" \
  --model-identifier "yolov8_det" \
  --model-name "qualcomm-yolov8-qcs9075" \
  --qualcomm-device QCS9075 \
  --qualcomm-precision fp16 \
  --wait

# List all import jobs
zededa-eip import-jobs list

# Filter by organization and status
zededa-eip import-jobs list \
  --organization-id organization-456 \
  --status completed

# Get job status
zededa-eip import-jobs get job-id-789

# Cancel a running job
zededa-eip import-jobs cancel job-id-789

# Retry a failed job
zededa-eip import-jobs retry job-id-789

# Delete a job record
zededa-eip import-jobs delete job-id-789
```

### Python Usage

#### External Provider Management

```python
from zededa_eip_client.client import ZededaEIPClient

client = ZededaEIPClient()

# List providers
providers = client.list_external_providers(limit=50, page=1, search="azure")

# Create a provider
provider = client.create_external_provider({
    "name": "My Azure",
    "type": "azure",
    "url": "https://azure.microsoft.com",
    "config": {"subscription_id": "sub-123", "token": "token-xxx"},
    "description": "Azure ML provider"
})
provider_id = provider["id"]
provider_name = provider["provider_name"]

# Get provider by ID
provider_by_id = client.get_external_provider(provider_id)

# Get provider by name
provider_by_name = client.get_external_provider_by_name(provider_name)

# Update provider by name
updated = client.update_external_provider(provider_name, {
    "name": "Updated Azure",
    "config": {"token": "new-token"}
})

# Test connection by name
result = client.test_provider_connection(provider_name)
print(f"Connection test: {result['success']}")

# Browse provider by name
items = client.browse_provider(provider_name, path="/models", search="yolo")

# Qualcomm AI Hub provider
qualcomm = client.create_external_provider({
    "name": "qualcomm-aihub",
    "type": "qualcomm_aihub",
    "description": "Qualcomm AI Hub catalog"
})

# Browse Qualcomm catalog models
qualcomm_models = client.browse_provider("qualcomm-aihub", search="yolo")

# List available Qualcomm variants for a specific model
variants = client.list_model_versions("qualcomm-aihub", "yolov8_det")
print(variants["versions"])

# Delete provider by name
success = client.delete_external_provider(provider_name)
```

#### Model Import

```python
from zededa_eip_client.client import ZededaEIPClient

client = ZededaEIPClient()

# RECOMMENDED: Upload files from your local machine
job = client.upload_model_from_local_files(
    provider_id="local-provider-id",
    organization_id="my-organization",
    model_name="My Local Model",
    file_paths="/path/to/model.pt",  # Single file
    # Or multiple files:
    # file_paths=["/path/to/model.pt", "/path/to/config.json"],
    import_config={
        "model_version": "1.0",
        "metadata": {"framework": "pytorch", "task": "classification"}
    },
    wait=True
)
print(f"Upload completed: {job['status']}")

# Import model from external provider (async)
job = client.import_model_from_external_provider({
    "provider_id": "provider-123",
    "model_identifier": "organization/model-name",
    "organization_id": "organization-456",
    "model_name": "My Model",
    "model_version": "1.0",
    "metadata": {"task": "object-detection"}
})
print(f"Import job created: {job['job_id']}")

# Import and wait for completion
job = client.import_model_from_external_provider({
    "provider_id": "provider-123",
    "model_identifier": "organization/model-name",
    "organization_id": "organization-456"
}, wait=True)
print(f"Import completed: {job['status']}")

# Import from Qualcomm AI Hub using provider name
qualcomm_job = client.import_model_from_external_provider({
    "provider_name": "qualcomm-aihub",
    "model_identifier": "yolov8_det",
    "model_name": "qualcomm-yolov8",
})

# Import a device-specific Qualcomm AI Hub variant
qualcomm_job_qcs9075 = client.import_model_from_external_provider({
    "provider_name": "qualcomm-aihub",
    "model_identifier": "yolov8_det",
    "model_name": "qualcomm-yolov8-qcs9075",
    "qualcomm_device": "QCS9075",
    "qualcomm_precision": "fp16",
}, wait=True)

# List jobs
jobs = client.list_import_jobs(limit=20, page=1, organization_id="organization-456", status="completed")

# Get job status
job = client.get_import_job("job-id-789")
print(f"Status: {job['status']}, Progress: {job.get('progress', 0)}%")

# Cancel job
result = client.cancel_import_job("job-id-789")

# Retry failed job
result = client.retry_import_job("job-id-789")

# Delete job
result = client.delete_import_job("job-id-789")
```

#### Module-Level Convenience Functions

```python
from zededa_eip_client.client import (
    list_external_providers,
    create_external_provider,
    list_model_versions,
    import_model_from_external_provider,
    list_import_jobs
)

# List providers
providers = list_external_providers(limit=50)

# Create provider
provider = create_external_provider({
    "name": "HuggingFace Hub",
    "type": "huggingface",
    "url": "https://huggingface.co"
})

# Import model
job = import_model_from_external_provider({
    "provider_id": provider["id"],
    "model_identifier": "facebook/detr-resnet-50",
    "organization_id": "my-organization"
}, wait=True)

# Inspect Qualcomm AI Hub model variants
variants = list_model_versions("qualcomm-aihub", "yolov8_det")

# List jobs
jobs = list_import_jobs(status="completed")
```

#### Provider Types

Supported provider types:
- `huggingface` - HuggingFace Hub
- `azure` - Azure ML
- `mlflow` - MLflow Registry
- `s3` - AWS S3
- `blob` - Azure Blob Storage
- `local` - Local filesystem
- `sagemaker` - AWS SageMaker
- `nvidia_ngc` - NVIDIA NGC Model Registry
- `qualcomm_aihub` - Qualcomm AI Hub model catalog

#### Import Job Statuses

- `pending` - Job created, waiting to start
- `running` - Currently executing
- `completed` - Successfully finished
- `failed` - Execution failed
- `cancelled` - User cancelled

## Architecture Overview

```
zededa_eip_client/
├── commands/              # CLI sub-command modules
│   ├── login.py           # CLI handler + reusable login workflow helper
│   ├── catalogs.py        # CLI handler + catalog listing workflow
│   └── set_catalog_context.py # CLI handler for catalog switching with shell launch
├── services/              # Low-level backend interactions
│   ├── http.py            # Debug-aware HTTP client built on requests
│   ├── auth.py            # OAuth browser flow and callback server
│   ├── catalogs.py        # Catalog discovery and token scoping helpers
│   └── storage.py         # MinIO credential retrieval
├── environment.py         # Environment application/sanitisation helpers
├── client.py              # Public high-level Python API
└── zededa_eip_sdk.py      # Service coordination facade
```

Adding a new command means:

1. Create `zededa_eip_client/commands/<command>.py` with a `CommandSpec`
   registration function.
2. Implement the workflow using the shared services.
3. Optionally expose convenient helpers from `client.py` or `__init__.py`.

The CLI automatically discovers commands from the registry.

## Environment Variables

The login workflow applies the following variables to the current process and any spawned shells:

- `EDGEAI_CURRENT_ORG`
- `EDGEAI_CURRENT_CATALOG`
- `EDGEAI_ACCESS_TOKEN`
- `EDGEAI_USER_ROLE`
- `EDGEAI_USER_ID`
- `MLFLOW_TRACKING_TOKEN`
- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`
- `MLFLOW_S3_ENDPOINT_URL`
- `MLFLOW_TRACKING_URI`
- `MINIO_BUCKET`
- `EDGEAI_BACKEND_URL`

Use `zededa_eip_client.environment.APPLIED_ENVIRONMENT_KEYS` for the authoritative list.

## Development

```bash
# Run unit tests (creates/uses the local virtual environment)
./.venv/bin/python -m unittest discover -s tests

# Lint or format as needed
ruff check
black zededa_eip_client tests
```

All new features should include a matching command module and, when 
backend access is required, a focused service module.

## Troubleshooting

- Pass `--debug` to log all HTTP requests/responses with sensitive fields masked.
- If the browser doesn't open automatically, copy the printed URL into a browser window manually.
- To retry a failed OAuth flow, simply rerun the command; a fresh callback port is selected automatically.

## Support

- File an issue at [github.com/zededa/edgeai](https://github.com/zededa/edgeai)
- Email support@zededa.com
