Skip to main content

agent_server

Copyright (c) 2025 SignalWire

This file is part of the SignalWire AI Agents SDK.

Licensed under the MIT License. See LICENSE file in the project root for full license information.

AgentBase

Base class for all SignalWire AI Agents.

This class extends SWMLService and provides enhanced functionality for building agents including:

  • Prompt building and customization
  • SWML rendering
  • SWAIG function definition and execution
  • Web service for serving SWML and handling webhooks
  • Security and session management

Subclassing options:

  1. Simple override of get_prompt() for raw text
  2. Using prompt_* methods for structured prompts
  3. Declarative PROMPT_SECTIONS class attribute

Methods

tool

tool(cls, name = None, kwargs = {})

Class method decorator for defining SWAIG tools

Used as:

@AgentBase.tool(name="example_function", parameters={...}) def example_function(self, param1):

...

define_contexts

define_contexts()

Define contexts and steps for this agent (alternative to POM/prompt)

Returns: ContextBuilder for method chaining

Note: Contexts can coexist with traditional prompts. The restriction is only that you can't mix POM sections with raw text in the main prompt.

set_prompt_text

set_prompt_text(text: str)

Set the prompt as raw text instead of using POM

Args: text: The raw prompt text

Returns: Self for method chaining

set_post_prompt

set_post_prompt(text: str)

Set the post-prompt text for summary generation

Args: text: The post-prompt text

Returns: Self for method chaining

set_prompt_pom

set_prompt_pom(pom: List[Dict[str, Any]])

Set the prompt as a POM dictionary

Args: pom: POM dictionary structure

Returns: Self for method chaining

prompt_add_section

prompt_add_section(title: str, body: str = '', bullets: Optional[List[str]] = None, numbered: bool = False, numbered_bullets: bool = False, subsections: Optional[List[Dict[str, Any]]] = None)

Add a section to the prompt

Args: title: Section title body: Optional section body text bullets: Optional list of bullet points numbered: Whether this section should be numbered numbered_bullets: Whether bullets should be numbered subsections: Optional list of subsection objects

Returns: Self for method chaining

prompt_add_to_section

prompt_add_to_section(title: str, body: Optional[str] = None, bullet: Optional[str] = None, bullets: Optional[List[str]] = None)

Add content to an existing section (creating it if needed)

Args: title: Section title body: Optional text to append to section body bullet: Optional single bullet point to add bullets: Optional list of bullet points to add

Returns: Self for method chaining

prompt_add_subsection

prompt_add_subsection(parent_title: str, title: str, body: str = '', bullets: Optional[List[str]] = None)

Add a subsection to an existing section (creating parent if needed)

Args: parent_title: Parent section title title: Subsection title body: Optional subsection body text bullets: Optional list of bullet points

Returns: Self for method chaining

define_tool

define_tool(name: str, description: str, parameters: Dict[str, Any], handler: Callable, secure: bool = True, fillers: Optional[Dict[str, List[str]]] = None, webhook_url: Optional[str] = None, swaig_fields = {})

Define a SWAIG function that the AI can call

Args: name: Function name (must be unique) description: Function description for the AI parameters: JSON Schema of parameters handler: Function to call when invoked secure: Whether to require token validation fillers: Optional dict mapping language codes to arrays of filler phrases webhook_url: Optional external webhook URL to use instead of local handling **swaig_fields: Additional SWAIG fields to include in function definition

Returns: Self for method chaining

register_swaig_function

register_swaig_function(function_dict: Dict[str, Any])

Register a raw SWAIG function dictionary (e.g., from DataMap.to_swaig_function())

Args: function_dict: Complete SWAIG function definition dictionary

Returns: Self for method chaining

get_name

get_name()

Get agent name

Returns: Agent name

get_app

get_app()

Get the FastAPI application instance for deployment adapters like Lambda/Mangum

This method ensures the FastAPI app is properly initialized and configured, then returns it for use with deployment adapters like Mangum for AWS Lambda.

Returns: FastAPI: The configured FastAPI application instance

