Metadata-Version: 2.4
Name: crackedai-connect
Version: 0.3.0
Summary: Local runtime for crackedaicode.com — run ML code on your own machine
License-Expression: MIT
Project-URL: Homepage, https://crackedaicode.com
Project-URL: Repository, https://github.com/anupa/cracked-ai-connect
Keywords: machine-learning,pytorch,jax,coding-practice
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: flask
Requires-Dist: flask-cors
Dynamic: license-file

# crackedai-connect

Local runtime for [crackedaicode.com](https://crackedaicode.com) — execute ML code on your own machine using your own GPU.

The website (Elixir/Phoenix, separate repo) ships your code and test cases to this runtime over `localhost`. Code never round-trips through a server.

---

## Table of contents

- [Install & run](#install--run)
- [What it does](#what-it-does)
- [How it works](#how-it-works)
- [Already have PyTorch / JAX?](#already-have-pytorch--jax)
- [HTTP API](#http-api)
- [Tensor wire format](#tensor-wire-format)
- [Configuration](#configuration)
- [For contributors](#for-contributors)
- [Releasing](#releasing)
- [License](#license)

---

## Install & run

```bash
pip install crackedai-connect
crackedai-connect
```

That's it. On first run the CLI inspects your hardware and installs the right PyTorch + JAX:

- **NVIDIA GPU** — PyTorch + JAX with CUDA support. The runtime parses `nvidia-smi`, picks the highest PyTorch wheel tag that doesn't exceed your driver's CUDA version (`cu118`, `cu121`, `cu124`, or `cu126`), and installs the matching `jax[cudaN]` extra.
- **Apple Silicon** — PyTorch with MPS + the default JAX wheel.
- **Everything else** — CPU-only PyTorch (smaller download) + the default JAX wheel.

Setup state is recorded in `~/.crackedai/setup_done`. Subsequent runs skip the install step. If the marker exists but `torch` or `jax` can't be imported (e.g. you uninstalled them), the CLI re-runs setup.

```
$ crackedai-connect

  CrackedAI Runtime v0.2.2

  Setting up ML frameworks for your hardware...
  Found NVIDIA GPU (CUDA 12.4)
  Installing PyTorch (CUDA 12.4, cu124)... done
  Installing JAX (CUDA 12)... done
  Setup complete!

  Detected: NVIDIA RTX 4090 | Linux
    numpy 1.26.4 ✓
    pytorch 2.3.0 ✓ (CUDA accelerated)
    jax 0.4.30 ✓ (cuda)

  Listening on http://127.0.0.1:52341
  Open crackedaicode.com to start solving problems.
  Press Ctrl+C to stop.
```

Then open <https://crackedaicode.com> and start solving problems. The site auto-detects the runtime via a 2-second health check on the problem-solving page.

---

## What it does

When you click Run or Submit on crackedaicode.com, your code executes **locally on your machine**:

- **Your own GPU** — CUDA, Apple Silicon MPS, or CPU
- **Your own packages** — whatever `pip` (or your venv / conda / pixi) gave you
- **Zero server round-trip** — `localhost` only
- **Code never leaves your machine** — only pass/fail + benchmark numbers go back to the website

The runtime is small (Flask + numpy + flask-cors) and runs only while you keep the terminal open. Stop it with Ctrl+C.

---

## How it works

```
Browser (crackedaicode.com)                  localhost:52341 (this runtime)
        │                                                 │
        │   GET /health                                   │
        │ ────────────────────────────────────────────►   │
        │                                                 │
        │   { hardware, frameworks, python, version }     │
        │ ◄────────────────────────────────────────────   │
        │                                                 │
        │   POST /execute                                 │
        │   { code, framework, function_name,             │
        │     test_cases, timeout_ms,                     │
        │     reference_code? }                           │
        │ ────────────────────────────────────────────►   │
        │                                                 │
        │                                       ┌─────────▼──────────┐
        │                                       │ Python subprocess  │
        │                                       │ (your code,        │
        │                                       │  isolated tempdir) │
        │                                       └─────────┬──────────┘
        │                                                 │
        │   { status, test_results[], benchmark? }        │
        │ ◄────────────────────────────────────────────   │
```

Each `/execute` request runs your code in a **fresh Python subprocess** in a temporary directory. The harness script, your solution, and (when benchmarking) the reference solution are written into that tempdir, then `python run_tests.py framework function_name '[test_cases_json]'` is invoked with `subprocess.run(..., timeout=timeout_sec)`.

If the subprocess crashes, segfaults, hangs, or pollutes its environment, only that subprocess dies. The Flask server keeps serving.

For the deeper internals, see [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).

---

## Already have PyTorch / JAX?

If `torch` and `jax` are already importable when you start `crackedai-connect`, the setup step is skipped (the marker file `~/.crackedai/setup_done` is created so we don't re-check on every launch).

To force a reinstall:

```bash
rm ~/.crackedai/setup_done
crackedai-connect
```

The runtime executes whatever it can `import` — so if you want to test against a specific PyTorch build, install it in your environment before running and it will be picked up.

---

## HTTP API

CORS is locked to `https://crackedaicode.com` and `http://localhost:4000` (the website's dev server). Other origins will be rejected by the browser even though the requests would work over plain HTTP.

### `GET /health`

Returns the cached environment info that was detected at startup.

```json
{
  "status": "ok",
  "version": "0.2.2",
  "hardware": {
    "platform": "Darwin",
    "processor": "arm",
    "device": "mps",
    "device_name": "Apple arm64"
  },
  "frameworks": {
    "numpy":   { "version": "1.26.4", "available": true },
    "pytorch": { "version": "2.3.0",  "available": true, "device": "mps" },
    "jax":     { "version": "0.4.30", "available": true, "backend": "METAL" }
  },
  "python": "3.12.4"
}
```

### `POST /execute`

Required fields:

| Field | Type | Notes |
|---|---|---|
| `code` | string | The user's solution. Must define `function_name`. |
| `framework` | `"pytorch"` \| `"jax"` | Selects the tensor library used for input/output deserialization. |
| `function_name` | string | Function in `code` to call. |
| `test_cases` | array | See below. |

Optional:

| Field | Type | Default | Notes |
|---|---|---|---|
| `timeout_ms` | int | `30000` | Per-execution timeout for the subprocess. |
| `reference_code` | string | none | If provided **and** all tests pass, runs a benchmark against this reference solution. |

A `test_case` looks like:

```json
{
  "id": "case_1",
  "inputs": { "x": { "shape": [3], "values": [1.0, 2.0, 3.0] } },
  "expected": { "shape": [3], "values": [0.09, 0.24, 0.66] },
  "tolerance": 0.0001
}
```

`tolerance` defaults to `1e-5` if omitted. Inputs are passed as keyword arguments to `function_name(**inputs)`.

#### Response

```json
{
  "status": "passed" | "failed" | "error",
  "test_results": [
    {
      "id": "case_1",
      "passed": true,
      "actual": { "shape": [3], "values": [...] },
      "error": "",
      "runtime_us": 1234.5
    }
  ],
  "stdout": "",
  "stderr": "",
  "runtime_us": 4567.8,
  "benchmark": {
    "user_median_ms": 12.34,
    "reference_median_ms": 10.0,
    "ratio": 1.234
  }
}
```

`benchmark` is present only on a passing run when `reference_code` was supplied. The runtime does **1 warmup + 3 timed runs** for both the user and reference solutions and takes the median; `ratio = user_median / reference_median`. Lower is better — the website turns this into a percentile against other solvers.

`status` is `"error"` when the subprocess crashed, timed out, or returned non-JSON.

Errors are returned with HTTP 200 and `status: "error"` in the body. The only HTTP error you'll see is `400` for missing required fields.

---

## Tensor wire format

The runtime and website agree on a tiny convention for serializing tensors over JSON:

- A scalar (`int`, `float`, `bool`) passes through as-is.
- A tensor / numpy array becomes `{ "shape": [...], "values": [...] }`. `values` is the row-major flattened list at `dtype=float32`.

The harness deserializes inputs into framework-native tensors:
- `pytorch` → `torch.tensor(np.array(values, dtype=float32).reshape(shape))`
- `jax` → `jnp.array(...)` of the same.

It serializes the function's return value the same way. Comparison is `np.allclose(actual, expected, atol=tolerance)` for tensors, `abs(a - b) < tolerance` for scalars, `==` for everything else.

This means: your function can return a Python int/float/bool *or* a tensor; non-tensor non-primitive returns will round-trip through `==`. If you need dict/list support, extend `serialize_value` / `deserialize_value` in `harness.py`.

---

## Configuration

There isn't much.

| Where | Purpose |
|---|---|
| `~/.crackedai/setup_done` | Marker file. Delete to force framework reinstall. |
| Port `52341` | Hardcoded in `cli.py` and `local_runtime.js` on the website. Don't change one without changing the other. |
| CORS origins | Hardcoded in `server.py`: `https://crackedaicode.com`, `http://localhost:4000`. |
| `KMP_DUPLICATE_LIB_OK=TRUE` | Set in `pixi.toml` activation env to dodge the OpenMP duplicate-library crash on macOS when both PyTorch and JAX get imported. |

---

## For contributors

Two supported environments.

### `pixi` (preferred, fully-pinned)

```bash
git clone https://github.com/anupa/cracked-ai-connect.git
cd cracked-ai-connect
pixi install                # solves env, installs python+flask+pytorch+jax
pixi run test               # pytest tests/ -v
pixi run connect            # python -m crackedai_connect.cli
```

`pixi.toml` defines two environments:
- `default` — CPU PyTorch + plain JAX (works on every platform)
- `cuda` — CUDA-enabled JAX (Linux/Windows only). Activate with `pixi shell -e cuda`.

### `pip` (lighter, what end users get)

```bash
pip install -e .            # install in editable mode
just test                   # python -m pytest tests/ -v
just run                    # crackedai-connect
```

This mirrors what end users do, except for the editable install. Note that `pip install -e .` installs only the runtime deps (`numpy`, `flask`, `flask-cors`) — for the test suite to actually exercise PyTorch/JAX paths you need `torch` + `jax` in the env too (run `crackedai-connect` once to auto-install them, or `pip install torch jax`).

### Layout

```
cracked-ai-connect/
├── src/crackedai_connect/
│   ├── __init__.py        # __version__ — kept in sync with pyproject.toml
│   ├── __main__.py        # `python -m crackedai_connect`
│   ├── cli.py             # Entry point: setup → detect → start Flask
│   ├── setup.py           # First-run installer (CUDA detection, PyTorch/JAX install)
│   ├── detect.py          # Hardware + framework detection (cached)
│   ├── server.py          # Flask app: /health, /execute (with CORS)
│   ├── executor.py        # Subprocess orchestration + benchmark loop
│   └── harness.py         # Run inside the subprocess. Imports user code, runs tests.
├── tests/                 # pytest
├── docs/
│   ├── superpowers/       # Historical design specs + plans
│   └── ARCHITECTURE.md    # Deep-dive on the internals
├── justfile               # task runner (`just test`, `just bump`, `just release`)
├── pixi.toml / pixi.lock  # pixi environment
├── pyproject.toml         # PyPI metadata + setuptools config
└── README.md              # this file
```

### Tests

```bash
pixi run test
# or
python -m pytest tests/ -v
```

Tests live in `tests/`:
- `test_detect.py` — environment introspection
- `test_setup.py` — first-run install logic
- `test_server.py` — Flask routes (uses `app.test_client()`)
- `test_executor.py` — subprocess orchestration (timeout, crash, success)
- `test_harness.py` — the in-subprocess harness directly

---

## Releasing

The `release` GitHub Action publishes to PyPI on any push to `main` whose `pyproject.toml` `version` field changed.

The `justfile` automates the bump:

```bash
just bump 0.2.3      # rewrites version in both pyproject.toml and __init__.py, commits
git push             # CI publishes to PyPI + creates a GitHub Release
```

Or do it all in one shot from local (skips CI, requires `~/.pypirc` or `TWINE_PASSWORD`):

```bash
just release 0.2.3   # bump + build + twine upload
```

Always update both `pyproject.toml` and `src/crackedai_connect/__init__.py`. The `bump` recipe does this in lockstep.

---

## License

MIT — see [`LICENSE`](LICENSE).
