Variables & Templates

Use dynamic variables and templates to personalize conversations and inject context into your voice agent.

Variables & Templates

Variables allow you to inject dynamic content into prompts, greetings, and responses, enabling personalized conversations.

Overview

┌──────────────────────────────────────────────────────────────┐
│                    Variable System                            │
│                                                              │
│   Call Context ───► Template Engine ───► Rendered Output    │
│   {customer: "John"}      "Hello {{customer}}!"  "Hello John!"│
│                                                              │
└──────────────────────────────────────────────────────────────┘

Configuration

Define Variables

{
  "agent": {
    "name": "Customer Support",
    "variables": {
      "companyName": "Acme Corp",
      "supportHours": "9 AM to 6 PM",
      "website": "acme.com"
    },
    "greetingMessage": "Hello! Welcome to {{companyName}}. How can I help?",
    "prompt": "You are a support agent for {{companyName}}. Support hours are {{supportHours}}."
  }
}

Variable Types

Type Example Description
Static companyName Configured in agent settings
Dynamic customerName Passed at call time
System currentTime Auto-populated by system
Computed orderTotal Calculated from data

Implementation

Template Engine

type TemplateEngine struct {
    staticVars  map[string]string
    dynamicVars map[string]string
    systemVars  map[string]func() string
}

func NewTemplateEngine(staticVars map[string]string) *TemplateEngine {
    return &TemplateEngine{
        staticVars:  staticVars,
        dynamicVars: make(map[string]string),
        systemVars: map[string]func() string{
            "currentTime":     getCurrentTime,
            "currentDate":     getCurrentDate,
            "callDuration":    func() string { return "calculating..." },
            "agentName":       func() string { return "Assistant" },
        },
    }
}

func (t *TemplateEngine) Render(template string) string {
    result := template

    // Replace static variables
    for key, value := range t.staticVars {
        placeholder := "{{" + key + "}}"
        result = strings.ReplaceAll(result, placeholder, value)
    }

    // Replace dynamic variables
    for key, value := range t.dynamicVars {
        placeholder := "{{" + key + "}}"
        result = strings.ReplaceAll(result, placeholder, value)
    }

    // Replace system variables
    for key, fn := range t.systemVars {
        placeholder := "{{" + key + "}}"
        if strings.Contains(result, placeholder) {
            result = strings.ReplaceAll(result, placeholder, fn())
        }
    }

    return result
}

func (t *TemplateEngine) SetDynamicVar(key, value string) {
    t.dynamicVars[key] = value
}

Passing Variables at Call Time

// When initiating a call
func (s *Session) StartCall(config CallConfig) error {
    // Set dynamic variables from call config
    for key, value := range config.Variables {
        s.templateEngine.SetDynamicVar(key, value)
    }

    // Render greeting with variables
    greeting := s.templateEngine.Render(s.agent.GreetingMessage)
    s.tts.Speak(greeting)

    // Render prompt with variables
    prompt := s.templateEngine.Render(s.agent.Prompt)
    s.llm.SetSystemPrompt(prompt)

    return nil
}

API Example

# Initiate call with variables
curl -X POST https://voice-agent.edesy.in/api/v1/calls \
  -H "Authorization: Bearer $API_KEY" \
  -d '{
    "agent_id": "agent_123",
    "to": "+1234567890",
    "variables": {
      "customerName": "John Smith",
      "orderId": "ORD-12345",
      "orderStatus": "shipped",
      "deliveryDate": "December 30th"
    }
  }'

System Variables

Built-in Variables

Variable Description Example
{{currentTime}} Current time "2:30 PM"
{{currentDate}} Current date "December 28, 2024"
{{callDuration}} Time in call "2 minutes"
{{callerNumber}} Caller's phone "+1234567890"
{{agentName}} Agent's name "Customer Support"

System Variable Implementation

func getCurrentTime() string {
    return time.Now().Format("3:04 PM")
}

func getCurrentDate() string {
    return time.Now().Format("January 2, 2006")
}

func (s *Session) getCallDuration() string {
    duration := time.Since(s.startTime)
    if duration < time.Minute {
        return fmt.Sprintf("%d seconds", int(duration.Seconds()))
    }
    return fmt.Sprintf("%d minutes", int(duration.Minutes()))
}

func (s *Session) getCallerNumber() string {
    return s.callerID
}

