Metadata-Version: 2.4
Name: outerproduct
Version: 0.3.0
Summary: Python SDK for the OuterProduct API
Project-URL: Documentation, https://docs.outerproduct.com
Project-URL: Homepage, https://outerproduct.com
Author: OuterProduct, Inc.
License-Expression: MIT
License-File: LICENSE
Keywords: explainability,machine-learning,outerproduct,sdk,xai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: httpx~=0.27
Requires-Dist: narwhals>=1.13
Requires-Dist: numpy~=1.23
Requires-Dist: outerproduct-http-types
Requires-Dist: pandas~=2.0
Requires-Dist: polars<=1.41.0
Requires-Dist: pydantic<3,>=2.0
Provides-Extra: dev
Requires-Dist: anyio; extra == 'dev'
Requires-Dist: pyarrow>=14.0; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: respx; extra == 'dev'
Provides-Extra: parquet
Requires-Dist: pyarrow>=14.0; extra == 'parquet'
Description-Content-Type: text/markdown

# outerproduct-sdk

Python SDK for training and explaining tabular ML models with the OuterProduct API.

## Installation

```bash
uv add outerproduct
```

Or with pip:

```bash
pip install outerproduct
```

## Quick Start

```python
import outerproduct as op

# 1. Initialize the SDK
op.init(api_key="op_live_...")

# 2. Upload data
dataset = op.LocalDataset.from_csv("credit.csv").upload()

# 3. Train a reasoning model (returns a job handle; .wait() blocks for the model)
rm = op.reasoning.fit(dataset).wait()

# 4. Predict
predictions = rm.predict(X_new)

# 5. Explain
reasoning = rm.explain(X_new)
print(reasoning.feature_names[:3])
print(reasoning.attributions)
```

You can also set `OUTERPRODUCT_API_KEY` as an environment variable and call `op.init()` with no arguments.

## Usage

### Build a Dataset from Code

```python
import pandas as pd
import outerproduct as op

df = pd.read_csv("credit.csv")
dataset = op.Dataset.from_pandas(df, label_column="default")
```

### Tabularize Documents into a Dataset

```python
from outerproduct.agentic import documents

docs = documents.DocumentSet.from_directory("./invoices")
refs = documents.upload(docs)
schema = documents.induce_schema(refs, use_case="fraud audit", skill="invoice")
dataset = documents.tabularize(refs, schema, label_column="is_fraud")
```

You can also hand-author the schema:

```python
from outerproduct.agentic.documents import DocumentSchema, DocumentQuestion, AnswerType

schema = DocumentSchema(
    skill="invoice",
    use_case="fraud audit",
    questions=[
        DocumentQuestion(id="invoice_total", question="Total amount due?", answer_type=AnswerType.NUMBER, unit="USD"),
        DocumentQuestion(id="payment_terms", question="Payment terms?", answer_type=AnswerType.ENUM, enum=("net_15", "net_30", "net_60")),
    ],
)
```

### Train a Plain Model (No Explanations)

```python
trainer = op.Trainer.configure(dataset, model_types=["tabm", "xgboost"])
model = trainer.run(strategy="random").wait()
predictions = model.predict(X_new)
```

### Distill a Black-Box Model

```python
# Wrap an external prediction endpoint
teacher = op.model.Predictor(
    "https://your-model-server.com/predict",
    headers={"Authorization": "Bearer ..."},
)

# Distill into a reasoning model
rm = op.reasoning.fit(dataset, teacher=teacher).wait()
```

You can also distill an existing OuterProduct-trained model:

```python
trained = op.Trainer.configure(dataset).run().wait()
rm = op.reasoning.fit(dataset, teacher=trained).wait()
```

### Reasoning Model Operations

```python
# Per-sample explanations
reasoning = rm.explain(X)

# Predict + explain in one call
predictions, reasoning = rm.predict_and_explain(X)

# Global feature importance
reasoning = rm.get_global_drivers()

# Counterfactual scenario search
trace = rm.scenario(X, target_class=1, max_steps=10)

# Pattern aggregation across rejected predictions
import outerproduct as op
pt = op.reasoning.pattern_tracker.fit(rm, dataset, target_range=(0.5, None)).wait()
pt.distribution(X_new)
```

### Jobs

Every async server operation — `Trainer.run()`, `reasoning.fit()`,
`pattern_tracker.fit()` — submits the work and returns a typed job handle
(`TrainerJob`, `ReasoningFitJob`, `PatternTrackerJob`). Drive it however you
need:

```python
job = op.reasoning.fit(dataset)

job.status()      # "pending" | "running" | "completed" | "failed"
job.results()     # typed JobResult once finished (raises JobFailed on failure)
rm = job.wait()   # block until completion, return the trained ReasoningModel
```

### Configuration

```python
import outerproduct as op

# Reads OUTERPRODUCT_API_KEY from environment
op.init()

# All options
op.init(
    api_key="op_live_...",
    base_url="https://api.outerproduct.com",
)
```
