Metadata-Version: 2.4
Name: osco
Version: 0.1.0
Summary: OptimNet supply chain ontology + data-preparation engine. AI-native successor to a legacy schema dictionary and its data-preparation engine, unified in one distribution.
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

AI-native successor to a legacy pair of supply-chain packages — a schema
dictionary and a data-preparation engine ("the architect") —
unified in one distribution with a clean layer boundary. Notes and packages
stay: 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 | Replaces | Deps |
|---|---|---|---|
| Ontology | `osco.ontology` | legacy schema dicts + `create_schema_files.py` | none |
| Names | `osco.ontology.names` | generated `tables` module, architect `Names.*` | none |
| Catalog | `osco.catalog` | Excel/Sheets schema docs | none |
| Notes | `osco.notes` | `note_schemas/` (input configs, output schemas, resolver, note JSON) | none |
| Spec | `osco.spec` | (new) the note's AI-facing contract | none |
| Lint | `osco.lint` | the cross-schema half of `lint_schemas.py` (the per-file half is now unrepresentable) | none |
| Export | `osco.scripts.export_json_schema` | `create_schema_files.py` → the LakeFS `json_schema/` artifact | none |
| Engine | `osco.prepare` | architect `DataPreparer` + validators | polars (`osco[engine]`) |
| Migration | `osco.migrate.from_legacy` | (new) mechanical legacy-schema port; `--merged` emits one entity + profile overlays per table | none |

## What changed vs the legacy packages, and why

1. **One source of truth per table.** The legacy package spread a table across
   `TABLE_METADATA` + `FIELD_METADATA` + four per-domain dicts + generated
   generated `tables` module + separate `note_json_schema/`. An OptimNet `Entity` holds
   schema, semantics, relationships, invariants and profile overlays in one
   typed object; JSON Schema and name constants are *derived*, never stored.
2. **Domains became profiles.** Instead of four diverging schema copies, one
   entity plus thin `ProfileOverride`s (requiredness flips, exclusions).
   The copies can no longer 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`) — no second schema system for internal tables.
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 engine (successor to DataPreparer)
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
```

## Porting the rest of the legacy schema

Fourteen entities are hand-modeled as the seed set. The remaining tables port
mechanically:

```bash
# one merged entity per table, all domains folded into profile overlays
python -m osco.migrate.from_legacy <legacy_repo>/schema out/ --merged

# or per-domain drafts
python -m osco.migrate.from_legacy <legacy_repo>/schema out/ planning
```

Verified against the current legacy repo: `--merged` migrates **all 182 tables**
into valid single entities (48 with real per-domain overrides), and the
per-domain mode migrates planning 77 / network 82 / transportation 68 /
simulation 66 — zero validation failures in either mode. Generated files carry
`TODO(enrich)` markers where human/agent enrichment (meanings, units, synonyms,
relationships, invariants) adds the AI-native value, and `TODO(conflict)`
markers where the legacy domains disagreed on a field's type.

## Tests

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