Use Cases

Personalized Greeting

{
  "greetingMessage": "Hello {{customerName}}! This is {{agentName}} from {{companyName}}. I see you have an order {{orderId}}. How can I help you today?"
}

Dynamic Prompt

{
  "prompt": "You are a support agent for {{companyName}}.\n\nCustomer Information:\n- Name: {{customerName}}\n- Account: {{accountId}}\n- Plan: {{subscriptionPlan}}\n\nPrevious interactions: {{lastInteractionSummary}}\n\nBe helpful and address them by name."
}

Order Status

// Lookup order and set variables
func (s *Session) prepareOrderVariables(orderID string) error {
    order, err := s.orderService.GetOrder(orderID)
    if err != nil {
        return err
    }

    s.templateEngine.SetDynamicVar("orderId", order.ID)
    s.templateEngine.SetDynamicVar("orderStatus", order.Status)
    s.templateEngine.SetDynamicVar("orderTotal", formatCurrency(order.Total))
    s.templateEngine.SetDynamicVar("shippingAddress", order.ShippingAddress)
    s.templateEngine.SetDynamicVar("estimatedDelivery", formatDate(order.EstimatedDelivery))

    return nil
}

// Template
prompt := `Customer is calling about order {{orderId}}.
Current status: {{orderStatus}}
Order total: {{orderTotal}}
Shipping to: {{shippingAddress}}
Estimated delivery: {{estimatedDelivery}}`

Appointment Confirmation

variables := map[string]string{
    "patientName":     "John Smith",
    "appointmentDate": "December 30th",
    "appointmentTime": "2:30 PM",
    "doctorName":      "Dr. Johnson",
    "clinicName":      "Main Street Clinic",
    "clinicPhone":     "555-123-4567",
}

greeting := "Hello {{patientName}}! This is {{clinicName}} calling to confirm your appointment with {{doctorName}} on {{appointmentDate}} at {{appointmentTime}}. Press 1 to confirm or 2 to reschedule."

Conditional Content

If-Else Templates

type ConditionalTemplate struct {
    conditions map[string]string
}

func (t *TemplateEngine) RenderConditional(template string) string {
    // Pattern: {{#if variable}}content{{else}}alternative{{/if}}
    re := regexp.MustCompile(`\{\{#if (\w+)\}\}(.*?)\{\{else\}\}(.*?)\{\{/if\}\}`)

    result := re.ReplaceAllStringFunc(template, func(match string) string {
        parts := re.FindStringSubmatch(match)
        varName := parts[1]
        ifContent := parts[2]
        elseContent := parts[3]

        if t.hasVar(varName) && t.getVar(varName) != "" {
            return t.Render(ifContent)
        }
        return t.Render(elseContent)
    })

    return result
}

// Usage
template := `Hello {{customerName}}!
{{#if hasActiveOrder}}Your order {{orderId}} is {{orderStatus}}.{{else}}I don't see any active orders.{{/if}}`

Switch Templates

func (t *TemplateEngine) RenderSwitch(template string) string {
    // Pattern: {{#switch variable}}{{#case value}}content{{/case}}{{/switch}}
    // Implementation...
    return template
}

// Usage
template := `{{#switch orderStatus}}
{{#case "processing"}}Your order is being processed.{{/case}}
{{#case "shipped"}}Your order has shipped!{{/case}}
{{#case "delivered"}}Your order was delivered.{{/case}}
{{/switch}}`

Formatting Helpers

Built-in Formatters

type Formatter struct{}

func (f *Formatter) Currency(amount float64, currency string) string {
    switch currency {
    case "USD":
        return fmt.Sprintf("$%.2f", amount)
    case "INR":
        return fmt.Sprintf("₹%.2f", amount)
    case "EUR":
        return fmt.Sprintf("€%.2f", amount)
    default:
        return fmt.Sprintf("%.2f %s", amount, currency)
    }
}

func (f *Formatter) Date(t time.Time, format string) string {
    formats := map[string]string{
        "short":    "Jan 2",
        "medium":   "January 2, 2006",
        "long":     "Monday, January 2, 2006",
        "relative": "", // "yesterday", "2 days ago"
    }

    if fmt, ok := formats[format]; ok {
        if format == "relative" {
            return f.relativeDate(t)
        }
        return t.Format(fmt)
    }
    return t.Format(format)
}