get_prompt

get_prompt()

Get the prompt for the agent

Returns: Either a string prompt or a POM object as list of dicts

get_post_prompt

get_post_prompt()

Get the post-prompt for the agent

Returns: Post-prompt text or None if not set

define_tools

define_tools()

Define the tools this agent can use

Returns: List of SWAIGFunction objects or raw dictionaries (for data_map tools)

This method can be overridden by subclasses.

on_summary

on_summary(summary: Optional[Dict[str, Any]], raw_data: Optional[Dict[str, Any]] = None)

Called when a post-prompt summary is received

Args: summary: The summary object or None if no summary was found raw_data: The complete raw POST data from the request

on_function_call

on_function_call(name: str, args: Dict[str, Any], raw_data: Optional[Dict[str, Any]] = None)

Called when a SWAIG function is invoked

Args: name: Function name args: Function arguments raw_data: Raw request data

Returns: Function result

validate_basic_auth

validate_basic_auth(username: str, password: str)

Validate basic auth credentials

Args: username: Username from request password: Password from request

Returns: True if valid, False otherwise

This method can be overridden by subclasses.

validate_tool_token

validate_tool_token(function_name: str, token: str, call_id: str)

Validate a tool token

Args: function_name: Name of the function/tool token: Token to validate call_id: Call ID for the session

Returns: True if token is valid, False otherwise

get_basic_auth_credentials

get_basic_auth_credentials(include_source: bool = False)

Get the basic auth credentials

Args: include_source: Whether to include the source of the credentials

Returns: If include_source is False: (username, password) tuple If include_source is True: (username, password, source) tuple, where source is one of: "provided", "environment", or "generated"

get_full_url

get_full_url(include_auth: bool = False)

Get the full URL for this agent's endpoint

Args: include_auth: Whether to include authentication credentials in the URL

Returns: Full URL including host, port, and route (with auth if requested)

as_router

as_router()

Get a FastAPI router for this agent

Returns: FastAPI router

serve

serve(host: Optional[str] = None, port: Optional[int] = None)

Start a web server for this agent

Args: host: Optional host to override the default port: Optional port to override the default

run

run(event = None, context = None, force_mode = None, host: Optional[str] = None, port: Optional[int] = None)

Smart run method that automatically detects environment and handles accordingly

Args: event: Serverless event object (Lambda, Cloud Functions) context: Serverless context object (Lambda, Cloud Functions) force_mode: Override automatic mode detection for testing host: Host override for server mode port: Port override for server mode

Returns: Response for serverless modes, None for server mode

handle_serverless_request

handle_serverless_request(event = None, context = None, mode = None)

Handle serverless environment requests (CGI, Lambda, Cloud Functions)

Args: event: Serverless event object (Lambda, Cloud Functions) context: Serverless context object (Lambda, Cloud Functions) mode: Override execution mode (from force_mode in run())

Returns: Response appropriate for the serverless platform

setup_graceful_shutdown

setup_graceful_shutdown()

Setup signal handlers for graceful shutdown (useful for Kubernetes)

on_swml_request

on_swml_request(request_data: Optional[dict] = None, callback_path: Optional[str] = None, request: Optional[Request] = None)

Customization point for subclasses to modify SWML based on request data

Args: request_data: Optional dictionary containing the parsed POST body callback_path: Optional callback path request: Optional FastAPI Request object for accessing query params, headers, etc.

Returns: Optional dict with modifications to apply to the SWML document

add_hint

add_hint(hint: str)

Add a simple string hint to help the AI agent understand certain words better

Args: hint: The hint string to add

Returns: Self for method chaining

add_hints

add_hints(hints: List[str])

Add multiple string hints

Args: hints: List of hint strings

Returns: Self for method chaining

add_pattern_hint

add_pattern_hint(hint: str, pattern: str, replace: str, ignore_case: bool = False)

Add a complex hint with pattern matching

Args: hint: The hint to match pattern: Regular expression pattern replace: Text to replace the hint with ignore_case: Whether to ignore case when matching

