Metadata-Version: 2.4
Name: credit-platform-sdk
Version: 1.0.0
Summary: Python SDK for the public CREDIT Platform APIs
Author: IDENER
Requires-Python: >=3.10,<4.0
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: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Requires-Dist: attrs (>=22.2.0)
Requires-Dist: httpx (>=0.23.0,<0.29.0) ; python_version < "3.13"
Requires-Dist: httpx (>=0.24.0,<0.29.0) ; python_version == "3.13"
Requires-Dist: httpx (>=0.26.0,<0.29.0) ; python_version >= "3.14"
Requires-Dist: python-dateutil (>=2.8.0,<3.0.0)
Description-Content-Type: text/markdown

# CREDIT Platform Python SDK

Synchronous and asynchronous Python clients for the supported public CREDIT HTTP APIs.

The SDK covers authentication and users, tenant/space API keys, semantic entities,
datasources and alerts, batch/media files, data-serving queries, and the public ML model
lifecycle, metrics, and prediction operations. Internal supervisor/callback routes and
service-to-service identity headers are deliberately excluded.

## Installation

The package supports Python 3.10 and newer. HTTPX must be 0.24 or newer on
Python 3.13, and 0.26 or newer on Python 3.14 and later. See
[validation notes](docs/SDK_UPDATE_NOTES.md#validation) for test isolation and cleanup.
The [live test controls](docs/LIVE_TESTS.md) describe development-only execution,
the S-LCA test scope, and the default infrastructure verifier with an explicit
public API cleanup option for the four non-ML success cases.

```bash
pip install credit-platform-sdk
```

## Clients and authentication

```python
from credit_sdk import CreditClient

with CreditClient(
    "https://api.example.invalid",
    token="access-token",
) as client:
    spaces = client.semantic.spaces.list(tenant_id)
```

API keys use the gateway's `X-Entity-ApiKey` header automatically:

```python
client = CreditClient(base_url, api_key="credit-api-key")
```

`token` and `api_key` are mutually exclusive. Authentication tokens can be obtained and
refreshed through `client.auth.token(username, password)` and
`client.auth.refresh(refresh_token)`.

The asynchronous client exposes the same groups with genuinely asynchronous type
signatures:

```python
from credit_sdk import AsyncCreditClient

async with AsyncCreditClient(base_url, token=token) as client:
    individuals = await client.semantic.individuals.list(tenant_id, space_id)
```

## Supported API groups

- `client.auth`: login and refresh.
- `client.tenants`: get, create, update, and delete.
- `client.users`: tenant users, roles, current-user tenant/space views, and user CRUD.
- `client.api_keys`: tenant/space list, get, create, delete, assigned list, and reveal.
- `client.semantic.spaces`: CRUD, linking, unlinking, and linked-space positioning.
- `client.semantic.individual_types`, `individuals`, `property_types`,
  `relation_types`, and `relations`: CRUD and `create_many`.
- `client.semantic.properties`: individual-scoped CRUD and `create_many`.
- `client.semantic.datasources`: space CRUD and tenant-wide listing.
- `client.semantic.alerts`: tenant/space/datasource lists, acknowledgement, and settings.
- `client.semantic.models`: list/get/train/upload/update/delete/serve/unserve.
- `client.files`: batch ingestion and media upload/list/download.
- `client.data_serving`: wide-table and long-table queries, result retrieval, and bounded polling.
- `client.mlops`: model metrics and prediction.
- `client.lca.assessments`: space-scoped LCA/LCC assessment CRUD.
- `client.lca.factors`: tenant-default and space emission factors, including provider reads.
- `client.slca.assessments` and `client.slca.social_references`: tenant S-LCA operations.

## Typed requests and responses

Request models serialize service field aliases, UUIDs, enums, dates, and nested values.
Every JSON operation also accepts ordinary mappings/lists.

Responses remain ordinary dictionaries/lists at runtime so newly-added server fields are
not discarded. Public `TypedDict` response contracts and `platform.pyi` provide stable
field discovery and distinct sync/async return types to IDEs and type checkers.

The semantic and user services use replacement `PUT` payloads. Update models raise
`ValueError` during serialization if a required replacement field is omitted. Datasource
variable lists must be explicit lists, not null. The datasource body ID is optional;
the update uses the ID in the URL. Read the current state and pass every field that
must stay unchanged. The SDK performs no hidden
read or merge. Plain mappings are sent as supplied and bypass this model guard.

```python
from credit_sdk.models import UpdateIndividualRequest

current = client.semantic.individuals.get(tenant_id, space_id, individual_id)
client.semantic.individuals.update(
    tenant_id,
    space_id,
    individual_id,
    UpdateIndividualRequest(
        name=current["name"],
        description="Updated description",
        x=current["x"],
        y=current["y"],
        has_data_source=current["hasDataSource"],
    ),
)
```

Individuals, relations, and properties accept `user_defined_id` on create/update.
On update, omitted or null `user_defined_id` preserves the identifier; `""` clears it.
Other fields follow their own service rules. See the replacement-field table in
[SDK_UPDATE_NOTES.md](docs/SDK_UPDATE_NOTES.md).

User creation requires `client.users.create(body, tenant_id=tenant_id)`. User updates
require the complete desired tenant membership lists. Omitted/null groups preserve
Keycloak group membership; an explicit empty group list requests removal.

## Datasources and data queries

```python
from datetime import datetime, timedelta, timezone

end = datetime.now(timezone.utc)
rows = client.data_serving.wide_table(
    tenant_id,
    space_id,
    datasource_id,
    columns=["temperature"],
    start_datetime=end - timedelta(days=7),
    end_datetime=end,
    max_registers=100,
    order_by_ascending=False,
)

long_rows = client.data_serving.long_table(
    tenant_id,
    space_id,
    datasource_id,
    variable_name="temperature",
    start_datetime=end - timedelta(days=7),
    end_datetime=end,
)
```

Wide-table queries accept an optional `columns` list. Long-table queries require one
`variable_name`. Both accept `start_datetime`, `end_datetime`, `date_format`,
`max_registers`, `reset`, and `order_by_ascending`.

Historical queries can return `{"queryId": "..."}`. Use
`client.data_serving.get_result(tenant_id, space_id, datasource_id, query_id)` to retrieve
the result, or `wait_for_result(..., timeout=300, poll_interval=2)` to poll. Both clients
support these methods; the async wait uses cancellable async sleeps. A result of `None`
from retrieval means pending. Existing submission methods keep their raw return values.

An empty result is complete, not pending. A polling timeout or cancellation of the
async wait stops the client wait; it does not cancel the server query. The data-serving
service has a known historical-query cancellation issue. The SDK does not expose a
dedicated query cancellation method.

Historical queries can read published Druid segments only. Fresh uploads can appear in
ordinary queries before they appear in historical results. Query responses can also
include an all-null Druid header row; the SDK preserves the service response.

## Batch files and media

The gateway allows a maximum DSS request body of 1 GiB, including multipart overhead.
Use an open binary file stream and set a suitable client timeout for larger uploads.
The SDK reads the stream in chunks. HTTP 413 raises `ApiError`; a larger timeout does
not increase the server size limit. Other service upload limits are independent.

The DSS batch `Variables` field must map each CSV column to an existing datasource
variable ID. It is not a list containing only variable names.

```python
import json

variable_map = json.dumps(
    [
        {
            "DataSourceVariableId": datasource_variable_id,
            "FileVariableName": "temperature",
        }
    ]
)

with open("measurements.csv", "rb") as source:
    client.files.process_batch(
        tenant_id,
        space_id,
        datasource_id,
        source,
        variables=variable_map,
        file_extension="csv",
        timestamp_column_name="timestamp",
    )
```

## Alerts and MLOps

Retraining or uploading over an existing model, serving it, and stopping service require
model update permission. Read serving state from `isServed`; the MLflow production stage
does not prove that a model is served. These calls have no automatic SDK retry.

Alert creation is intentionally omitted. Failed-row alerts are produced internally by
the ingestion pipeline; public callers can list, enable/disable, and acknowledge them.

```python
from credit_sdk.models import PredictionRequest

metrics = client.mlops.metrics(tenant_id, space_id, model_id)
prediction = client.mlops.predict(
    tenant_id,
    space_id,
    datasource_id,
    PredictionRequest(
        model_id=model_id,
        model_name=model_name,
        instances=[[21.4, 42.0]],
    ),
)
```

## API keys

API key management uses the record ID. Authentication uses its separate secret:
pass the secret as `CreditClient(base_url, api_key=secret)` (or the async equivalent).
An unassigned create response contains the secret once. For an assigned key, only
the assignee can call `reveal` once; that response contains only `key`.
List/get/assigned-list responses always return `key: ""`.

`create` returns `ApiKeyResponse`; metadata reads return `MaskedApiKeyResponse`;
`reveal` returns `ApiKeyRevealResponse`. A second reveal raises `ApiError` with
HTTP 410. Delete uses the record ID. Gateway caching can delay rejection of a
deleted key (configured positive-cache TTL; currently 60 seconds in source).
See the [lifecycle notes](docs/SDK_UPDATE_NOTES.md#api-key-lifecycle-and-purpose-examples)
and the existing `examples/workflows/api_keys.py` workflow. The workflow never prints secrets.

## Errors

All non-success responses raise `ApiError`. Besides the original response body, it
exposes normalized problem-details context:

```python
from credit_sdk.errors import ApiError

try:
    client.semantic.individuals.get(tenant_id, space_id, individual_id)
except ApiError as error:
    print(error.status_code, error.message, error.trace_id, error.request_id)
    print(error.validation_errors, error.context, error.headers)
```

## Repository layout

```text
credit_sdk/             maintained package implementation
examples/workflows/     executable mock/live end-to-end examples
scripts/                workflow runner
tests/unit/             fast transport-isolated tests
docs/                   verified API notes and request contracts
```

Run all workflows against the deterministic mock gateway:

```bash
poetry install
poetry run python scripts/run_workflows.py
```

For live execution, copy `.env.example` to `.env`. The ignored `.env` is loaded without
overriding values already present in the shell. The runner forces mock mode unless
you pass `--live`; a local `.env` cannot enable live mode for a normal run.
Live workflows permit only the approved development URL from the local, ignored
`.development-settings.json` file. Missing settings block live execution.
Main/production and every other live URL are blocked before HTTP. Set
`CREDIT_BASE_URL`, `CREDIT_TENANT_ID`, `CREDIT_SPACE_ID`, and either a current
`CREDIT_ACCESS_TOKEN` or `CREDIT_USERNAME` plus `CREDIT_PASSWORD`. Existing-resource
workflows also require `CREDIT_INDIVIDUAL_ID` and `CREDIT_DATASOURCE_ID`.
Unit/contract tests use in-process HTTP mocks and reject real HTTP transports.

Username/password takes precedence over `CREDIT_ACCESS_TOKEN` in workflow mode so a stale
shell token cannot silently override a fresh login. `CREDIT_USER_ID` is optional and is
normally derived from the JWT subject. Tenant/user mutations remain opt-in through
`CREDIT_WORKFLOW_ADMIN_MUTATIONS=1`.

The MLOps workflow reads metrics from an existing registered model when one is available.
Set `CREDIT_MLOPS_MODEL_ID` to select a specific model and `CREDIT_MLOPS_INPUTS` to a JSON
array to exercise prediction against a served version.

## Intentionally excluded APIs

- Training Hub courses, enrolment/consent/monitoring, materials, and admin roles.
- DSS supervisor, processed-supervisor, cleanup, and internal datasource callbacks.
- Failed-row alert batch creation.
- Data-processing and MLOps pipeline callbacks/cleanup routes.
- Spark agent and service-to-service APIs.
- Gateway-owned `X-Entity-Id`, `X-Tenant-Id`, and role propagation headers.
- gRPC ingestion/data-serving APIs until CREDIT defines a stable external contract.
- UI-only upload/logout controllers.

See [SDK_UPDATE_NOTES.md](docs/SDK_UPDATE_NOTES.md) for the verified API contracts,
method reference, scope, and request contracts.