func (f *Formatter) Phone(number string) string {
    // Format: (555) 123-4567
    if len(number) == 10 {
        return fmt.Sprintf("(%s) %s-%s", number[:3], number[3:6], number[6:])
    }
    return number
}

func (f *Formatter) SpeakableNumber(number string) string {
    // "1234" -> "1 2 3 4"
    return strings.Join(strings.Split(number, ""), " ")
}

Template with Formatters

// {{amount|currency:"USD"}} -> "$25.99"
// {{date|format:"medium"}} -> "December 28, 2024"
// {{phone|format}} -> "(555) 123-4567"

func (t *TemplateEngine) RenderWithFormatters(template string) string {
    re := regexp.MustCompile(`\{\{(\w+)\|(\w+)(?::"([^"]+)")?\}\}`)

    result := re.ReplaceAllStringFunc(template, func(match string) string {
        parts := re.FindStringSubmatch(match)
        varName := parts[1]
        formatter := parts[2]
        arg := parts[3]

        value := t.getVar(varName)

        switch formatter {
        case "currency":
            amount, _ := strconv.ParseFloat(value, 64)
            return t.formatter.Currency(amount, arg)
        case "format":
            // Parse as date and format
            date, _ := time.Parse(time.RFC3339, value)
            return t.formatter.Date(date, arg)
        case "phone":
            return t.formatter.Phone(value)
        case "speakable":
            return t.formatter.SpeakableNumber(value)
        }

        return value
    })

    return result
}

Variable Scopes

Session Variables

Persist for the entire call:

func (s *Session) SetSessionVar(key, value string) {
    s.sessionVars[key] = value
}

func (s *Session) GetSessionVar(key string) string {
    return s.sessionVars[key]
}

Turn Variables

Reset each conversation turn:

func (s *Session) SetTurnVar(key, value string) {
    s.turnVars[key] = value
}

func (s *Session) ClearTurnVars() {
    s.turnVars = make(map[string]string)
}

Global Variables

Shared across all calls:

var globalVars = map[string]string{
    "companyName":   "Acme Corp",
    "supportEmail":  "[email protected]",
    "supportPhone":  "1-800-ACME",
}

Webhook Integration

Receiving Variables via Webhook

type WebhookPayload struct {
    Event     string            `json:"event"`
    CallID    string            `json:"call_id"`
    Variables map[string]string `json:"variables"`
}

func (s *Session) HandleWebhook(payload WebhookPayload) {
    // Update session variables from webhook
    for key, value := range payload.Variables {
        s.templateEngine.SetDynamicVar(key, value)
    }
}

Example: CRM Integration

{
  "event": "call.started",
  "call_id": "call_123",
  "variables": {
    "customerName": "John Smith",
    "accountTier": "Premium",
    "lastPurchase": "Widget Pro",
    "lifetimeValue": "$5,432"
  }
}

Best Practices

1. Validate Variables

func (t *TemplateEngine) ValidateTemplate(template string) []string {
    var missing []string

    re := regexp.MustCompile(`\{\{(\w+)\}\}`)
    matches := re.FindAllStringSubmatch(template, -1)

    for _, match := range matches {
        varName := match[1]
        if !t.hasVar(varName) {
            missing = append(missing, varName)
        }
    }

    return missing
}

2. Provide Defaults

func (t *TemplateEngine) RenderWithDefaults(template string) string {
    // {{variable|default:"fallback"}}
    re := regexp.MustCompile(`\{\{(\w+)\|default:"([^"]+)"\}\}`)

    result := re.ReplaceAllStringFunc(template, func(match string) string {
        parts := re.FindStringSubmatch(match)
        varName := parts[1]
        defaultVal := parts[2]

        if value := t.getVar(varName); value != "" {
            return value
        }
        return defaultVal
    })

    return result
}

// Usage
template := "Hello {{customerName|default:\"valued customer\"}}!"

3. Escape User Input

func (t *TemplateEngine) EscapeForSpeech(value string) string {
    // Remove or escape characters that might affect TTS
    value = strings.ReplaceAll(value, "<", "less than")
    value = strings.ReplaceAll(value, ">", "greater than")
    value = strings.ReplaceAll(value, "&", "and")
    return value
}

Next Steps