Returns: Self for method chaining

add_language

add_language(name: str, code: str, voice: str, speech_fillers: Optional[List[str]] = None, function_fillers: Optional[List[str]] = None, engine: Optional[str] = None, model: Optional[str] = None)

Add a language configuration to support multilingual conversations

Args: name: Name of the language (e.g., "English", "French") code: Language code (e.g., "en-US", "fr-FR") voice: TTS voice to use. Can be a simple name (e.g., "en-US-Neural2-F") or a combined format "engine.voice:model" (e.g., "elevenlabs.josh:eleven_turbo_v2_5") speech_fillers: Optional list of filler phrases for natural speech function_fillers: Optional list of filler phrases during function calls engine: Optional explicit engine name (e.g., "elevenlabs", "rime") model: Optional explicit model name (e.g., "eleven_turbo_v2_5", "arcana")

Returns: Self for method chaining

Examples:

Simple voice name

agent.add_language("English", "en-US", "en-US-Neural2-F")

Explicit parameters

agent.add_language("English", "en-US", "josh", engine="elevenlabs", model="eleven_turbo_v2_5")

Combined format

agent.add_language("English", "en-US", "elevenlabs.josh:eleven_turbo_v2_5")

set_languages

set_languages(languages: List[Dict[str, Any]])

Set all language configurations at once

Args: languages: List of language configuration dictionaries

Returns: Self for method chaining

add_pronunciation

add_pronunciation(replace: str, with_text: str, ignore_case: bool = False)

Add a pronunciation rule to help the AI speak certain words correctly

Args: replace: The expression to replace with_text: The phonetic spelling to use instead ignore_case: Whether to ignore case when matching

Returns: Self for method chaining

set_pronunciations

set_pronunciations(pronunciations: List[Dict[str, Any]])

Set all pronunciation rules at once

Args: pronunciations: List of pronunciation rule dictionaries

Returns: Self for method chaining

set_param

set_param(key: str, value: Any)

Set a single AI parameter

Args: key: Parameter name value: Parameter value

Returns: Self for method chaining

set_params

set_params(params: Dict[str, Any])

Set multiple AI parameters at once

Args: params: Dictionary of parameter name/value pairs

Returns: Self for method chaining

set_global_data

set_global_data(data: Dict[str, Any])

Set the global data available to the AI throughout the conversation

Args: data: Dictionary of global data

Returns: Self for method chaining

update_global_data

update_global_data(data: Dict[str, Any])

Update the global data with new values

Args: data: Dictionary of global data to update

Returns: Self for method chaining

set_native_functions

set_native_functions(function_names: List[str])

Set the list of native functions to enable

Args: function_names: List of native function names

Returns: Self for method chaining

set_internal_fillers

set_internal_fillers(internal_fillers: Dict[str, Dict[str, List[str]]])

Set internal fillers for native SWAIG functions

Internal fillers provide custom phrases the AI says while executing internal/native functions like check_time, wait_for_user, next_step, etc.

Args: internal_fillers: Dictionary mapping function names to language-specific filler phrases Format: {"function_name": {"language_code": ["phrase1", "phrase2"]}} Example: {"next_step": {"en-US": ["Moving to the next step...", "Great, let's continue..."]}}

Returns: Self for method chaining

Example: agent.set_internal_fillers({ "next_step": { "en-US": ["Moving to the next step...", "Great, let's continue..."], "es": ["Pasando al siguiente paso...", "Excelente, continuemos..."] }, "check_time": { "en-US": ["Let me check the time...", "Getting the current time..."] } })

add_internal_filler

add_internal_filler(function_name: str, language_code: str, fillers: List[str])

Add internal fillers for a specific function and language

Args: function_name: Name of the internal function (e.g., 'next_step', 'check_time') language_code: Language code (e.g., 'en-US', 'es', 'fr') fillers: List of filler phrases for this function and language

Returns: Self for method chaining

Example: agent.add_internal_filler("next_step", "en-US", ["Moving to the next step...", "Great, let's continue..."])

add_function_include

