Skip to content

Agent Server Changes Per-Contact Dynamic Variables ​

Overview ​

The Node.js backend now pre-merges all dynamic variable sources (agent defaults, campaign defaults, per-contact CSV values) into a single flat dictionary called dynamic_values before dispatching to LiveKit. The Agent server just needs to read this field and use it for prompt substitution.

What Changed in Dispatch Metadata ​

One new optional field is included in the dispatch metadata JSON:

json
{
  "mode": "call",
  "name": "John Doe",
  "phone_number": "+919876543210",
  "agent_id": "abc123",
  "agent_config": { ... },
  "dynamic_values": {
    "company_name": "Acme Corp",
    "product_name": "Widget Pro",
    "price": "₹999",
    "offer_code": "SAVE20"
  },
  ...
}

dynamic_values ​

  • A flat {key: value} dictionary, already merged with the correct priority:
    1. Per-contact values from CSV extra columns (highest priority)
    2. Campaign-wide defaults
    3. Agent-level DynamicVar defaults (lowest priority)
  • Will be null/absent if no dynamic variables exist at any level
  • The Node.js backend handles all the merging the Agent just consumes the result

Required Changes ​

1. Update MetadataSchema (src/schemas/metadata_schema.py) ​

Add one new optional field:

python
class MetadataSchema(BaseModel):
    # ... existing fields ...

    # Pre-merged dynamic variables (contact > campaign > agent defaults)
    # Ready for direct prompt substitution
    dynamic_values: Optional[dict] = None

2. Update VoiceAssistant.__init__() (src/core/voice_assistant.py) ​

Change the dynamic variable loading logic. Current code:

python
dynamic_variables = {item.key: item.value for item in agent_meta.DynamicVar or []}
dynamic_variables["name"] = user_name if user_name and user_name.lower() != "unknown" else "User"
dynamic_variables["date"] = datetime.now().strftime("%Y%m%d_%H%M%S")

Replace with:

python
# Start with agent-level defaults (backward compat)
dynamic_variables = {item.key: item.value for item in agent_meta.DynamicVar or []}

# Overlay pre-merged values from dispatch (already has correct priority)
if meta.dynamic_values:
    for k, v in meta.dynamic_values.items():
        if v is not None and str(v).strip():
            dynamic_variables[str(k)] = str(v)

# Built-in variables (always set, cannot be overridden)
dynamic_variables["name"] = user_name if user_name and user_name.lower() != "unknown" else "User"
dynamic_variables["date"] = datetime.now().strftime("%Y%m%d_%H%M%S")

3. No Changes Needed in agent_settings.py ​

The sync_agent_settings function already handles agent_config correctly. The DynamicVar field on the agent document is still passed through as before. The new dynamic_values is on the meta object, not on agent_meta.


How the Merge Works (done by Node.js backend) ​

The Node.js calling.service.ts builds dynamic_values before dispatch:

Priority 1 (lowest):  agent_config.DynamicVar  → [{key: "company", value: "Acme"}]
Priority 2:           campaign.dynamicVariables → {offer_code: "SUMMER2025"}
Priority 3 (highest): contact.dynamicVariables  → {product_name: "Widget Pro", price: "₹999"}

Result sent as dynamic_values:

json
{
  "company": "Acme",
  "offer_code": "SUMMER2025",
  "product_name": "Widget Pro",
  "price": "₹999"
}

The Agent server doesn't need to know about the priority it's already resolved.


Example Scenario ​

Agent prompt:

Hi {{name}}, I'm calling from {{company}} about {{product_name}} priced at {{price}}.
We have a special offer: {{offer_code}}.

Dispatch metadata for John:

json
{
  "name": "John",
  "dynamic_values": {
    "company": "Acme Corp",
    "product_name": "Widget Pro",
    "price": "₹999",
    "offer_code": "SUMMER2025"
  }
}

Final resolved prompt:

Hi John, I'm calling from Acme Corp about Widget Pro priced at ₹999.
We have a special offer: SUMMER2025.

Dispatch metadata for Jane (different CSV row):

json
{
  "name": "Jane",
  "dynamic_values": {
    "company": "Acme Corp",
    "product_name": "Widget Lite",
    "price": "₹499",
    "offer_code": "SUMMER2025"
  }
}

Test Call Support ​

Test calls also send dynamic_values when the user fills in variable values in the test call modal. The merge is: agent DynamicVar defaults → test variable values from UI. Same field, same format.


Testing Checklist ​

  1. Backward compatibility: Campaigns without extra CSV columns → dynamic_values absent, agent uses DynamicVar only
  2. Contact-level override: Two contacts in same campaign get different dynamic_values
  3. Missing variables: {{unknown_var}} remains as-is in prompt (no crash)
  4. Empty values: Empty strings don't override existing defaults
  5. Test calls: Test calls with variable values → dynamic_values present
  6. Test calls without values: dynamic_values absent, falls back to DynamicVar

Migration Notes ​

  • Non-breaking change. Existing campaigns and agents continue to work.
  • dynamic_values is optional in MetadataSchema defaults to None.
  • No database migration needed on the Agent server side.
  • The DynamicVar on agent_meta still works as the base layer.

Last updated: