Metadata-Version: 2.4
Name: osco
Version: 0.1.1
Summary: OptimNet supply chain ontology and data-preparation engine: typed entities, note contracts, problem specs and an ontology-driven validation engine.
Author-email: Vivek Singh <s1992vivek@users.noreply.github.com>
License-Expression: LicenseRef-Proprietary
Keywords: supply chain,ontology,schema,data validation,optimization
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: engine
Requires-Dist: polars>=0.20; extra == "engine"
Provides-Extra: dev
Requires-Dist: polars>=0.20; extra == "dev"
Requires-Dist: pytest>=7; extra == "dev"
Dynamic: license-file

# OSCO — OptimNet Supply Chain Objects

A typed supply-chain ontology and an ontology-driven data-preparation engine in one
distribution, with a clean layer boundary. Notes and packages stay separate: a note
is an engine that accepts a `ProblemSpec`; packages provide the blocks a spec names.
OptimNet is the foundation both stand on.

## Install

```bash
pip install osco            # ontology, catalog, notes, spec (no dependencies)
pip install "osco[engine]"  # plus the polars data-preparation engine
```

## Layers

| Layer | Import | What it is | Deps |
|---|---|---|---|
| Ontology | `osco.ontology` | typed entities, fields, relationships, invariants, profiles | none |
| Names | `osco.ontology.names` | validated name constants `T` / `F` / `L` | none |
| Catalog | `osco.catalog` | agent-facing describe / search / graph / context | none |
| Notes | `osco.notes` | note contracts: input field subsets, output entities, note JSON | none |
| Spec | `osco.spec` | the AI-facing `ProblemSpec` and its fingerprint | none |
| Lint | `osco.lint` | cross-entity consistency checks of the whole ontology | none |
| Export | `osco.scripts.export_json_schema` | JSON Schema files per profile, plus the catalog document | none |
| Engine | `osco.prepare` | ontology-driven validation and preparation of input tables | polars (`osco[engine]`) |

## Design

1. **One source of truth per table.** An `Entity` holds schema, semantics,
   relationships, invariants and profile overlays in one typed object; JSON
   Schema and name constants are *derived*, never stored.
2. **Profiles are overlays.** One entity plus thin `ProfileOverride`s
   (requiredness flips, exclusions) serves every profile, so profiles cannot
   drift apart.
3. **Semantics are first-class.** Every field carries a mandatory `meaning`
   (written for LLM grounding), optional `synonyms`, and a physical `unit`.
   `catalog.search("minimum order quantity")` resolves to
   `vendor_contract.moq`. This is what makes the ontology usable as an
   agent's vocabulary, not just a validator's checklist.
4. **Relationships are declared, not discovered.** FK edges are data on the
   entity; the engine derives its integrity checks from them and the catalog
   can render the whole join graph.
5. **Invariants are declared.** Row-level business rules
   (`open_date <= close_date`, `min_stock <= max_stock`) live on the entity
   as SQL expressions; the engine enforces them generically.
6. **No codegen.** `T` / `F` / `L` name constants are derived at import time
   and validate on access with did-you-mean suggestions. There is no file to
   regenerate and forget.
7. **The engine is table-agnostic.** Every preparation step reads only the
   `EntityView`; there is zero table-specific code. Adding a table = adding
   one entity file.
8. **The engine's own reports are ontology entities** (`validation_detail`,
   `validation_summary`).
9. **Determinism is enforced in code**, not by prompt discipline: PK dedupe
   sorts first, report frames have fixed schemas, specs fingerprint
   canonically (`spec.fingerprint()` + data hash = run identity).

## Quickstart

```python
import polars as pl
from osco.ontology.names import T, F, L
from osco.prepare import Preparer
from osco import catalog

# names, validated at access time
T.vendor_contract                  # "vendor_contract"
F.vendor_contract.moq              # "moq"
L.location.location_type.WAREHOUSE # "warehouse"

# agent-facing catalog
catalog.search("minimum order quantity")[0]   # -> vendor_contract.moq
catalog.describe("lane", profile="transportation")
print(catalog.to_context(names=["demand_forecast"]))  # prompt grounding block

# the preparation engine
prepared = Preparer(profile="planning", strict=False).prepare({
    "location": pl.read_csv("location.csv"),
    "product": pl.read_csv("product.csv"),
    "calendar_period": pl.read_csv("calendar.csv"),
    "demand_forecast": pl.read_csv("demand.csv"),
})
clean = prepared.tables["demand_forecast"]
prepared.report.summary_frame()    # validation_summary as a DataFrame
```

The AI-emitted spec:

```python
from osco.spec import ProblemSpec, BlockRef

spec = ProblemSpec(
    problem="supply_plan", profile="planning",
    objective=("maximize_service", "minimize_cost"),
    blocks=(BlockRef("demand_fulfilment", {"backlog": True}),
            BlockRef("vendor_capacity", {"vendor": "V1"})),
    inputs=("demand_forecast", "vendor_contract", "location",
            "product", "calendar_period"),
    outputs=("validation_summary",),
    horizon={"buckets": 13, "granularity": "week"},
    solver={"engine": "gurobi", "mip_gap": 0.005},
)
spec.fingerprint()   # stable content hash — half of a run's identity
```

## Tests

```bash
pip install -e '.[dev]'
pytest tests/    # 34 tests: ontology, names, catalog, notes, spec, engine
```
