| Duration | 2 hours |
| Day | 3 of 5 |
Learning Objectives
By the end of this module, students will be able to:
- Create DataMap functions for REST API integration
- Use variable substitution in requests
- Process API responses for voice output
- Handle arrays with foreach expressions
Topics
1. What is DataMap? (20 min)
The Problem
Traditional function flow:
AI → Your Webhook → External API → Your Webhook → AI
↑ ↑
You write code You write code
The DataMap Solution
AI → DataMap (No Code) → External API → DataMap → AI
DataMap handles API calls without custom webhook handlers!
When to Use DataMap
| Use DataMap | Use Custom Functions |
|---|---|
| Simple REST calls | Complex business logic |
| Direct API mapping | Multiple API calls |
| No transformation needed | Data transformation |
| Quick integration | Database operations |
2. DataMap Structure (30 min)
Basic DataMap Definition
from signalwire_agents import AgentBase, DataMap, SwaigFunctionResult
agent = AgentBase(name="weather-agent")
# Create DataMap using builder pattern
weather_map = (
DataMap("get_weather")
.description("Get current weather for a city")
.parameter("city", "string", "City name", required=True)
.webhook("GET", "https://api.weather.com/current?q=${enc:args.city}&appid=API_KEY")
.output(SwaigFunctionResult(
"The weather in ${args.city} is ${response.main.temp} degrees "
"with ${response.weather[0].description}."
))
)
# Register with agent
agent.register_swaig_function(weather_map.to_swaig_function())
DataMap Components
| Method | Purpose |
|---|---|
DataMap("name") | Function name AI will call |
.description() | When AI should use it |
.parameter() | Add input parameter |
.webhook() | API call configuration |
.output() | Response mapping with SwaigFunctionResult |
.fallback_output() | Response when API fails |
.to_swaig_function() | Convert to registerable function |
3. Variable Substitution (25 min)
Available Variables
| Pattern | Source |
|---|---|
${args.name} | Function parameters |
${env.VAR} | Environment variables |
${response.field} | API response data |
${meta.call_id} | Call metadata |
Parameter Substitution
from signalwire_agents import DataMap, SwaigFunctionResult
# Use ${enc:args.param} for URL-encoded values in URLs
data_map = (
DataMap("search")
.description("Search for data")
.parameter("id", "string", "Record ID", required=True)
.parameter("search_term", "string", "Search query", required=True)
.webhook(
"GET",
"https://api.example.com/data/${enc:args.id}?query=${enc:args.search_term}",
headers={"Authorization": "Bearer ${global_data.api_token}"}
)
.output(SwaigFunctionResult("Found: ${response.title}"))
)
Response Substitution
order_map = (
DataMap("get_order")
.description("Get order status")
.parameter("order_id", "string", "Order ID", required=True)
.webhook("GET", "https://api.example.com/orders/${enc:args.order_id}")
.output(SwaigFunctionResult(
"Order ${response.order_id} is ${response.status}. "
"Shipped via ${response.carrier}."
))
)
Nested Response Access
# API returns: {"data": {"user": {"name": "John"}}}
user_map = (
DataMap("get_user")
.description("Get user info")
.parameter("user_id", "string", "User ID", required=True)
.webhook("GET", "https://api.example.com/users/${enc:args.user_id}")
.output(SwaigFunctionResult("Hello, ${response.data.user.name}!"))
)
4. Array Processing (25 min)
The foreach Expression
Process arrays in responses using the .foreach() method:
from signalwire_agents import DataMap, SwaigFunctionResult
# API returns: {"orders": [{"id": "1", "status": "shipped"}, {"id": "2", "status": "pending"}]}
order_map = (
DataMap("get_orders")
.description("Get customer orders")
.parameter("customer_id", "string", "Customer ID", required=True)
.webhook("GET", "https://api.example.com/orders?customer_id=${enc:args.customer_id}")
.foreach({
"input_key": "orders",
"output_key": "order_list",
"max": 5,
"append": "Order ${this.id} is ${this.status}\n"
})
.output(SwaigFunctionResult("Your orders:\n${order_list}"))
)
# Output: "Your orders:\nOrder 1 is shipped\nOrder 2 is pending\n"
foreach Configuration
| Key | Description |
|---|---|
input_key | Path to array in response |
output_key | Variable name for built string |
max | Maximum items to process (optional) |
append | Template for each item (use ${this.field}) |
5. Error Handling (20 min)
Fallback Responses
Use .fallback_output() to handle API failures gracefully:
weather_map = (
DataMap("get_weather")
.description("Get current weather")
.parameter("city", "string", "City name", required=True)
.webhook("GET", "https://api.weather.com/current?q=${enc:args.city}")
.output(SwaigFunctionResult("The weather is ${response.temp} degrees."))
.fallback_output(SwaigFunctionResult(
"I'm having trouble getting the weather right now. Please try again."
))
)
Multiple Webhooks with Fallback
Chain multiple webhooks - if the first fails, the second is tried:
search_map = (
DataMap("search")
.description("Search multiple sources")
.parameter("query", "string", "Search query", required=True)
# Primary API
.webhook("GET", "https://api.primary.com/search?q=${enc:args.query}")
.output(SwaigFunctionResult("Primary result: ${response.title}"))
# Backup API (tried if primary fails)
.webhook("GET", "https://api.backup.com/search?q=${enc:args.query}")
.output(SwaigFunctionResult("Backup result: ${response.title}"))
# Final fallback if all webhooks fail
.fallback_output(SwaigFunctionResult("Sorry, search is currently unavailable."))
)
Complete Example
Click to reveal complete solution
Weather Lookup Agent
#!/usr/bin/env python3
"""Weather agent using DataMap."""
from signalwire_agents import AgentBase, DataMap, SwaigFunctionResult
agent = AgentBase(name="weather-agent")
agent.prompt_add_section(
"Role",
"You help users check the weather. Ask what city they want weather for."
)
agent.add_language("English", "en-US", "rime.spore")
# Weather DataMap using builder pattern
weather = (
DataMap("get_weather")
.description("Get current weather conditions for a city")
.parameter("city", "string", "City name", required=True)
.webhook(
"GET",
"https://api.openweathermap.org/data/2.5/weather"
"?q=${enc:args.city}&appid=${global_data.api_key}&units=imperial"
)
.output(SwaigFunctionResult(
"In ${response.name}, it's currently ${response.main.temp} degrees "
"with ${response.weather[0].description}. "
"Humidity is ${response.main.humidity} percent."
))
.fallback_output(SwaigFunctionResult(
"I couldn't get weather data for that city. Please check the city name."
))
)
# Register the DataMap as a SWAIG function
agent.register_swaig_function(weather.to_swaig_function())
if __name__ == "__main__":
agent.run()
Key Takeaways
- DataMap = No-code API integration - Direct REST calls
- Variable substitution is powerful - Args, env, response
- foreach handles arrays - Process lists in responses
- Always set fallbacks - Handle API failures gracefully
- Keep it simple - Complex logic needs custom functions
Preparation for Lab 2.2
- Get an API key (OpenWeather, or any REST API)
- Review the API documentation
- Identify response fields you want to use
Lab Preview
In Lab 2.2, you will:
- Create a weather lookup DataMap
- Add order status DataMap
- Handle array responses
- Implement error fallbacks