Bolna

repository·master·Indexed 20 days ago

https://github.com/bolna-ai/bolna

An end-to-end open-source orchestration platform for building LLM-based voice conversational assistants. Bolna orchestrates ASR, LLM, and TTS providers over websockets to create production-ready voice agents. It supports programmatic agent creation via a Python SDK using the Assistant class, as well as a REST API for managing agents. The platform integrates with telephony providers like Twilio and Plivo and can be deployed locally using Docker.

Tokens
11.8K
Snippets
34
Records
42
Agent score
72%

What's inside Bolna

  1. Extend Bolna with new Telephony Providers

    master

    To add support for a new telephony provider (e.g., Vonage, Telnyx), ensure the provider supports bi-directional streaming and follow these four steps:

    1. Verify Streaming Support: Confirm the provider supports bi-directional streaming.
    2. Implement Input Handler: Create a telephony-specific input handler file in bolna/input_handlers/telephony_providers. This file must contain custom functions that extend the telephony.py class and define how event packets from the provider are ingested.
    3. Implement Output Handler: Create a telephony-specific output handler file in bolna/output_handlers/telephony_providers. This file must extend the telephony.py class and handle converting audio from the synthesizer class into a supported format for streaming over the provider's websocket.
    4. Create a Dedicated Server: Write a dedicated server to initiate calls over websockets. You can use local_setup/telephony_server/twilio_api_server.py as a reference implementation.
  2. Set up Bolna locally using Docker

    master

    Bolna can be run locally using a Docker-based setup consisting of four main containers: a Telephony web server (Twilio or Plivo), the Bolna server, ngrok for tunneling, and redis for data persistence.

    Before starting, you must populate an environment .env file by copying the provided .env.sample.

    Prerequisites

    • Telephony Account: You must have either a Twilio or Plivo account.
    • ngrok Configuration: Add your authtoken to ngrok-config.yml to enable tunneling.
  3. Quick Start with Local Docker Setup

    master

    The easiest way to set up a local environment for Bolna, including telephony (Twilio or Plivo), the Bolna server, ngrok for tunneling, and Redis, is to use the provided shell script in the local_setup directory.

    Prerequisites:

    • Docker and Docker Compose V2 installed.
    • A Twilio or Plivo account for telephony.
    • An .env file populated from .env.sample in the local_setup directory.
    • An authtoken added to ngrok-config.yml for tunneling.
    cd local_setup
    chmod +x start.sh
    ./start.sh
  4. Quick Start Bolna with the start script

    master

    The fastest way to initialize the local environment is to use the provided start.sh script. This script verifies Docker dependencies, builds all services using BuildKit, and starts the containers in detached mode.

    chmod +x start.sh
    ./start.sh
  5. Manual Setup using Docker Compose

    master

    If you prefer to manage services manually, you can build and run the Bolna orchestration platform using Docker Compose. It is recommended to enable BuildKit for faster builds.

    1. Enable BuildKit:
      export DOCKER_BUILDKIT=1
      export COMPOSE_DOCKER_CLI_BUILD=1
    2. Build images:
      docker compose build
    3. Run services in detached mode:
      docker compose up -d

    To run only specific services (e.g., just the Bolna app and a specific telephony provider):

    docker compose up -d bolna-app twilio-app
    # or
    docker compose up -d bolna-app plivo-app
    export DOCKER_BUILDKIT=1
    export COMPOSE_DOCKER_CLI_BUILD=1
    docker compose build
    docker compose up -d
  6. Manually build and run Bolna services with Docker Compose

    master

    If you prefer manual control, follow these steps to build and run the services using Docker Compose V2. It is recommended to enable BuildKit for faster builds.

    1. Enable BuildKit:
      export DOCKER_BUILDKIT=1
      export COMPOSE_DOCKER_CLI_BUILD=1
    2. Build the images:
      docker compose build
    3. Start the services:
      docker compose up -d
    export DOCKER_BUILDKIT=1
    export COMPOSE_DOCKER_CLI_BUILD=1
    docker compose build
    docker compose up -d
  7. How Graph Agents work

    master

    A GraphAgentConfig defines a conversation flow using a directed graph of GraphNodes and GraphEdges.

    Nodes

    Nodes can be of type NodeType.LLM (standard conversation) or NodeType.ROUTER (silent dispatch).

    • Router Nodes: Must NOT have a prompt or static_message. They must have at least one UNCONDITIONAL edge to ensure the conversation always advances.
    • LLM Nodes: Contain a prompt, static_message, and edges.

    Edges

    Edges define transitions between nodes using several EdgeConditionTypes:

    1. expression: Uses ExpressionGroup (logic + conditions) to evaluate variables (e.g., recipient_data.age).
    2. event: Triggers based on a CallEvent name.
    3. llm: The LLM decides the transition based on a condition description.
    4. unconditional: Always triggers.

    Edges also support priority to resolve conflicts between multiple valid transitions.

    # Conceptual structure of a Graph Agent
    agent_config = GraphAgentConfig(
        model="gpt-4o",
        agent_information="You are a helpful assistant.",
        nodes=[
            GraphNode(id="start", node_type="llm", prompt="Hello!", edges=[...]),
            GraphNode(id="router_1", node_type="router", edges=[...])
        ],
        current_node_id="start"
    )
  8. Set up the local development environment with start.sh

    master

    The start.sh script automates the local setup of Bolna services using Docker Compose. It performs the following steps:

    1. Prerequisite Check: Verifies that docker and docker compose (v2 CLI) are installed.
    2. BuildKit Configuration: Enables DOCKER_BUILDKIT=1 and COMPOSE_DOCKER_CLI_BUILD=1 to optimize the build process.
    3. Service Build: Executes docker compose build to build all services defined in the docker-compose.yml file.
    4. Service Startup: Runs docker compose up -d to start the services in detached mode.

    Prerequisites:

    • Docker must be installed.
    • Docker Compose (v2 CLI) must be available.

    Post-setup commands:

    • To check running containers: docker compose ps
    • To view logs: docker compose logs -f
    ./local_setup/start.sh
  9. Configure Bolna local setup with Docker Compose

    master

    To run Bolna locally, use the provided docker-compose.yml file. The setup orchestrates several services including the main Bolna application, Redis for persistent storage, ngrok for local tunneling, and telephony servers (Twilio and Plivo).

    Key Services

    • bolna-app: The core Bolna service. It builds from dockerfiles/bolna_server.Dockerfile and exposes port 5001. It requires a .env file for environment variables and mounts local AWS credentials for cloud integration.
    • redis: Used as persistent storage, running on port 6379.
    • ngrok: Provides local tunneling. It requires a local ngrok-config.yml to be mounted to /etc/ngrok.yml inside the container. It exposes port 4040 for the dashboard.
    • twilio-app: The Twilio telephony server, building from dockerfiles/twilio_server.Dockerfile and exposing port 8001.
    • plivo-app: The Plivo telephony server, building from dockerfiles/plivo_server.Dockerfile and exposing port 8002.

    Required Files and Volumes

    Before running docker-compose up, ensure the following are present:

    1. A .env file in the same directory as the compose file.
    2. An ngrok-config.yml file for ngrok configuration.
    3. An agent_data directory located one level above the compose file (../agent_data) to persist agent information.
    4. Valid AWS credentials at $HOME/.aws/credentials and $HOME/.aws/config if using AWS services.
    # Example command to start the stack
    docker-compose up
  10. Build a Text-only Agent pipeline in Python

    master

    If you do not require audio processing, you can create a text-only pipeline by setting enable_textual_input=True in assistant.add_task() and omitting the transcriber and synthesizer configurations.

    import asyncio
    from bolna.assistant import Assistant
    from bolna.models import LlmAgent, SimpleLlmAgent
    
    
    async def main():
        assistant = Assistant(name="text_only_agent")
    
        llm_agent = LlmAgent(
            agent_type="simple_llm_agent",
            agent_flow_type="streaming",
            llm_config=SimpleLlmAgent(
                provider="openai",
                model="gpt-4o-mini",
                temperature=0.2,
            ),
        )
    
        # No transcriber/synthesizer; enable a text-only pipeline
        assistant.add_task(
            task_type="conversation",
            llm_agent=llm_agent,
            enable_textual_input=True,
        )
    
        async for chunk in assistant.execute():
            print(chunk)
    
    
    if __name__ == "__main__":
        asyncio.run(main())
  11. Build a Voice AI Agent programmatically in Python

    master

    You can build and run a voice agent directly in Python without needing the local telephony setup. This involves configuring a Transcriber (ASR), an LlmAgent (LLM), and a Synthesizer (TTS), then adding them as a conversation task to an Assistant instance.

    assistant.execute() returns an async generator that yields per-task result dictionaries (event-like chunks).

    import asyncio
    from bolna.assistant import Assistant
    from bolna.models import (
        Transcriber,
        Synthesizer,
        ElevenLabsConfig,
        LlmAgent,
        SimpleLlmAgent,
    )
    
    
    async def main():
        assistant = Assistant(name="demo_agent")
    
        # Configure audio input (ASR)
        transcriber = Transcriber(provider="deepgram", model="nova-2", stream=True, language="en")
    
        # Configure LLM
        llm_agent = LlmAgent(
            agent_type="simple_llm_agent",
            agent_flow_type="streaming",
            llm_config=SimpleLlmAgent(
                provider="openai",
                model="gpt-4o-mini",
                temperature=0.3,
            ),
        )
    
        # Configure audio output (TTS)
        synthesizer = Synthesizer(
            provider="elevenlabs",
            provider_config=ElevenLabsConfig(voice="George", voice_id="JBFqnCBsd6RMkjVDRZzb", model="eleven_turbo_v2_5"),
            stream=True,
            audio_format="wav",
        )
    
        # Build a single coherent pipeline: transcriber -> llm -> synthesizer
        assistant.add_task(
            task_type="conversation",
            llm_agent=llm_agent,
            transcriber=transcriber,
            synthesizer=synthesizer,
            enable_textual_input=False,
        )
    
        # Stream results
        async for chunk in assistant.execute():
            print(chunk)
    
    
    if __name__ == "__main__":
        asyncio.run(main())
  12. Retrieve all Agents

    master

    Fetch a list of all agents currently managed by the system.

    Endpoint: GET /all

    Response (200 OK): Returns an object containing an array of agents. Each agent object includes its agent_id and its configuration data (containing agent_config and agent_prompts).

    {
      "agents": [
        {
          "agent_id": "string",
          "data": {
            "agent_config": {
              "agent_name": "Alfred",
              "agent_type": "other",
              "tasks": []
            },
            "agent_prompts": {}
          }
        }
      ]
    }