# Decorator Pattern

Flow SDK provides decorators for remote GPU execution, similar to serverless frameworks.

## Basic Usage

```python
from flow import FlowApp

app = FlowApp()

@app.function(gpu="a100")
def train_model(data_path: str, epochs: int = 100):
    import torch
    model = torch.nn.Linear(10, 1)
    # Training logic
    return {"accuracy": 0.95, "loss": 0.01}

# Remote execution
result = train_model.remote("s3://bucket/data.csv", epochs=50)

# Local execution
local_result = train_model("./local_data.csv")
```

## Resource Configuration

```python
# Single GPU
@app.function(gpu="a100")
def single_gpu_task():
    pass

# Multi-GPU
@app.function(gpu="h100:8")
def multi_gpu_task():
    pass

# With memory and image
@app.function(
    gpu="a100",
    memory=131072,  # 128GB
    image="pytorch/pytorch:2.1.0-cuda12.1-cudnn8"
)
def memory_intensive_task():
    pass

# Full configuration
@app.function(
    gpu="h100:8",
    image="nvcr.io/nvidia/pytorch:23.10-py3",
    volumes={
        "/data": "training-data",
        "/models": {"name": "model-cache", "size_gb": 500}
    },
    env={"WANDB_API_KEY": "..."}
)
def distributed_training(config_path: str) -> dict:
    return {"final_loss": 0.023}
```

## Asynchronous Execution

```python
@app.function(gpu="a100")
def process_batch(batch_id: int):
    return {"processed": batch_id}

# Launch without waiting
task_id = process_batch.spawn(42)

# Launch multiple
task_ids = [process_batch.spawn(i) for i in range(10)]

# Check status
from flow import status
print(status(task_id))
```

## Module-Level Usage

```python
from flow import function

# Direct decorator
@function(gpu="a100")
def inference(text: str) -> dict:
    return {"sentiment": "positive", "confidence": 0.95}

result = inference.remote("Hello world!")

# Global app
from flow import app

@app.function(gpu="h100")
def global_task():
    return "success"
```

## Volume Management

```python
@app.function(
    gpu="a100",
    volumes={
        "/checkpoints": "model-checkpoints",
        "/datasets": {"size_gb": 1000},
        "/cache": {"name": "cache", "size_gb": 100}
    }
)
def training_with_storage():
    pass
```

## Error Handling

```python
# Non-serializable arguments fail with helpful errors
@app.function(gpu="a100")
def process_array(arr):
    return arr.mean()

# Correct approach
@app.function(gpu="a100")
def process_array_file(path: str) -> float:
    import numpy as np
    arr = np.load(path)
    return float(arr.mean())

# Save data first
np.save('/tmp/data.npy', arr)
result = process_array_file.remote('/tmp/data.npy')
```

## Best Practices

1. **Type hints**: Use for IDE support and documentation
2. **Serializable args**: Stick to JSON-serializable types
3. **Import isolation**: Import GPU libraries inside functions
4. **Resource specification**: Always specify GPU requirements

```python
# Good
@app.function(gpu="a100", image="pytorch/pytorch:2.1.0")
def train(config: dict, epochs: int) -> dict[str, float]:
    import torch  # Import inside
    return {"loss": 0.1}

# Bad
import torch  # May not exist locally

@app.function()  # No GPU specified
def train(model_object, dataset_iterator):  # Non-serializable
    pass
```

## Pattern Comparison

| Feature | Decorator | invoke() | run() |
|---------|-----------|----------|-------|
| Code modification | Required | None | None |
| Local testing | Native | Manual | N/A |
| Type safety | Full | Runtime | N/A |
| IDE support | Excellent | Limited | N/A |
| Best for | New projects | Existing code | Scripts |

## Migration Examples

### From Modal

```python
# Modal
import modal
app = modal.App()

@app.function(gpu="A100")
def train():
    return "done"

# Flow SDK (identical)
from flow import FlowApp
app = FlowApp()

@app.function(gpu="a100")
def train():
    return "done"
```

### From AWS Lambda

```python
# Chalice
from chalice import Chalice
app = Chalice(app_name='ml-app')

@app.lambda_function()
def train(event, context):
    return {"result": "done"}

# Flow SDK
from flow import FlowApp
app = FlowApp()

@app.function(gpu="a100")
def train(event: dict):
    return {"result": "done"}
```