Metadata-Version: 2.4
Name: vincta
Version: 0.1.2
Summary: Auto-discover and serve Python functions over HTTP with a built-in RAM object store
License-Expression: MIT
Keywords: fastapi,functions,orchestration,http,object-store,autodiscovery,api
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Framework :: FastAPI
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: fastapi>=0.111.0
Requires-Dist: uvicorn[standard]>=0.29.0
Requires-Dist: pydantic>=2.0.0
Provides-Extra: pandas
Requires-Dist: pandas>=2.0.0; extra == "pandas"
Provides-Extra: numpy
Requires-Dist: numpy>=1.24.0; extra == "numpy"
Provides-Extra: all
Requires-Dist: pandas>=2.0.0; extra == "all"
Requires-Dist: numpy>=1.24.0; extra == "all"

# vincta

Auto-discover and serve Python functions over HTTP with a built-in RAM object store.

Drop your functions in a folder, point vincta at it, and every function becomes a callable API endpoint. Heavy objects (DataFrames, arrays) are stored by reference so you never serialise large data over HTTP.

## Install

```bash
pip install vincta
```

## Quickstart

### 1. CLI — zero config

```bash
vincta --dir ./my_functions --port 8000
```

Any `.py` file in `my_functions/` is auto-discovered. All public functions are registered and served instantly.

### 2. Call a function

```bash
curl -X POST http://localhost:8000/function/run \
  -H "Content-Type: application/json" \
  -d '{"function": "my_function", "inputs": {"x": 42}}'
```

### 3. Browse the API

Visit `http://localhost:8000/docs` for the auto-generated Swagger UI.

---

## How it works

### Object store

Heavy outputs (DataFrames, numpy arrays, dicts/lists > 100 items) are stored in RAM and returned as a lightweight ref string (`obj_abc123`). Pass the ref as input to the next function — vincta resolves it automatically.

```
POST /function/run  {"function": "load_csv", "inputs": {"path": "data.csv"}}
-> {"df_ref": "obj_abc123"}

POST /function/run  {"function": "compute_returns", "inputs": {"df_ref": "obj_abc123"}}
-> {"result_ref": "obj_def456"}
```

Primitives (`int`, `float`, `str`, `bool`, small dicts) are returned inline, never stored.

### Auto-discovery

vincta imports every `.py` file in your `--dir` folders and registers all public functions with schemas inferred from type hints.

---

## Exposing your own functions

### Option A — plain functions (auto-registered)

```python
# my_functions/transforms.py
import pandas as pd

def compute_returns(df: pd.DataFrame) -> pd.DataFrame:
    """Compute pct change for all numeric columns."""
    return df.select_dtypes("number").pct_change().dropna()
```

```bash
vincta --dir ./my_functions
```

### Option B — decorator for explicit schema

Use `@register_function` when inputs are object refs (`str`) but you want the API docs to be meaningful:

```python
from vincta import register_function
import pandas as pd

@register_function(
    name="clean_data",
    inputs={"df_ref": "str", "threshold": "float"},
    outputs={"clean_ref": "str"},
    description="Drop rows with nulls above threshold.",
)
def clean_data(df: pd.DataFrame, threshold: float) -> pd.DataFrame:
    return df.dropna(thresh=int(len(df.columns) * threshold))
```

### Option C — flat-import codebases

If your codebase uses flat imports (`from indicators import ...` instead of `from mypackage.indicators import ...`), add `--sys-path` so Python can resolve them:

```bash
vincta --dir ./MyCodebase --sys-path ./MyCodebase --port 8000
```

---

## Embedding in your own FastAPI app

```python
from fastapi import FastAPI
from vincta.routers import execution, functions, objects

app = FastAPI()
app.include_router(functions.router)
app.include_router(execution.router)
app.include_router(objects.router)
```

## Programmatic registration

```python
from vincta import register, discover_and_import
from pathlib import Path

# Register a single function
register(my_fn, name="my.function", inputs={"x": "float"}, outputs={"y": "float"})

# Run discovery on a directory
discover_and_import([Path("./my_functions")])
```

## Object store API

```python
from vincta import store_object, retrieve_object, delete_object, list_refs

ref = store_object(my_dataframe)   # -> "obj_abc123"
df  = retrieve_object(ref)
delete_object(ref)
print(list_refs())
```

---

## API reference

| Method | Path | Description |
|--------|------|-------------|
| GET | `/functions` | List all registered functions with schemas |
| POST | `/function/run` | Execute a function by name |
| GET | `/object/{ref}` | Preview a stored object (shape, columns, head) |
| DELETE | `/object/{ref}` | Free an object from RAM |
| GET | `/object/list` | All current object refs |
| GET | `/object/memory` | RAM usage breakdown |
| GET | `/health` | Function count + object count |

## CLI reference

```
vincta --dir DIR [--dir DIR ...]
       [--sys-path PATH ...]
       [--host HOST]
       [--port PORT]
       [--exclude PATTERN]
       [--reload]
       [--workers N]
```

| Flag | Default | Description |
|------|---------|-------------|
| `--dir` | — | Directory to discover functions from (repeatable) |
| `--sys-path` | — | Extra path prepended to sys.path before discovery (repeatable) |
| `--host` | `127.0.0.1` | Bind host |
| `--port` | `8000` | Bind port |
| `--exclude` | `test_*,*.test.py` | Glob patterns to exclude |
| `--reload` | off | Enable auto-reload (development) |
| `--workers` | `1` | Worker processes (ignored with --reload) |

## Notes

- The object store is **in-process RAM only** — refs are lost on server restart.
- Functions are registered by name. Duplicate names overwrite silently.
- `python -m vincta` works as an alternative to the `vincta` CLI command.
