> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Recipe Serve (Python)

> Programmatic server configuration and management using Python

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(name="code-recipe-agent", instructions="Execute code-based recipes.")
agent.start("Run the code-generation recipe to scaffold a new FastAPI project.")
```

The user hits the recipe server over HTTP; Python `serve()` config controls auth, CORS, and ports.

Expose recipe workflows over HTTP — configure auth, CORS, and deployment from Python or YAML.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import serve

serve(host="127.0.0.1", port=8765)
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Recipe Serve"
        Config["⚙️ serve.yaml"] --> Server["🖥️ Recipe Server"]
        Code["🐍 Python"] --> Server
        Server --> HTTP["🌐 HTTP"]
        HTTP --> Client["📱 Client"]
        Server --> Auth["🔐 Auth"]
    end

    classDef config fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef server fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef protocol fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef client fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef auth fill:#10B981,stroke:#7C90A0,color:#fff

    class Config,Code config
    class Server server
    class HTTP protocol
    class Client client
    class Auth auth
```

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant RecipeServePython

    User->>Agent: Request
    Agent->>RecipeServePython: Process
    RecipeServePython-->>Agent: Result
    Agent-->>User: Response
```

## Quick Start

<Steps>
  <Step title="Simple Usage">
    Start a local recipe server:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonai.recipe.serve import serve

    serve(host="127.0.0.1", port=8765)
    ```

    Or via CLI:

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai recipe serve --host 127.0.0.1 --port 8765
    ```
  </Step>

  <Step title="With Configuration">
    Load settings from `serve.yaml` and override auth:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import os
    from praisonai.recipe.serve import serve, load_config

    config = load_config("serve.yaml")
    config["auth"] = "api-key"
    config["api_key"] = os.environ["PRAISONAI_API_KEY"]

    serve(
        host=config.get("host", "127.0.0.1"),
        port=config.get("port", 8765),
        config=config,
    )
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant Feature as Recipe Serve (Python)

    User->>Agent: Request
    Agent->>Feature: Process request
    Feature-->>Agent: Result
    Agent-->>User: Response
```

`create_app()` builds a Starlette ASGI application; `serve()` runs it with uvicorn. Configuration merges file, environment variables, and Python overrides — CLI flags take highest precedence.

| Route                | Method | Description       |
| -------------------- | ------ | ----------------- |
| `/health`            | GET    | Health check      |
| `/v1/recipes`        | GET    | List recipes      |
| `/v1/recipes/{name}` | GET    | Describe recipe   |
| `/v1/recipes/run`    | POST   | Run recipe (sync) |
| `/v1/recipes/stream` | POST   | Run recipe (SSE)  |

***

## Configuration Options

| Key            | Type   | Default       | Description                          |
| -------------- | ------ | ------------- | ------------------------------------ |
| `host`         | `str`  | `"127.0.0.1"` | Bind address                         |
| `port`         | `int`  | `8765`        | Listen port                          |
| `auth`         | `str`  | `"none"`      | Auth mode (`none`, `api-key`, `jwt`) |
| `api_key`      | `str`  | env           | API key when `auth=api-key`          |
| `recipes`      | `list` | all           | Recipe names to expose               |
| `preload`      | `bool` | `false`       | Warm recipes on startup              |
| `cors_origins` | `str`  | `"*"`         | Allowed CORS origins                 |
| `log_level`    | `str`  | `"info"`      | Server log level                     |

### serve.yaml example

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
host: 127.0.0.1
port: 8765
auth: api-key
recipes:
  - support-reply-drafter
  - meeting-action-items
preload: true
cors_origins: "https://app.example.com"
```

Set `PRAISONAI_API_KEY` in the environment instead of hardcoding keys.

***

## Common Patterns

### ASGI app for custom deployment

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import create_app
import uvicorn

app = create_app(config={"auth": "none", "cors_origins": "*"})
uvicorn.run(app, host="0.0.0.0", port=8765)
```

### Unified agents.yaml config

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
serve:
  host: 127.0.0.1
  port: 8765
  auth: api-key
  preload: true
```

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.recipe.serve import load_config, serve

config = load_config("agents.yaml").get("serve", {})
serve(host=config.get("host", "127.0.0.1"), port=config.get("port", 8765), config=config)
```

### Test with TestClient

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import os
from starlette.testclient import TestClient
from praisonai.recipe.serve import create_app

app = create_app(config={"auth": "api-key", "api_key": "test-key"})
client = TestClient(app)

assert client.get("/health").json()["status"] == "healthy"
assert client.get("/v1/recipes", headers={"X-API-Key": "test-key"}).status_code == 200
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Always use auth in production">
    Set `auth: api-key` and load the key from `PRAISONAI_API_KEY`. Never expose an unauthenticated recipe server on a public network.
  </Accordion>

  <Accordion title="Preload recipes for faster startup">
    Set `preload: true` to warm recipes at startup and eliminate cold-start latency on the first request.
  </Accordion>

  <Accordion title="Restrict CORS origins">
    Set `cors_origins` to your exact frontend domain in production rather than `"*"`.
  </Accordion>

  <Accordion title="Poll /health for orchestration">
    Use the `/health` endpoint in Docker, Kubernetes, and load-balancer health checks.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Recipe Serve Advanced" icon="gauge-high" href="/docs/features/recipe-serve-advanced">
    Rate limiting, metrics, admin reload, and OpenTelemetry
  </Card>

  <Card title="Endpoints Code" icon="code" href="/docs/features/endpoints-code">
    Client-side code for calling recipe endpoints
  </Card>
</CardGroup>
