Metadata-Version: 2.5
Name: algoriq
Version: 0.4.0
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.

⛔ **Installing does not need the credential, and it comes first.** `pip install
algoriq` resolves from PyPI like any other package, and the device authorization
above runs afterwards, from inside the kernel you just installed into. The
credential authorizes the _planes_ — reading ingredients, submitting research —
never the download.

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.
