Metadata-Version: 2.5
Name: algoriq
Version: 0.3.2
Summary: The AlgoriQ notebook SDK — the platform's supported compute surface (AFML labeling, purged/combinatorial CV, importance, sample weighting, indicators) plus the research submission plane client
Requires-Python: >=3.12
Requires-Dist: cryptography>=42.0.0
Requires-Dist: dcor>=0.6
Requires-Dist: econml<0.17,>=0.16.0
Requires-Dist: hmmlearn>=0.3.2
Requires-Dist: holidays>=0.65
Requires-Dist: httpx>=0.27.0
Requires-Dist: joblib>=1.3.0
Requires-Dist: numba>=0.59
Requires-Dist: numpy>=2.2.6
Requires-Dist: orjson>=3.9.0
Requires-Dist: pandas>=2.2.3
Requires-Dist: pyarrow>=15.0.0
Requires-Dist: pydantic-settings>=2.3.0
Requires-Dist: pydantic>=2.9.0
Requires-Dist: scikit-learn>=1.4.0
Requires-Dist: scipy>=1.11.0
Requires-Dist: structlog>=25.0.0
Requires-Dist: tenacity>=8.2.0
Requires-Dist: threadpoolctl>=3.1.0
Provides-Extra: boosting
Requires-Dist: catboost>=1.2; extra == 'boosting'
Requires-Dist: lightgbm>=4.5.0; extra == 'boosting'
Requires-Dist: ngboost>=0.5; extra == 'boosting'
Requires-Dist: xgboost>=2.1.0; extra == 'boosting'
Provides-Extra: foundation
Requires-Dist: chronos-forecasting>=1.4; extra == 'foundation'
Requires-Dist: torch<2.6,>=2.2; extra == 'foundation'
Provides-Extra: mlx
Requires-Dist: cvxpy>=1.5.0; extra == 'mlx'
Requires-Dist: cvxpylayers>=1.2.0; extra == 'mlx'
Requires-Dist: mlx>=0.31.0; (sys_platform == 'darwin' and platform_machine == 'arm64') and extra == 'mlx'
Provides-Extra: signal
Requires-Dist: pywavelets>=1.7.0; extra == 'signal'
Requires-Dist: statsmodels>=0.15.0; extra == 'signal'
Provides-Extra: symbolic
Requires-Dist: pysr<2,>=1.5; extra == 'symbolic'
Provides-Extra: tabpfn
Requires-Dist: tabpfn>=2.0; extra == 'tabpfn'
Requires-Dist: torch<2.6,>=2.2; extra == 'tabpfn'
Description-Content-Type: text/markdown

# algoriq — the AlgoriQ notebook SDK

```bash
pip install algoriq
```

The pro-code side of AlgoriQ. Install it in any Python kernel — Colab, a local
Jupyter, a VS Code notebook — and you get two things:

1. **The supported compute surface** of the platform: AFML labeling, purged and
   combinatorial cross-validation, MDI/MDA/SFI importance, sample weighting,
   covariance denoising and ONC clustering, indicators and fractional
   differentiation. These are re-exports, not copies — the notebook runs the
   same code the platform runs.
2. **The research submission plane client**: declare an observation, seal a
   snapshot, submit a result, read it back.

```python
import algoriq as aq

aq.print_capabilities()          # what this install actually supports
client = aq.ResearchClient()     # reads ALGORIQ_SUBMISSION_TOKEN
```

> `import algoriq_research as aq` names the same surface and keeps working —
> that is the package this SDK is built as, and notebooks written against it do
> not need editing. `algoriq` is the name the distribution is installed under, so
> it is the one the first line should say.

## Connecting

**There is one connection flow and it does not depend on where your kernel
runs.** The notebook starts a device authorization (RFC 8628), prints a short
code and a link, and you approve it in a browser already signed in to AlgoriQ —
choosing on that screen which account the notebook acts as. The credential is
then issued straight to the kernel, into `ALGORIQ_SUBMISSION_TOKEN`, and the SDK
sends it as a bearer. Nothing else identifies you. Colab, a local Jupyter and a
VS Code kernel take the identical path; no step of this has a per-runtime
variant.

The same credential fetches the SDK itself, because the wheel is served by
AlgoriQ rather than by a public index: you download it with the credential,
check it against the digest the server published, and install that exact file.
So connecting comes first and installing second, on every runtime.

Both steps, working, are cells 1 and 2 of
[`examples/notebooks/research_roundtrip.ipynb`](examples/notebooks/research_roundtrip.ipynb).

⛔ Never paste a credential into a notebook cell. A cell's contents are saved
with the notebook, and a notebook is the thing people share. The device grant is
what makes that avoidable — you never see the credential at all.

⛔ Never put a credential on a `pip` command line or in an index URL. It belongs
in an `Authorization` header: a command line lands in shell history, in the cell
output saved with the notebook, and in the process table of a shared machine.

