Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Install Genesis using `pip`:
pip install genesis
```

For better asyncio performance on Unix (Linux and macOS), use the optional uvloop extra: `pip install genesis[uvloop]`. See the [Installation Guide](https://otoru.github.io/Genesis/docs/installation/) for details.

## Quickstart

### Inbound Socket Mode
Expand Down
4 changes: 4 additions & 0 deletions docker/freeswitch/conf/autoload_configs/local_stream.conf.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<configuration name="local_stream.conf" description="Local Stream">
<!-- Music on Hold: stream "moh" → sounds/music/8000 (see vars.xml hold_music) -->
<directory name="moh" path="$${sounds_dir}/music/8000"/>
</configuration>
27 changes: 27 additions & 0 deletions docs/content/docs/Examples/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,37 @@ docker-compose down

For more details about the Docker setup, see the [docker/freeswitch/README.md](https://github.com/Otoru/Genesis/blob/main/docker/freeswitch/README.md) file.

## Testing with a SIP Client

You can test the examples using a SIP client (e.g. Linphone, Zoiper, or X-Lite):

1. Configure your SIP client to connect to FreeSWITCH:
- **Server:** `127.0.0.1:5060`
- **Username:** `1000` or `1001`
- **Password:** `1000` or `1001` (same as username)
- **Domain:** `127.0.0.1`

2. Register the SIP client.

## Dialplan Configuration

The Docker environment includes a dialplan entry that routes calls to `9999` to the outbound socket:

```xml
<extension name="outbound_socket_test">
<condition field="destination_number" expression="^(9999)$">
<action application="socket" data="127.0.0.1:9696 async full"/>
</condition>
</extension>
```

Calls to `9999` trigger FreeSWITCH to connect to your application at `127.0.0.1:9696`.

## Available Examples

{{< cards cols="1" >}}
{{< card link="fastapi-click2call/" title="Click2Call API" icon="code" subtitle="REST API endpoint for click2call functionality using FastAPI." >}}
{{< card link="ivr/" title="IVR" icon="phone" subtitle="Simple IVR system using Outbound mode with DTMF interaction." >}}
{{< card link="group-call/" title="Group Call" icon="users" subtitle="Simultaneous originate that calls multiple destinations and bridges with the first to answer." >}}
{{< card link="queue/" title="Queue" icon="view-list" subtitle="Outbound with a queue: one call at a time; others wait in line (FIFO)." >}}
{{< /cards >}}
10 changes: 5 additions & 5 deletions docs/content/docs/Examples/fastapi-click2call.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,20 @@ The example uses a **per-request connection**, opening a new connection to FreeS

{{% steps %}}

### 1. Clone the Repository
### Clone the Repository

```bash
git clone https://github.com/Otoru/Genesis.git
cd Genesis
```

### 2. Install Dependencies
### Install Dependencies

```bash
poetry install --with examples
```

### 3. Configure FreeSWITCH Connection
### Configure FreeSWITCH Connection

Set environment variables for your FreeSWITCH connection:

Expand All @@ -87,15 +87,15 @@ export FS_PORT=8021
export FS_PASSWORD=ClueCon
```

### 4. Run the Server
### Run the Server

```bash
uvicorn examples.click2call:app --reload
```

The API will be available at `http://localhost:8000`.

### 5. Test the Endpoint
### Test the Endpoint

```bash
curl -X POST "http://localhost:8000/" \
Expand Down
24 changes: 21 additions & 3 deletions docs/content/docs/Examples/group-call.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,30 @@ sequenceDiagram

## Running the Example

Start FreeSWITCH (see [Examples environment]({{< relref "../Examples/_index.md" >}})) and run:
{{% steps %}}

### Start FreeSWITCH

Make sure FreeSWITCH is running (see [Examples environment]({{< relref "../Examples/_index.md" >}})).

### Run the example

```bash
python examples/group_call.py
```

The example will ring the group `["user/1001", "user/1002", "user/1003"]` in parallel mode, wait for the first callee to answer, create and bridge the caller (`user/1000`) with the answered callee, then hang up all channels after 5 seconds.
### Make test calls

- Register multiple SIP clients: user `1000` , `1001`, `1002` and `1003`.
- Run the example; the first callee to answer is connected to the caller.

### View Logs

To see what's happening in FreeSWITCH:

```bash
docker exec -it genesis-freeswitch fs_cli -x "show channels"
docker logs genesis-freeswitch -f
```

To test this properly, you'll need multiple SIP clients registered: user `1000` (caller) and users `1001`, `1002`, `1003` (callees). The first callee to answer will be connected to the caller.
{{% /steps %}}
40 changes: 5 additions & 35 deletions docs/content/docs/Examples/ivr.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ This example demonstrates Outbound Socket mode, where FreeSWITCH connects to you

{{% steps %}}

### 1. Start FreeSWITCH
### Start FreeSWITCH

Make sure FreeSWITCH is running in Docker (see [Examples environment]({{< relref "../Examples/_index.md" >}})).

### 2. Start the IVR Server
### Start the IVR Server

In a terminal, run the IVR example:

Expand All @@ -86,7 +86,7 @@ python examples/ivr.py

The server will start listening on `0.0.0.0:9696` and wait for FreeSWITCH to connect.

### 3. Make a Test Call
### Make a Test Call

In another terminal, use FreeSWITCH CLI to originate a call to the IVR:

Expand All @@ -98,14 +98,14 @@ This command:
- Creates a call from user `1000` (a test user configured in the Docker environment)
- Routes it to number `9999` (configured in the dialplan to connect to your outbound socket)

### 4. Interact with the IVR
### Interact with the IVR

Once the call is connected:
- You'll hear the welcome message
- Press `1`, `2`, or `3` to select an option
- The IVR will respond to your selection

### 5. View Logs
### View Logs

To see what's happening in FreeSWITCH:

Expand All @@ -115,33 +115,3 @@ docker logs genesis-freeswitch -f
```

