A channel connects an agent module to callers. Guava.Channel is a supervised
process you add to your own supervision tree; the blocking Guava.* helpers are
for scripts.
Supervised (production)
children = [
{Guava.Channel, agent: SalesAgent, listen: {:phone, "+14155550100"}},
{Guava.Channel, agent: SurveyAgent, campaign: "camp_abc"}
]
Supervisor.start_link(children, strategy: :one_for_one)Options:
:agent— the agent module (required)one of
listen: {:phone, num} | {:webrtc, code | nil} | {:sip, code},campaign: code, oroutbound: {from, to, variables}:client— aGuava.Client(defaults toGuava.Client.new!/0)
The channel owns the listen/campaign socket and starts a supervised process per live call. The listener restarts on crash; the WebSocket layer reconnects on its own. This replaces hand-rolled listen/reconnect loops.
Blocking helpers (scripts)
Each blocks the caller:
Guava.listen_phone(MyAgent, "+14155550123")
Guava.listen_webrtc(MyAgent) # creates a temporary code
Guava.listen_sip(MyAgent, "guavasip-...")
Guava.attach_campaign(MyAgent, "camp_abc")
Guava.call_phone(MyAgent, from, to, %{"customer" => "Ada"}) # returns when the call endsGuava.run/1 starts one or more channel child specs under a supervisor and
blocks — handy for a mix run entrypoint:
Guava.run([
{Guava.Channel, agent: SalesAgent, listen: {:phone, "+14155550100"}},
{Guava.Channel, agent: SurveyAgent, campaign: "camp_abc"}
])Inbound example: Q&A with RAG
defmodule Nova do
use Guava.Agent, name: "Nova", organization: "Clearfield Home"
@impl true
def handle_question(_call, question, state) do
{:ok, qa} = {:ok, Process.get(:qa)} # or hold it in state via init/1
{:reply, Guava.DocumentQA.ask!(qa, question), state}
end
end
Guava.listen_phone(Nova, "+14155550123")Outbound example: scheduling
defmodule Scheduler do
use Guava.Agent, name: "Nova", organization: "Acme"
@impl true
def handle_start(call, state) do
Guava.Call.reach_person(call, Guava.Call.get_variable(call, "customer_name", "there"))
{:noreply, state}
end
@impl true
def handle_task_complete("reach_person", call, state) do
if Guava.Call.get_field(call, "contact_availability") == "available" do
Guava.Call.set_task(call, "schedule",
objective: "Offer available times and book one.",
checklist: [Guava.Field.new(key: "slot", field_type: "calendar_slot", searchable: true)]
)
else
Guava.Call.hangup(call)
end
{:noreply, state}
end
@impl true
def handle_search_query("slot", _call, query, state) do
filter = Guava.DatetimeFilter.new(Process.get(:client), Scheduling.open_slots())
{:reply, Guava.DatetimeFilter.filter!(filter, query), state}
end
@impl true
def handle_task_complete("schedule", call, state) do
Scheduling.book(Guava.Call.get_field(call, "slot"))
Guava.Call.hangup(call, "You're all set — see you then!")
{:noreply, state}
end
end
Guava.call_phone(Scheduler, "+14155550100", "+16285550123", %{"customer_name" => "Ada"})Next: Campaigns.