Metadata-Version: 2.4
Name: mini-oss-osdk
Version: 0.1.0a11
Summary: Async Python client and object facade for the ITEM Mini-OSS runtime
License-Expression: LicenseRef-Proprietary
Keywords: item,ontology,osdk,object-query
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
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: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1,>=0.27
Dynamic: license-file

# Mini-OSS Python OSDK

Async Python builder/runtime for canonical Object Query AST, Engine-backed SQL
explain, Tenant-scoped Operation polling and bounded Result Gateway access.
**`0.1.0a11`** adds explicit `collect_set` / `collect_list`, typed Array results,
and Catalog-bound related scalar selection. Python 3.9+ remains supported.

```bash
python -m pip install --upgrade "mini-oss-osdk==0.1.0a11"
```

This is a pre-GA alpha package, not a `.dev` build. Pin the exact version;
unqualified pip upgrades may skip prereleases. Public package availability does
not grant a software license (see [LICENSE](LICENSE)). The import stays
`mini_oss_osdk`.

## a11 compatibility and migration

- Related scalar selection requires a **cardinality.1-compatible Engine/Catalog**.
  Remove `assert_zero_or_one`: explicit True **and** False are ignored and emit
  `AssertZeroOrOneDeprecatedWarning`; omitting it does not warn. The published
  Catalog, not the caller or SDK, determines whether every path hop is ONE.
- Collections require **Query/ResultRef `1.0.0-collection.1`** support. Both
  `collect_set` and `collect_list` return typed lists. The SDK does not aggregate
  client-side or decode Athena display text.
- Ordinary queries retain Alpha.8, old derived queries Alpha.9, and native-only
  expressions native.1. Operation/Explain and primitive-only ResultRef remain
  Alpha.8. No silent downgrade, scalar-to-array fallback or guessed cardinality.
- The matching Engine/Result and cardinality Catalogs have been deployed in
  staging and production. Configure matching service endpoints explicitly;
  installing the SDK does not deploy services, activate models, or upgrade
  Gateway/MCP/Web validators. Those collection entry points are not adapted.
- Source/model type errors still fail closed. The production public-demo
  `Airport.longitude` Float declaration vs Double physical source is a known
  model limitation, not something this SDK coerces or repairs.

See [scalar migration](docs/cardinality.md), [collection semantics and
limits](docs/collections.md), [historical a10 native API](docs/native-completion.md),
and [a11 release verification](docs/deployment/2026-09-19-osdk-a11-release.md).
No row expansion, `left_link`, materialization or full205 replacement is included.

## Collect query-local values

```python
from mini_oss_osdk import ObjectSet

query = ObjectSet.base("Sales", "Order").with_properties(
    productNames=lambda root: root.pivot_to("Sales", "lines").collect_set(
        "productName", limit=100, require_complete=True,
    ),
    productNamesWithDuplicates=lambda root: root.pivot_to("Sales", "lines").collect_list(
        "productName", limit=100,
    ),
).fetch_page(select=("productNames", "productNamesWithDuplicates"), page_size=25)
```

Names are synthetic. Building the query is zero-I/O. NULL values are skipped;
no target/all missing values yields `[]`. Candidates deduplicate typed
`(target identity, value)` first; list preserves equal values from different
identities, while set deduplicates typed values further. There is **no array order
or cross-field alignment/zip guarantee**. Arrays are projection-only, not Catalog
Properties or scalar/filter/order/group inputs.

`limit` (1–100) bounds returned elements, not Athena scanning or aggregation
memory. Standard results may truncate; `require_complete=True` requests failure
instead of truncated success. Independent byte/page budgets still apply. SDK
fetches and caches immutable ResultRef metadata (a cold page adds a metadata GET),
validates arrays, and maps object arrays to read-only list values. Long/Decimal
remain exact strings. Nothing in this API repairs upstream data or picks a value
for a scalar path.

## Build a qualified cross-Namespace query

Namespace API names are exact, case-sensitive PascalCase programming identifiers.
The first `pivot_to` argument is the Namespace that owns the Link, not the target
ObjectType Namespace. Both arguments are required; the old one-argument builder is
not supported.

