Metadata-Version: 2.4
Name: intellign
Version: 0.1.0
Summary: Python SDK and MCP server for the Intellign optimization platform — assignment, scheduling, routing, allocation, and matching problems solved via API
Project-URL: Homepage, https://intellign.ai
Project-URL: Documentation, https://github.com/DataBacked-Africa/intellign-python#readme
Project-URL: Repository, https://github.com/DataBacked-Africa/intellign-python
Project-URL: Issues, https://github.com/DataBacked-Africa/intellign-python/issues
Project-URL: Changelog, https://github.com/DataBacked-Africa/intellign-python/releases
Author-email: DataBacked Africa <hello@databackedafrica.com>
License-Expression: MIT
License-File: LICENSE
Keywords: assignment,genetic-algorithm,llm-tools,mcp,operations-research,optimization,routing,scheduling
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Provides-Extra: dev
Requires-Dist: icalendar>=5.0; extra == 'dev'
Requires-Dist: mcp>=1.2; extra == 'dev'
Requires-Dist: pandas>=2.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Provides-Extra: ical
Requires-Dist: icalendar>=5.0; extra == 'ical'
Provides-Extra: mcp
Requires-Dist: mcp>=1.2; extra == 'mcp'
Provides-Extra: pandas
Requires-Dist: pandas>=2.0; extra == 'pandas'
Description-Content-Type: text/markdown

# intellign