{{% /steps %}}

## Testing with a SIP Client

You can also test using a SIP client (like Linphone, Zoiper, or X-Lite):

1. Configure your SIP client to connect to FreeSWITCH:
- **Server:** `127.0.0.1:5060`
- **Username:** `1000` or `1001`
- **Password:** `1000` or `1001` (same as username)
- **Domain:** `127.0.0.1`

2. Register the SIP client

3. Make a call to `9999`

4. The call will be routed to your IVR application

## Dialplan Configuration

The Docker environment includes a dialplan entry that routes calls to `9999` to your outbound socket:

```xml
<extension name="outbound_socket_test">
<condition field="destination_number" expression="^(9999)$">
<action application="socket" data="127.0.0.1:9696 async full"/>
</condition>
</extension>
```

This means any call to `9999` will trigger FreeSWITCH to connect to your application at `127.0.0.1:9696`.
116 changes: 116 additions & 0 deletions docs/content/docs/Examples/queue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
title: Queue
weight: 25
parent: Examples
---

Outbound example: one extension calls another through the app. The caller hears hold music (or a message) until a queue slot is free, then we bridge them to the callee. Only one bridge at a time, so you keep control (e.g. one agent per queue).

## Example Code

```python {filename="examples/queue.py" base_url="https://github.com/Otoru/Genesis/blob/main"}
"""
Queue example.

One extension calls another via the app: the caller is held (music or message)
until a queue slot is free, then we bridge them to the callee. Only one
bridge at a time so you keep control (e.g. one agent per queue).
"""

import asyncio
import os

from genesis import Outbound, Session, Queue, Channel
from genesis.types import ChannelState

HOST = os.getenv("HOST", "0.0.0.0")
PORT = int(os.getenv("PORT", "9696"))
CALLEE = "user/1001"
HOLD_SOUND = os.getenv("HOLD_SOUND", "local_stream://moh")

queue = Queue() # in-memory by default


async def handler(session: Session) -> None:
if session.channel is None:
return
await session.channel.answer()
await session.channel.playback(HOLD_SOUND, block=False)

async with queue.slot("support"):
callee = await Channel.create(session, CALLEE)
await callee.wait(ChannelState.EXECUTE, timeout=30.0)
await session.channel.bridge(callee)


async def main() -> None:
server = Outbound(handler=handler, host=HOST, port=PORT)
await server.start()


if __name__ == "__main__":
asyncio.run(main())
```

## Flow

{{% steps %}}

### FreeSWITCH sends the call

FreeSWITCH sends the call to your app (outbound socket).

### Answer and play hold sound

We answer and start playing a hold sound (`playback(..., block=False)`), so the caller hears it while waiting.

### Wait for a queue slot

The handler waits for a slot in the `"support"` queue (`async with queue.slot("support")`). If another call is already in the slot, this call waits (caller keeps hearing the hold sound).

### Originate callee and bridge

When we get the slot, we originate the callee (`Channel.create(session, CALLEE)`), wait for them to answer, then bridge the caller to the callee. The bridge replaces the hold playback.

### Release the slot

When the handler leaves the `async with` block, the slot is released and the next waiting caller can be served.

{{% /steps %}}

## Running the Example

{{% steps %}}

### Start FreeSWITCH

Make sure FreeSWITCH is running (see [Examples environment]({{< relref "../Examples/_index.md" >}})).

### Run the queue example

```bash
python examples/queue.py
```

### Make test calls