```python
from mini_oss_osdk import Field, ObjectSet

query = (
    ObjectSet.base("FlightStage", "Flight")
    .where(Field("flightNumber").eq("SYN-001"))
    .pivot_to("Commerce", "orders")
    .fetch_page(select=("id", "status"), page_size=25)
)
```

Ordinary queries use the alpha.8 recursive AST and its schema URL:
`namespace` and `linkNamespace` contain API names, never Namespace short IDs or
display names. A Link owner may be a third Namespace, for example
`.pivot_to("Scheduling", "assignedFlights")` from `Fleet.Aircraft`.

## Build opt-in derived properties

**New in `0.1.0a9`:** public `0.1.0a8` does not provide this API. Derived queries
require an Engine supporting Query Alpha.9; an older Engine rejects them and the
SDK does not retry with a downgraded query. See [derived-property usage and
compatibility](docs/derived-properties.md). Installing this package does not
deploy services or upgrade Tool Gateway/MCP validators.

`with_properties()` is a lazy query-local projection. The callback receives an
expression-only root; it never receives a result row or a service client. CASE is
an ITEM query extension (not a claim about a matching Palantir Python API), and
its branch order and explicit `otherwise` are part of the query semantics:

```python
from mini_oss_osdk import Field, ObjectSet, case_when, literal

query = (
    ObjectSet.base("Sales", "Order")
    .with_properties(
        statusLabel=lambda root: case_when(
            (root.select_property("status").eq(1), literal("ready")),
            otherwise=literal("other"),
        ),
        lineCount=lambda root: root.pivot_to("Sales", "lines").count(),
    )
    .where(Field("statusLabel").eq("ready"))
    .fetch_page(select=("orderNo", "statusLabel", "lineCount"))
)
```

Existing a9-compatible `with_properties` constructions retain their Alpha.9 wire.
New native expressions explicitly choose native.1. No expression is evaluated in
Python, and scalar cardinality is never inferred from a local Catalog guess.

## Native expressions and type constants (a10)

```python
from mini_oss_osdk import ObjectSet, cast, coalesce, concat, literal, type

objects = ObjectSet.base("Sales", "Order").with_properties(
    customerName=lambda r: coalesce(
        r.pivot_to("Sales", "customer").select_property("name"),
        literal("Unknown"),
    ),
    exportVersion=lambda r: cast(literal("42"), type.Long),
)
query = objects.with_properties(
    label=lambda r: concat(
        r.derived_property("customerName"),
        r.derived_property("exportVersion").cast(type.String),
    ),
).fetch_page(select=("label",), page_size=25)
```

Names above are synthetic; use published Namespace/traversal/Property API names.
In a11, the **published Catalog** controls whether this scalar
path is allowed; do not supply `assert_zero_or_one`. The SQL still returns NULL
for no target and fails for multiple identity/value candidates, even for equal
values on distinct identities. It never picks first/MIN/MAX. This differs from
the historical a10 caller assertion; see the migration guide above. `pivot_to`
remains a deduplicated ObjectSet traversal, not row expansion.

Native.1 also supports nested ordered CASE, Boolean/comparison/null expressions,
previous-batch aliases, controlled strict CAST and `query_timestamp()`. It does
not add arbitrary SQL/UDFs or broaden CAST implicitly. Traversal support is validated by the matching Engine; unsupported paths are
rejected, never emulated locally. Long/Decimal preserve exact wire strings.

`from mini_oss_osdk import type` exposes nine canonical plain-string constants:
Boolean, Integer, Long, Float, Double, Decimal, String, Date and Timestamp.
For example `cast(expr, type.Long)` and `cast(expr, "long")` have identical wire.
Use `type as T` if Python's built-in `type()` is needed; wildcard imports do not
export the `type` namespace. Type constants do not widen the Engine allowlist.

## Explain a query without executing it