[![PyPI](https://img.shields.io/pypi/v/intellign)](https://pypi.org/project/intellign/)
[![Python](https://img.shields.io/pypi/pyversions/intellign)](https://pypi.org/project/intellign/)
[![CI](https://github.com/DataBacked-Africa/intellign-python/actions/workflows/ci.yml/badge.svg)](https://github.com/DataBacked-Africa/intellign-python/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Python SDK + MCP server for the [Intellign](https://intellign.ai) optimization
platform — describe assignment, scheduling, routing, allocation, or matching
problems and get solved rosters back. All computation runs server-side; this
package is a typed HTTP client plus an MCP wrapper.

**Guides:**
[SDK guide](https://github.com/DataBacked-Africa/intellign-python/blob/main/SDK.md) ·
[REST API reference](https://github.com/DataBacked-Africa/intellign-python/blob/main/API.md) ·
[MCP server](https://github.com/DataBacked-Africa/intellign-python/blob/main/MCP.md) ·
[Deployment](https://github.com/DataBacked-Africa/intellign-python/blob/main/DEPLOYMENT.md) ·
[Examples](https://github.com/DataBacked-Africa/intellign-python/tree/main/examples)

---

## Install

```bash
pip install intellign                  # core SDK (httpx + pydantic)
pip install "intellign[mcp]"           # + intellign-mcp server
pip install "intellign[pandas]"        # + Result.to_dataframe()
pip install "intellign[ical]"          # + Result.export_ical()
```

Python ≥ 3.10. Get an API key from your Intellign dashboard — `ik_live_`
(production) or `ik_test_` (free sandbox: ≤200 entities/targets, capped
solver budget, no quota usage).

## Quickstart

```python
from intellign import Client, Problem

client = Client(
    api_key="ik_live_...",
    base_url="https://api.intellign.ai",   # or your deployment / http://localhost:8000
)

nurses = [
    {"id": "n1", "name": "Ada",  "skills": "triage,general"},
    {"id": "n2", "name": "Bola", "skills": "surgery"},
]
clinics = [
    {"id": "c1", "name": "ER",      "required_skills": "triage",  "capacity": 1},
    {"id": "c2", "name": "Theatre", "required_skills": "surgery", "capacity": 1},
]

problem = (
    Problem.assignment(name="Nurse assignment")
    .entities(rows=nurses)
    .targets(rows=clinics)
    .maximize("skill_match", weight=70,
              entity_column="skills", target_column="required_skills")
    .maximize("workload_balance", weight=30, target_column="capacity")
    .require("entity.skills superset_of target.required_skills")   # hard constraint
    .quality("balanced")                                           # fast | balanced | best
)

result = client.submit(problem).wait()
for a in result.assignments:
    print(a["resource_id"], "->", a["target_id"])

df = result.to_dataframe()              # optional pandas extra
```

Async — identical surface, plus SSE progress streaming:

```python
from intellign import AsyncClient

async with AsyncClient(api_key="ik_live_...") as client:
    job = await client.submit(problem)
    async for event in job.stream_progress():
        print(event.get("current_generation"), event.get("best_fitness"))
    result = await job.result()
```

## What you can do

| Capability | SDK entry point | Example |
|---|---|---|
| Structured solve (builder) | `Problem.assignment()...` → `client.submit()` | `examples/01` |
| Upload data once, reference by id | `client.upload_dataset("team.csv")` → `.entities(dataset_id=...)` | `examples/02` |
| Natural-language solve | `client.solve_nl("assign vans to zones...", ds_a, ds_b)` | `examples/03` |
| Starter templates | `client.templates()` / `client.template(name)` | `examples/04` |
| Live progress (SSE) | `job.stream_progress()` (sync or async) | `examples/05` |
| Webhooks on completion | `client.create_webhook(url)` — HMAC-signed deliveries | `examples/06` |
| Guided conversational flow | `client.create_session()` / `send_message()` / `export_session()` | `examples/07` |
| Robust error handling | typed exceptions, idempotency keys, retries | `examples/08` |
| LLM tool access (MCP) | `intellign-mcp` — stdio + Streamable HTTP | [MCP guide](https://github.com/DataBacked-Africa/intellign-python/blob/main/MCP.md) |

## Objectives & constraints

Objectives are named metrics with weights (live catalog:
`client.capabilities()`): `skill_match`, `distance`, `workload_balance`,
`attribute_match`, `cost`, `preference_match` — each `maximize` or `minimize`.

Constraints use a small formal grammar (invalid expressions are rejected at
create time with the reason):

```
entity.skills superset_of target.required_skills
entity.hours <= target.max_hours          # also >= == < >, or numeric literal
sum(entity.load) <= target.capacity
haversine(entity.location, target.location) <= 25
entity.region in ['north', 'east']
```

`.require(expr)` = hard (violation weight 100); `.require(expr, hard=False)` = soft.

## Jobs & results

`client.submit()` returns a `Job`: `.status()`, `.wait(timeout=300)`,
`.result()`, `.stream_progress()`. Solves are asynchronous server-side —
prefer webhooks over polling for production integrations.

`Result`: `.assignments` (list of dicts with `resource_id`/`target_id`/
`score`/`rationale`), `.metrics`, `.best_fitness`, `.to_dataframe()`,
`.export_ical(path)` for scheduling results with start/end timestamps.

## Errors, retries, idempotency

Every API failure raises a typed subclass of `IntelligError` carrying
`.code`, `.status_code`, and `.request_id` (quote it in support requests):
`AuthenticationError`, `ScopeError`, `InvalidSpecError`, `NotFoundError`,
`ConflictError`, `QuotaError`, `RateLimitError` (`.retry_after`),
`SandboxLimitError`, `SolveFailedError`, `ServerError`.

- The client automatically retries 429/502/503/504 (honoring `Retry-After`).
- Pass `idempotency_key="your-uuid"` to `create_problem`/`solve_problem` —
  retries return the original response instead of duplicating work (24 h window).
- Rate limits per key/minute by plan: free 30, pro 120, enterprise 600.

Full error taxonomy and endpoint reference:
[API.md](https://github.com/DataBacked-Africa/intellign-python/blob/main/API.md).

## Webhooks

```python
hook = client.create_webhook("https://myapp.com/hooks/intellign")
secret = hook["secret"]        # shown exactly once — store it
```

Deliveries are `POST`s with `X-Intellign-Event` and
`X-Intellign-Signature: sha256=<hmac_sha256(secret, raw_body)>`; verify with
a constant-time compare (working receiver: `examples/06_webhooks.py`).
Failed deliveries retry 3× and everything is inspectable via
`client.webhook_deliveries(id)`.

## MCP server

Expose Intellign as tools to Claude or any MCP client:

```bash
pip install "intellign[mcp]"
INTELLIGN_API_KEY=ik_live_... intellign-mcp                          # stdio (local)
intellign-mcp --transport http --port 8001 --stateless               # hosted, multi-tenant
```

Tools: `create_problem`, `solve_problem`, `get_status`, `get_result`,
`list_templates`, `get_template`. Resources: `intellign://schema/spec`,
`intellign://capabilities`. Over HTTP each request authenticates with its
own `Authorization: Bearer ik_...` header.
Setup + hosting: [MCP.md](https://github.com/DataBacked-Africa/intellign-python/blob/main/MCP.md)
and [DEPLOYMENT.md](https://github.com/DataBacked-Africa/intellign-python/blob/main/DEPLOYMENT.md).

## Development

```bash
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest -q                     # unit suite (offline)

# contract tests against a live server:
INTELLIGN_CONTRACT_BASE_URL=http://localhost:8000 \
INTELLIGN_CONTRACT_API_KEY=ik_... .venv/bin/python -m pytest -m contract -q

.venv/bin/python -m build                         # wheel + sdist into dist/
```

Layout: `src/intellign/` (client, spec models, builders, job/result, errors,
`mcp/server.py`), `tests/` (unit, respx-mocked), `tests/contract/`
(env-gated, live server), `examples/` (runnable per capability).

## Releasing

CI (`.github/workflows/ci.yml`) tests every push/PR on Python 3.10–3.12 and
builds artifacts. Publishing (`publish.yml`) uses PyPI Trusted Publishing:

1. Bump `src/intellign/_version.py`.
2. Commit, tag `vX.Y.Z`, push the tag.
3. Create a GitHub Release from the tag → workflow builds, verifies the tag
   matches the package version, and publishes to PyPI via OIDC (no token
   secrets). One-time setup: add this repo as a trusted publisher on
   pypi.org (workflow `publish.yml`, environment `pypi`) and create the
   `pypi` environment in repo settings.

## License

MIT
