Metadata-Version: 2.4
Name: brainy_deco
Version: 0.1.0
Summary: A decorator that turns Python functions into LLM calls
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest-asyncio; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# smart_function

A Python decorator that converts any function into an LLM call — powered by its **docstring** and **type signature**.

## How it works

```
@smart_func("gemma4")
def translate(text: str, target_language: str) -> str:
    """Translate the given text into the target language."""
```

When you call `translate("Hello!", "French")`, the decorator:

1. **Inspects** the function signature and docstring
2. **Assembles** a structured prompt describing the task and actual arguments
3. **Calls** the configured LLM backend
4. **Parses** the reply, expecting a `_llm_return = <value>` assignment
5. **Returns** the evaluated Python literal

## Requirements

- Python ≥ 3.10
- [httpx](https://www.python-httpx.org/) (`pip install httpx`)
- A running [Ollama](https://ollama.com/) instance (default) or an OpenAI-compatible API

## Installation

```bash
pip install -e .          # editable install
# or
uv pip install -e .
```

## Quick start

### Ollama (default, local)

```python
from smart_function import smart_func

@smart_func("gemma4")          # model name is the only required argument
def translate(text: str, target_language: str) -> str:
    """Translate the given text into the target language."""

print(translate("Hello, world!", "French"))
# → 'Bonjour, le monde !'
```

### Sentiment analysis (returns a float)

```python
@smart_func("gemma4")
def sentiment_score(text: str) -> float:
    """
    Analyse the sentiment of the text.
    Return a float between -1.0 (very negative) and 1.0 (very positive).
    """

print(sentiment_score("This is amazing!"))   # → 0.95
```

### Structured extraction (returns a dict)

```python
@smart_func("gemma4")
def extract_person(bio: str) -> dict:
    """
    Extract personal information from a biography.
    Return a dict with keys: name, age, occupation. Use None for unknown fields.
    """

print(extract_person("Marie Curie was a 66-year-old physicist."))
# → {'name': 'Marie Curie', 'age': 66, 'occupation': 'physicist'}
```

### OpenAI-compatible endpoint

```python
@smart_func(
    "gpt-4o-mini",
    backend_type="openai",
    api_key="sk-...",
    base_url="https://api.openai.com/v1",   # optional, this is the default
)
def summarize(article: str, max_words: int = 100) -> str:
    """Summarize the article in at most max_words words."""
```

### Custom backend instance

```python
from smart_function import smart_func
from smart_function.backends import OpenAIBackend

backend = OpenAIBackend(
    model="gemma4",
    base_url="http://localhost:1234/v1",   # LM Studio, etc.
    temperature=0.2,
)

@smart_func(backend=backend)
def classify(text: str, categories: list) -> str:
    """Classify the text into one of the given categories."""
```

## Decorator parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `model` | `str` | `"llama3"` | LLM model name |
| `backend` | `LLMBackend` | `None` | Pre-built backend (overrides all other connection params) |
| `backend_type` | `str` | `"ollama"` | `"ollama"` or `"openai"` |
| `base_url` | `str` | `"http://localhost:11434"` | API base URL |
| `api_key` | `str` | `None` | API key (OpenAI-compatible) |
| `debug` | `bool` | `False` | Log prompt and raw response |
| `**backend_kwargs` | | | Forwarded to backend (e.g. `temperature`, `timeout`) |

## Return value protocol

The LLM is instructed to produce **only** one line:

```
_llm_return = <Python literal>
```

The value is extracted with `ast.literal_eval` (safe, no arbitrary code execution).
Supported types: `str`, `int`, `float`, `bool`, `None`, `list`, `dict`, `tuple`.

## Running the demo

```bash
# make sure Ollama is running and gemma4 is pulled
ollama pull gemma4
uv run examples/demo.py
```

## Running tests

```bash
uv run pytest tests/ -v
```

## Project layout

```
smart_function/
├── smart_function/
│   ├── __init__.py      # public API
│   ├── decorator.py     # @smart_func decorator
│   ├── prompt.py        # prompt builder
│   ├── parser.py        # response parser
│   └── backends.py      # OllamaBackend, OpenAIBackend
├── examples/
│   └── demo.py
├── tests/
│   └── test_smart_function.py
└── pyproject.toml
```