- You need two SIP clients: caller and callee (`user/1001`). See [Examples environment]({{< relref "../Examples/_index.md" >}}) (Docker includes MOH).
- Call the number that hits this dialplan. You hear hold music until your turn, then you're bridged to the callee.
- Place a second call while the first is still connected: the second caller hears hold music until the first call ends.

### View Logs

To see what's happening in FreeSWITCH:

```bash
docker exec -it genesis-freeswitch fs_cli -x "show channels"
docker logs genesis-freeswitch -f
```

{{% /steps %}}

## Related

- [Queue]({{< relref "../Tools/queue/_index.md" >}}) - Queue API and backends
- [Outbound Socket]({{< relref "../Quickstart/outbound.md" >}}) - Outbound basics
- [Channel]({{< relref "../Tools/channel.md" >}}) - Creating channels and bridge
1 change: 1 addition & 0 deletions docs/content/docs/Observability/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ Genesis ships with **OpenTelemetry** for tracing, logging, and metrics. You get
{{< card link="logging/" title="Logging" icon="terminal" subtitle="Structured logs with trace correlation and optional JSON output." >}}
{{< card link="server/" title="Server" icon="server" subtitle="Health, readiness, and metrics over HTTP." >}}
{{< card link="metrics/" title="Metrics" icon="chart-bar" subtitle="Counters and histograms for commands, events, channels, and ring groups." >}}
{{< card link="otel-config/" title="OTEL configuration" icon="cog" subtitle="Configure OpenTelemetry via OTEL_SDK_DISABLED, OTEL_SERVICE_NAME, and OTEL_RESOURCE_ATTRIBUTES." >}}
{{< /cards >}}
56 changes: 56 additions & 0 deletions docs/content/docs/Observability/otel-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: Configuration
weight: 70
---

Genesis supports configuring OpenTelemetry via standard environment variables. When you run the CLI (`genesis consumer` or `genesis outbound`), these variables control the metrics resource and whether the SDK is enabled.

## Supported variables

- **`OTEL_SDK_DISABLED`**
- Disables the OpenTelemetry SDK when set to `true` (case-insensitive).
- When disabled, the CLI does not set a meter provider; metrics are no-ops.
- Default: not set (SDK enabled).

- **`OTEL_SERVICE_NAME`**
- Sets the `service.name` resource attribute for metrics (and traces if you configure a tracer provider).
- Default: `genesis`.

- **`OTEL_RESOURCE_ATTRIBUTES`**
- Extra resource attributes as comma-separated key-value pairs: `key1=value1,key2=value2`.
- If `service.name` is present here, it is overridden by `OTEL_SERVICE_NAME` when that variable is set.
- Example: `deployment.environment=production,service.version=1.0.0`.

- **`OTEL_EXPORTER_OTLP_ENDPOINT`**
- Base URL for OTLP/HTTP export (traces and metrics). When set, the CLI configures an OTLP HTTP exporter so telemetry is sent to this endpoint (e.g. an OpenTelemetry Collector).
- Default for HTTP per spec: `http://localhost:4318` (collector OTLP HTTP receiver).

- **`OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`**
- Overrides the metrics endpoint (if unset, `OTEL_EXPORTER_OTLP_ENDPOINT` is used).

- **`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`**
- Overrides the traces endpoint (if unset, `OTEL_EXPORTER_OTLP_ENDPOINT` is used). When set (or when `OTEL_EXPORTER_OTLP_ENDPOINT` is set), the CLI also sets a TracerProvider with OTLP HTTP span exporter.

## Examples

Disable OpenTelemetry (e.g. in tests or when using another instrumentation):

```bash
export OTEL_SDK_DISABLED=true
genesis consumer ...
```

Set a custom service name and environment:

```bash
export OTEL_SERVICE_NAME=my-call-center
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
genesis outbound ...
```

Send metrics and traces to an OTLP collector over HTTP:

```bash
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
genesis consumer ...
```
2 changes: 2 additions & 0 deletions docs/content/docs/Tools/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ weight: 50
Useful utilities and patterns to streamline your development with Genesis.

{{< cards cols="1" >}}
{{< card link="uvloop/" title="uvloop" icon="lightning-bolt" subtitle="Optional fast asyncio event loop on Unix; install with genesis[uvloop]." >}}
{{< card link="filtrate/" title="Filtrate" icon="code" subtitle="Filter events based on key-value pairs using decorators." >}}
{{< card link="channel/" title="Channel Abstraction" icon="phone" subtitle="Create and manage outbound channels for call origination and bridging." >}}
{{< card link="queue/" title="Queue" icon="view-list" subtitle="FIFO queue with concurrency limit; slot and semaphore API; in-memory or Redis backend." >}}
{{< card link="ring-group/" title="Ring Group" icon="users" subtitle="Call multiple destinations simultaneously or sequentially, connect to first answer." >}}
{{< /cards >}}
Loading
Loading