⚠️ Send an explicit `User-Agent` on any hand-rolled HTTP call to `*.algoriq.ai`.
The edge refuses `urllib`'s default `Python-urllib/*` signature with a `403`
whose body is `error code: 1010` — not JSON, and not our `401`, so a client that
reads only the status will report the wrong cause. Every other user agent tested
is accepted, `httpx`'s default among them, so this bites hand-rolled `urllib`
calls — the bootstrap and install cells — and not the SDK.

The credential is **opaque** to this SDK: it is read, sent, and never parsed. A
`401` means it is absent, expired or revoked, and **those three do not share a
remedy** — `NotAuthenticated.reason` carries the plane's own wording, so read it
rather than assuming. Absent or expired: run the connection cell again (an
expired credential can also be exchanged with its refresh token). Revoked: a
refresh will not restore it and re-running the cell cannot succeed, because the
grant itself was withdrawn — it has to be granted again. Revocation takes effect
on the next call.

A `403` is a different question with the same look: the credential is valid and
this account may not do this. The `WWW-Authenticate` header names the scope the
route required.

## What "supported" means, and what it does not

`print_capabilities()` answers by **running** each capability, not by reading a
table, so it cannot claim support this environment does not have. Every row comes
back as one of:

| Status                | Meaning                                    | What you do                                         |
| --------------------- | ------------------------------------------ | --------------------------------------------------- |
| `available`           | Installed and proven to execute here       | Nothing                                             |
| `not_installed`       | Its extra is not installed                 | `pip install 'algoriq[<extra>]'`                    |
| `broken_install`      | **Our own** module is missing from disk    | Reinstall the wheel and tell us — no extra fixes it |
| `unsupported_runtime` | This runtime cannot have it at any version | Use a different backend — installing won't help     |
| `not_entitled`        | Your account may not do this               | A plan/permission question, raised by the server    |

`broken_install` is its own word because it is the one state where the obvious
remedy is wrong: a missing file of ours raises the same `ImportError` a missing
library does, so reporting it as `not_installed` hands you a `pip install` that
installs the libraries, changes nothing, and prints the same line again.

The extras are `boosting`, `tabpfn`, `foundation`, `symbolic`, `mlx` and `signal`. An extra
means _not in the default install_ — never _you may not use it_. You are free to
`pip install xgboost` in Colab and use it directly; the extra is what makes
**our** wrappers around it resolve.

⛔ **Widening the supported surface does not raise the evidence grade of what you
submit.** Results declared from a notebook come back graded `descriptive` with
`population_status='unverified'`, because the platform stores what you declared
and does not certify that your search was complete. That boundary is independent
of this table.

## The round trip

```python
import algoriq as aq

client = aq.ResearchClient()

# 1. declare each terminal execution — including the ones you discarded
kept = client.record_observation(declaration)

# 2. submit the selected result's bytes (declare → upload → seal, in one call)
sealed = client.submit(submission_intent, parquet_bytes)

# 3. seal the scope, naming the server's normalised digest of that result
snapshot = client.seal_snapshot(snapshot_intent)   # selected_result_sha256=sealed.normalized_sha256

# 4. bind the result to the snapshot that selected it
publication = client.publish_submission(sealed.receipt_id, publication_intent)

# 5. read it back, any time
client.get_publication(sealed.receipt_id)
```

⛔ Steps 2 and 3 are in that order for a reason: a snapshot names the
**normalised** digest of its selected result, and that digest is the server's —
it does not exist until the bytes have been sealed. Sealing the scope first
leaves you with nothing to put in `selected_result_sha256`.

`submit()` checks your declared `raw_sha256` and `size_bytes` against the bytes
**before** it opens a connection. Found here it costs nothing; found server-side
it costs the whole upload first, which on a home uplink is minutes.

`prepare_submission()` returns an upload form whose `expires_in` is a function of
the `size_bytes` you declared — a large upload is given proportionally longer.
Read it rather than assuming a constant.

Worked example: [`examples/notebooks/research_roundtrip.ipynb`](examples/notebooks/research_roundtrip.ipynb).

## Not here yet

- **Reading platform data.** The read planes (`.../research/ingredients*`,
  market-data) are live and take the same credential, but this SDK has no typed
  client for them yet, so a notebook reaches them with plain HTTP for now.
- **Installation from an index.** The release flow, version SSOT and download
  authorisation are owned by `docs/guides/notebook-sdk-distribution.md`; until a
  release is published, install from a wheel built out of this repository.

## For maintainers

The public API is not written twice. `algoriq_research.capabilities.CAPABILITIES`
is both the allowlist and the support matrix: `__all__` is derived from it, and
each name's capability is checked before the name resolves. Adding a public name
means adding it there and nowhere else.

`tests/test_distribution_boundary.py` builds the wheel and the sdist and reads
the archives — paths, metadata, declared dependencies (including the shared
package's own base list) and a secret scan. Source-tree exclusion rules are not
evidence of what shipped.