add_function_include(url: str, functions: List[str], meta_data: Optional[Dict[str, Any]] = None)

Add a remote function include to the SWAIG configuration

Args: url: URL to fetch remote functions from functions: List of function names to include meta_data: Optional metadata to include with the function include

Returns: Self for method chaining

set_function_includes

set_function_includes(includes: List[Dict[str, Any]])

Set the complete list of function includes

Args: includes: List of include objects, each with url and functions properties

Returns: Self for method chaining

enable_sip_routing

enable_sip_routing(auto_map: bool = True, path: str = '/sip')

Enable SIP-based routing for this agent

This allows the agent to automatically route SIP requests based on SIP usernames. When enabled, an endpoint at the specified path is automatically created that will handle SIP requests and deliver them to this agent.

Args: auto_map: Whether to automatically map common SIP usernames to this agent (based on the agent name and route path) path: The path to register the SIP routing endpoint (default: "/sip")

Returns: Self for method chaining

register_sip_username

register_sip_username(sip_username: str)

Register a SIP username that should be routed to this agent

Args: sip_username: SIP username to register

Returns: Self for method chaining

auto_map_sip_usernames

auto_map_sip_usernames()

Automatically register common SIP usernames based on this agent's name and route

Returns: Self for method chaining

set_web_hook_url

set_web_hook_url(url: str)

Override the default web_hook_url with a supplied URL string

Args: url: The URL to use for SWAIG function webhooks

Returns: Self for method chaining

set_post_prompt_url

set_post_prompt_url(url: str)

Override the default post_prompt_url with a supplied URL string

Args: url: The URL to use for post-prompt summary delivery

Returns: Self for method chaining

on_request

on_request(request_data: Optional[dict] = None, callback_path: Optional[str] = None)

Called when SWML is requested, with request data when available

This method overrides SWMLService's on_request to properly handle SWML generation for AI Agents. It forwards the call to on_swml_request for compatibility.

Args: request_data: Optional dictionary containing the parsed POST body callback_path: Optional callback path

Returns: None to use the default SWML rendering (which will call _render_swml)

register_routing_callback

register_routing_callback(callback_fn: Callable[[Request, Dict[str, Any]], Optional[str]], path: str = '/sip')

Register a callback function that will be called to determine routing based on POST data.

When a routing callback is registered, an endpoint at the specified path is automatically created that will handle requests. This endpoint will use the callback to determine if the request should be processed by this service or redirected.

The callback should take a request object and request body dictionary and return:

  • A route string if it should be routed to a different endpoint
  • None if normal processing should continue

Args: callback_fn: The callback function to register path: The path where this callback should be registered (default: "/sip")

set_dynamic_config_callback

set_dynamic_config_callback(callback: Callable[[dict, dict, dict, EphemeralAgentConfig], None])

Set a callback function for dynamic agent configuration

This callback receives an EphemeralAgentConfig object that provides the same configuration methods as AgentBase, allowing you to dynamically configure the agent's voice, prompt, parameters, etc. based on request data.

Args: callback: Function that takes (query_params, body_params, headers, agent_config) and configures the agent_config object using familiar methods like:

  • agent_config.add_language(...)
  • agent_config.prompt_add_section(...)
  • agent_config.set_params(...)
  • agent_config.set_global_data(...)

Example: def my_config(query_params, body_params, headers, agent): if query_params.get('tier') == 'premium': agent.add_language("English", "en-US", "premium_voice") agent.set_params({"end_of_speech_timeout": 500}) agent.set_global_data({"tier": query_params.get('tier', 'standard')})

my_agent.set_dynamic_config_callback(my_config)

manual_set_proxy_url

manual_set_proxy_url(proxy_url: str)

Manually set the proxy URL base for webhook callbacks

This can be called at runtime to set or update the proxy URL

