4040)
4141from google .protobuf .json_format import MessageToDict , ParseDict
4242from google .protobuf .struct_pb2 import Value
43+ from pydantic import BaseModel as PydanticBaseModel
4344
4445from adcp .server .idempotency .backends import MemoryBackend as MemoryBackend
4546from adcp .server .idempotency .webhook_dedup import WebhookDedupStore as WebhookDedupStore
5758)
5859from adcp .types import GeneratedTaskStatus
5960from adcp .types .base import AdCPBaseModel
60- from adcp .types .generated_poc .core .async_response_data import AdcpAsyncResponseData
6161from adcp .webhook_receiver import (
6262 LegacyHmacFallback ,
6363 VerifiedSignerLike ,
@@ -86,7 +86,7 @@ def generate_webhook_idempotency_key() -> str:
8686def create_mcp_webhook_payload (
8787 task_id : str ,
8888 status : GeneratedTaskStatus | str ,
89- result : AdcpAsyncResponseData | dict [str , Any ] | None = None ,
89+ result : PydanticBaseModel | dict [str , Any ] | None = None ,
9090 timestamp : datetime | None = None ,
9191 task_type : str | None = None ,
9292 operation_id : str | None = None ,
@@ -358,7 +358,7 @@ def sign_legacy_webhook(
358358 return signature_headers , body_bytes
359359
360360
361- def extract_webhook_result_data (webhook_payload : dict [str , Any ]) -> AdcpAsyncResponseData | None :
361+ def extract_webhook_result_data (webhook_payload : dict [str , Any ]) -> dict [ str , Any ] | None :
362362 """
363363 Extract result data from webhook payload (MCP or A2A format).
364364
@@ -376,8 +376,8 @@ def extract_webhook_result_data(webhook_payload: dict[str, Any]) -> AdcpAsyncRes
376376 webhook_payload: Raw webhook dictionary from HTTP request (JSON-deserialized)
377377
378378 Returns:
379- AdcpAsyncResponseData union type containing the extracted AdCP response, or None
380- if no result present. For A2A webhooks, unwraps data from artifacts/message parts
379+ dict[str, Any] containing the extracted AdCP response data , or None if no
380+ result is present. For A2A webhooks, unwraps data from artifacts/message parts
381381 structure. For MCP webhooks, returns the result field directly.
382382
383383 Examples:
@@ -464,8 +464,8 @@ def extract_webhook_result_data(webhook_payload: dict[str, Any]) -> AdcpAsyncRes
464464 data = part ["data" ]
465465 # Unwrap {"response": {...}} wrapper if present (A2A convention)
466466 if isinstance (data , dict ) and "response" in data and len (data ) == 1 :
467- return cast (AdcpAsyncResponseData , data ["response" ])
468- return cast (AdcpAsyncResponseData , data )
467+ return cast (dict [ str , Any ] , data ["response" ])
468+ return cast (dict [ str , Any ] , data )
469469
470470 return None
471471
@@ -485,20 +485,20 @@ def extract_webhook_result_data(webhook_payload: dict[str, Any]) -> AdcpAsyncRes
485485 data = part ["data" ]
486486 # Unwrap {"response": {...}} wrapper if present
487487 if isinstance (data , dict ) and "response" in data and len (data ) == 1 :
488- return cast (AdcpAsyncResponseData , data ["response" ])
489- return cast (AdcpAsyncResponseData , data )
488+ return cast (dict [ str , Any ] , data ["response" ])
489+ return cast (dict [ str , Any ] , data )
490490
491491 return None
492492
493493 # MCP format: result field directly
494- return cast (AdcpAsyncResponseData | None , webhook_payload .get ("result" ))
494+ return cast (dict [ str , Any ] | None , webhook_payload .get ("result" ))
495495
496496
497497def create_a2a_webhook_payload (
498498 task_id : str ,
499499 status : GeneratedTaskStatus ,
500500 context_id : str ,
501- result : AdcpAsyncResponseData | dict [str , Any ],
501+ result : PydanticBaseModel | dict [str , Any ],
502502 timestamp : datetime | None = None ,
503503) -> Task | TaskStatusUpdateEvent :
504504 """
@@ -517,7 +517,7 @@ def create_a2a_webhook_payload(
517517 status: Current task status
518518 context_id: Session/conversation identifier (required by A2A protocol)
519519 timestamp: When the webhook was generated (defaults to current UTC time)
520- result: Task-specific payload (AdCP response data)
520+ result: Task-specific payload — any Pydantic model or plain dict
521521
522522 Returns:
523523 Task object for terminated statuses, TaskStatusUpdateEvent for intermediate statuses
@@ -529,17 +529,18 @@ def create_a2a_webhook_payload(
529529 >>>
530530 >>> task = create_a2a_webhook_payload(
531531 ... task_id="task_123",
532+ ... context_id="ctx_123",
532533 ... status=GeneratedTaskStatus.completed,
533534 ... result={"products": [...]},
534- ... message="Found 5 products"
535535 ... )
536536 >>> # task is a Task object with artifacts containing the result
537537
538538 Create a working status update:
539539 >>> event = create_a2a_webhook_payload(
540540 ... task_id="task_456",
541+ ... context_id="ctx_456",
541542 ... status=GeneratedTaskStatus.working,
542- ... message="Processing 3 of 10 items"
543+ ... result={"current_step": "processing", "percentage": 30},
543544 ... )
544545 >>> # event is a TaskStatusUpdateEvent with status.message
545546
@@ -590,7 +591,7 @@ def create_a2a_webhook_payload(
590591 # Build parts for the message/artifact.
591592 parts : list [pb .Part ] = []
592593
593- # Convert AdcpAsyncResponseData to dict if it's a Pydantic model
594+ # Convert Pydantic model to dict if needed
594595 if hasattr (result , "model_dump" ):
595596 result_dict : dict [str , Any ] = result .model_dump (mode = "json" )
596597 else :
0 commit comments