-
Notifications
You must be signed in to change notification settings - Fork 17
Description
Description
When receiving a payload from a Vapi Voice Agent, the ServerMessageEndOfCallReport model in the server-sdk-python fails to parse the incoming JSON. The payload uses camelCase field names (e.g., endedReason, endTime, secondsFromStart), but the SDK model expects snake_case (ended_reason, end_time, seconds_from_start).
Although the generated model defines aliases using FieldMetadata(alias="..."), those aliases are not actually used during model validation. As a result, the SDK throws large validation errors stating that required fields are missing.
Example
from vapi_server_sdk.models import ServerMessageEndOfCallReport
msg = {
"timestamp": 1762133576209,
"type": "end-of-call-report",
"endedReason": "customer-ended-call",
"analysis": {"summary": "...", "successEvaluation": "true"},
"artifact": {
"messages": [
{
"role": "bot",
"message": "Hi, this is a test.",
"time": 1762133501000.0,
"endTime": 1762133502000.0,
"secondsFromStart": 1.0,
}
],
"transcript": "AI: Hi, this is a test."
}
}
ServerMessageEndOfCallReport.model_validate(msg)Actual behavior
80 validation errors for ServerMessageEndOfCallReport
ended_reason
Field required [type=missing, input_value={...}, input_type=dict]
artifact.messages.0.BotMessage.end_time
Field required [type=missing, input_value={...}, input_type=dict]
artifact.messages.0.BotMessage.seconds_from_start
Field required [type=missing, input_value={...}, input_type=dict]
...Expected behavior
The SDK should correctly parse the Voice Agent’s camelCase JSON payload, populating the corresponding snake_case model fields using Pydantic’s alias handling.
Root cause
The generated models use Fern’s custom FieldMetadata(alias="...") annotations instead of Pydantic’s native Field(alias="...").
Pydantic v2 does not honor those aliases when parsing input (model_validate, __init__), only when serializing output (model_dump(by_alias=True)).
This means that incoming camelCase payloads from the Voice Agent (which follow the documented API schema) cannot be deserialized without manual key remapping.
Workarounds
Convert the incoming JSON keys from camelCase → snake_case before validation:
import re
from typing import Any, Mapping
CAMEL_RE = re.compile(r'(?<!^)(?=[A-Z])')
def camel_to_snake(name: str) -> str:
return CAMEL_RE.sub('_', name).lower()
def normalize_keys(obj: Any) -> Any:
if isinstance(obj, Mapping):
return {camel_to_snake(k): normalize_keys(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [normalize_keys(x) for x in obj]
else:
return obj
normalized = normalize_keys(msg)
ServerMessageEndOfCallReport.model_validate(normalized)Or manually subclass the model to redeclare fields using pydantic.Field(alias="...").
Proposed fix
- Update code generation for
server-sdk-pythonto usepydantic.Field(alias=...)rather thanFieldMetadata(alias=...), ensuring proper alias resolution during validation. - Alternatively, update the SDK’s
UncheckedBaseModelto include:
model_config = ConfigDict(populate_by_name=True)and propagate real Pydantic alias definitions into the generated models.
Environment
- SDK:
vapi/server-sdk-python - SDK Version: 1.7.3
- Pydantic: 2.12.3
- Python: 3.11
- Platform: macOS
Impact
Payloads received from Voice Agents fail validation because camelCase fields aren’t recognized. This breaks out-of-the-box use of the server SDK for handling voice agent events.