Args: proxy_url: The base URL to use for webhooks (e.g., https://example.ngrok.io)

Returns: Self for method chaining

add_skill

add_skill(skill_name: str, params: Optional[Dict[str, Any]] = None)

Add a skill to this agent

Args: skill_name: Name of the skill to add params: Optional parameters to pass to the skill for configuration

Returns: Self for method chaining

Raises: ValueError: If skill not found or failed to load with detailed error message

remove_skill

remove_skill(skill_name: str)

Remove a skill from this agent

list_skills

list_skills()

List currently loaded skills

has_skill

has_skill(skill_name: str)

Check if skill is loaded

SWMLService

Base class for creating and serving SWML documents.

This class provides core functionality for:

  • Loading and validating SWML schema
  • Creating SWML documents
  • Setting up web endpoints for serving SWML
  • Managing authentication
  • Registering SWML functions

It serves as the foundation for more specialized services like AgentBase.

Methods

reset_document

reset_document()

Reset the current document to an empty state

add_verb

add_verb(verb_name: str, config: Union[Dict[str, Any], int])

Add a verb to the main section of the current document

Args: verb_name: The name of the verb to add config: Configuration for the verb or direct value for certain verbs (e.g., sleep)

Returns: True if the verb was added successfully, False otherwise

add_section

add_section(section_name: str)

Add a new section to the document

Args: section_name: Name of the section to add

Returns: True if the section was added, False if it already exists

add_verb_to_section

add_verb_to_section(section_name: str, verb_name: str, config: Union[Dict[str, Any], int])

Add a verb to a specific section

Args: section_name: Name of the section to add to verb_name: The name of the verb to add config: Configuration for the verb or direct value for certain verbs (e.g., sleep)

Returns: True if the verb was added successfully, False otherwise

get_document

get_document()

Get the current SWML document

Returns: The current SWML document as a dictionary

render_document

render_document()

Render the current SWML document as a JSON string

Returns: The current SWML document as a JSON string

register_verb_handler

register_verb_handler(handler: SWMLVerbHandler)

Register a custom verb handler

Args: handler: The verb handler to register

as_router

as_router()

Create a FastAPI router for this service

Returns: APIRouter: FastAPI router

register_routing_callback

register_routing_callback(callback_fn: Callable[[Request, Dict[str, Any]], Optional[str]], path: str = '/sip')

Register a callback function that will be called to determine routing based on POST data.

When a routing callback is registered, an endpoint at the specified path is automatically created that will handle requests. This endpoint will use the callback to determine if the request should be processed by this service or redirected.

The callback should take a request object and request body dictionary and return:

  • A route string if it should be routed to a different endpoint
  • None if normal processing should continue

Args: callback_fn: The callback function to register path: The path where this callback should be registered (default: "/sip")

extract_sip_username

extract_sip_username(request_body: Dict[str, Any])

Extract SIP username from request body

This extracts the username portion of a SIP URI from the 'to' field in the call data of a request body.

Args: request_body: The parsed JSON body of the request

Returns: The extracted SIP username, or None if not found

on_request

on_request(request_data: Optional[dict] = None, callback_path: Optional[str] = None)

Called when SWML is requested, with request data when available

Subclasses can override this to inspect or modify SWML based on the request

Args: request_data: Optional dictionary containing the parsed POST body callback_path: Optional callback path

Returns: Optional dict to modify/augment the SWML document

serve

serve(host: Optional[str] = None, port: Optional[int] = None, ssl_cert: Optional[str] = None, ssl_key: Optional[str] = None, ssl_enabled: Optional[bool] = None, domain: Optional[str] = None)

Start a web server for this service

Args: host: Host to bind to (defaults to self.host) port: Port to bind to (defaults to self.port) ssl_cert: Path to SSL certificate file ssl_key: Path to SSL key file ssl_enabled: Whether to enable SSL domain: Domain name for SSL certificate

stop

stop()

Stop the web server

get_basic_auth_credentials

get_basic_auth_credentials(include_source: bool = False)

Get the basic auth credentials

Args: include_source: Whether to include the source of the credentials

Returns: (username, password) tuple or (username, password, source) tuple if include_source is True

add_answer_verb

add_answer_verb(max_duration: Optional[int] = None, codecs: Optional[str] = None)

Add an answer verb to the current document

Args: max_duration: Maximum duration in seconds codecs: Comma-separated list of codecs

Returns: True if added successfully, False otherwise

add_hangup_verb

add_hangup_verb(reason: Optional[str] = None)

Add a hangup verb to the current document

Args: reason: Hangup reason (hangup, busy, decline)

Returns: True if added successfully, False otherwise

add_ai_verb

add_ai_verb(prompt_text: Optional[str] = None, prompt_pom: Optional[List[Dict[str, Any]]] = None, post_prompt: Optional[str] = None, post_prompt_url: Optional[str] = None, swaig: Optional[Dict[str, Any]] = None, kwargs = {})

Add an AI verb to the current document

Args: prompt_text: Simple prompt text prompt_pom: Prompt object model post_prompt: Post-prompt text post_prompt_url: Post-prompt URL swaig: SWAIG configuration **kwargs: Additional parameters

Returns: True if added successfully, False otherwise

manual_set_proxy_url

manual_set_proxy_url(proxy_url: str)

Manually set the proxy URL base for webhook callbacks

This can be called at runtime to set or update the proxy URL

Args: proxy_url: The base URL to use for webhooks (e.g., https://example.ngrok.io)

AgentServer

Server for hosting multiple SignalWire AI Agents under a single FastAPI application.

This allows you to run multiple agents on different routes of the same server, which is useful for deployment and resource management.

Example: server = AgentServer() server.register(SupportAgent(), "/support") server.register(SalesAgent(), "/sales") server.run()

Methods

register

register(agent: AgentBase, route: Optional[str] = None)

Register an agent with the server

Args: agent: The agent to register route: Optional route to override the agent's default route

Raises: ValueError: If the route is already in use

setup_sip_routing

setup_sip_routing(route: str = '/sip', auto_map: bool = True)

Set up central SIP-based routing for the server

This configures all agents to handle SIP requests at the specified path, using a coordinated routing system where each agent checks if it can handle SIP requests for specific usernames.

Args: route: The path for SIP routing (default: "/sip") auto_map: Whether to automatically map SIP usernames to agent routes

register_sip_username

register_sip_username(username: str, route: str)

Register a mapping from SIP username to agent route

Args: username: The SIP username route: The route to the agent

unregister

unregister(route: str)

Unregister an agent from the server

Args: route: The route of the agent to unregister

Returns: True if the agent was unregistered, False if not found

get_agents

get_agents()

Get all registered agents

Returns: List of (route, agent) tuples

get_agent

get_agent(route: str)

Get an agent by route

Args: route: The route of the agent

Returns: The agent or None if not found

run

run(event = None, context = None, host: Optional[str] = None, port: Optional[int] = None)

Universal run method that automatically detects environment and handles accordingly

Detects execution mode and routes appropriately:

  • Server mode: Starts uvicorn server with FastAPI
  • CGI mode: Uses same routing logic but outputs CGI headers
  • Lambda mode: Uses same routing logic but returns Lambda response

Args: event: Serverless event object (Lambda, Cloud Functions) context: Serverless context object (Lambda, Cloud Functions)
host: Optional host to override the default (server mode only) port: Optional port to override the default (server mode only)

Returns: Response for serverless modes, None for server mode

register_global_routing_callback

register_global_routing_callback(callback_fn: Callable[[Request, Dict[str, Any]], Optional[str]], path: str)

Register a routing callback across all agents

This allows you to add unified routing logic to all agents at the same path.

Args: callback_fn: The callback function to register path: The path to register the callback at

get_logger

get_logger(name: str)

Get a logger instance for the specified name with structured logging support

This is the single entry point for all logging in the SDK. All modules should use this instead of direct logging module usage.

Args: name: Logger name, typically name

Returns: StructuredLoggerWrapper that supports both regular and structured logging

get_execution_mode

Determine the execution mode based on environment variables

Returns: str: 'server', 'cgi', 'lambda', 'google_cloud_function', 'azure_function', or 'unknown'