```python
from mini_oss_osdk import OSDKClient

async with OSDKClient(
    tenant_id="01jabcdefgh1",
    object_engine_url="https://object-engine.example.com",
    result_gateway_url="https://object-results.example.com",
    api_key=tenant_api_key,
) as client:
    explanation = await client.explain(query=query)
    print(explanation.athena_sql)
    print(explanation.manifest_id, explanation.manifest_content_hash)
```

`explain()` delegates binding, planning and SQL rendering to Object Engine. It
returns `ExplainResult` with alpha.8 manifest lineage, warnings, the logical plan
and generated `athena_sql`; it does not submit Athena, create an Operation or
access Result Gateway. The low-level equivalent is
`ObjectEngineClient.explain(...)`.

Generated SQL can contain source details or business literals. Do not record it in
logs, traces or telemetry.

## High-level object lookup

```python
from mini_oss_osdk import OSDKClient, case_when, literal

async with OSDKClient(
    tenant_id="01jabcdefgh1",
    object_engine_url="https://object-engine.example.com",
    result_gateway_url="https://object-results.example.com",
    api_key=tenant_api_key,
) as client:
    flight = await client.ontology.objects.object(
        "FlightStage", "Flight"
    ).get(
        "F001",
        select=("flightNumber",),
    )

    if flight is not None:
        print(flight.rid, flight.namespace_id, flight.namespace_api_name)
        print(flight.primary_key, flight["flightNumber"])
```

`get()` orchestrates qualified canonical lookup → Tenant-scoped Engine execute →
Operation wait → ResultRef lineage validation → bounded Result page. It never
reads Dataset/Redash/Athena metadata, creates SQL, guesses a Namespace from a RID,
or fills missing `$namespaceId`/`$namespace` fields from the lookup entry. Generic
runtime has no Catalog property list, so an omitted/empty `select` retains the
deployed server behavior of returning all published base properties (and all
currently visible query-local derived properties). Callers can list Property API
names explicitly when they need a bounded projection. A future generated ontology
package may provide that static list.

For query-local fields on one object, pass a `with_properties` mapping to `get()`:

```python
flight = await client.ontology.objects.object("FlightStage", "Flight").get(
    1,  # Example integer primary key; use the type declared by your model.
    select=("flightId", "origin", "originLabel"),
    with_properties={
        "originLabel": lambda root: case_when(
            (root.select_property("origin").eq("EWR"), literal("Newark")),
            otherwise=literal("Other or unknown"),
        ),
    },
)
```

The example assumes those ObjectType/Property API names exist in your active
model. `get()` handles execution, polling and Result validation; the derived
value is available as `flight["originLabel"]` when the object exists.

Both service endpoints are required. HTTPS is mandatory except loopback HTTP used
by tests. Configure exactly one public credential: `api_key` for the Tenant API
Key lane or `bearer_token` for the delegated Cognito ID Token lane. A local wait
timeout does not cancel the server Operation. Long and Decimal result values stay
as wire strings, and Result Gateway cursors remain opaque.

## Alpha.8 migration

Alpha.8 is a deliberately breaking runtime switch. Replace Namespace short-ID
arguments with Namespace API names and qualify every base and Link traversal;
move Engine/Operation/Result calls to Tenant routes; and do not reuse alpha.7
Operation, ResultRef, cursor or query wire. See
[`docs/migration-alpha8.md`](docs/migration-alpha8.md) for the mapping and
rejection behavior.

Generated ontology-specific classes are not implemented.

## Distribution verification

```bash
python -m unittest discover -s tests
python -m ruff check src tests
python -m build
python -m twine check dist/*
python scripts/verify_distribution.py dist
```

Branch and `main` pipelines only test and build. A `v<version>` tag is the sole
public PyPI upload authority, and the tag must exactly match wheel metadata before
upload. Installing this client does not deploy services or migrate saved queries.

## License

Proprietary. Copyright (c) 2026 ITEM. All rights reserved. See [`LICENSE`](LICENSE);
public package availability does not grant permission to use, modify, or
redistribute the software.

Timestamp derived literals follow existing Result Wire v1: offset-bearing RFC3339, at most six fractional digits, UTC output. Higher precision is rejected, never silently truncated.
