From 3c28cdd9b6adefe222bbec110f071f720b25ddf2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 4 May 2026 13:51:05 +0000 Subject: [PATCH 1/2] fix(types): add CreateMediaBuySubmittedResponse alias and fix handler return type - Adds `CreateMediaBuySubmittedResponse = CreateMediaBuyResponse3` alias so implementors have a semantic name instead of the internal `Response3` suffix. - Exports the alias from `adcp.types` (`__init__.py` + `__all__`). - Fixes `PlatformHandler.create_media_buy` return annotation from `CreateMediaBuySuccessResponse` to `CreateMediaBuyResponse` (the full union) and updates the matching `cast()`. - Adds `status` (const "submitted") and `task_id` (required string) to `create-media-buy-async-response-submitted.json` so the SDK's strict validator produces a clear missing-field error instead of propagating to FastMCP's misleading enum error. Fixes #570 https://claude.ai/code/session_01MXKE7txstMT3B7aTctfXcZ --- .../create-media-buy-async-response-submitted.json | 14 ++++++++++++-- src/adcp/decisioning/handler.py | 6 +++--- src/adcp/types/__init__.py | 2 ++ src/adcp/types/aliases.py | 5 +++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/schemas/cache/media-buy/create-media-buy-async-response-submitted.json b/schemas/cache/media-buy/create-media-buy-async-response-submitted.json index 4531f8776..ccbbf3347 100644 --- a/schemas/cache/media-buy/create-media-buy-async-response-submitted.json +++ b/schemas/cache/media-buy/create-media-buy-async-response-submitted.json @@ -1,9 +1,19 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Create Media Buy - Submitted", - "description": "Payload acknowledging the task is queued. Usually empty or just context.", + "description": "Payload acknowledging the task is queued for async processing (e.g. human-review tenants). Consumers must use task_id to poll tasks/get for the final outcome.", "type": "object", + "required": ["status", "task_id"], "properties": { + "status": { + "type": "string", + "const": "submitted", + "description": "Discriminates this async envelope from the synchronous success shape." + }, + "task_id": { + "type": "string", + "description": "Task handle the buyer uses with tasks/get. The media_buy_id is issued on the completion artifact, not here." + }, "context": { "$ref": "../core/context.json" }, @@ -12,4 +22,4 @@ } }, "additionalProperties": true -} \ No newline at end of file +} diff --git a/src/adcp/decisioning/handler.py b/src/adcp/decisioning/handler.py index 045a9fcb6..1847bebce 100644 --- a/src/adcp/decisioning/handler.py +++ b/src/adcp/decisioning/handler.py @@ -98,7 +98,7 @@ CreateContentStandardsRequest, CreateContentStandardsResponse, CreateMediaBuyRequest, - CreateMediaBuySuccessResponse, + CreateMediaBuyResponse, CreatePropertyListRequest, CreatePropertyListResponse, DeleteCollectionListRequest, @@ -1305,7 +1305,7 @@ async def create_media_buy( # type: ignore[override] self, params: CreateMediaBuyRequest, context: ToolContext | None = None, - ) -> CreateMediaBuySuccessResponse: + ) -> CreateMediaBuyResponse: from adcp.decisioning.types import AdcpError tool_ctx = context or ToolContext() @@ -1404,7 +1404,7 @@ async def _release_reservation_hook(_exc: BaseException) -> None: on_failure=on_failure, ) self._maybe_auto_emit_sync_completion("create_media_buy", params, result) - return cast("CreateMediaBuySuccessResponse", result) + return cast("CreateMediaBuyResponse", result) async def update_media_buy( # type: ignore[override] self, diff --git a/src/adcp/types/__init__.py b/src/adcp/types/__init__.py index d10142120..ff92830f0 100644 --- a/src/adcp/types/__init__.py +++ b/src/adcp/types/__init__.py @@ -459,6 +459,7 @@ CreateContentStandardsErrorResponse, CreateContentStandardsSuccessResponse, CreateMediaBuyErrorResponse, + CreateMediaBuySubmittedResponse, CreateMediaBuySuccessResponse, CssFormatAsset, CssFormatGroupAsset, @@ -1119,6 +1120,7 @@ def __init__(self, *args: object, **kwargs: object) -> None: "CreateContentStandardsSuccessResponse", "CreateMediaBuyErrorResponse", "CreateMediaBuySuccessResponse", + "CreateMediaBuySubmittedResponse", "Deployment", "Destination", "GetContentStandardsErrorResponse", diff --git a/src/adcp/types/aliases.py b/src/adcp/types/aliases.py index 5a5949a19..ff325878a 100644 --- a/src/adcp/types/aliases.py +++ b/src/adcp/types/aliases.py @@ -78,6 +78,7 @@ # Create media buy responses CreateMediaBuyResponse1, CreateMediaBuyResponse2, + CreateMediaBuyResponse3, # DAAST assets DaastAsset1, DaastAsset2, @@ -291,6 +292,9 @@ CreateMediaBuyErrorResponse = CreateMediaBuyResponse2 """Error response - media buy creation failed, no media buy created.""" +CreateMediaBuySubmittedResponse = CreateMediaBuyResponse3 +"""Async-submitted response - media buy queued for human review; poll task_id for completion.""" + # Performance Feedback Response Variants ProvidePerformanceFeedbackSuccessResponse = ProvidePerformanceFeedbackResponse1 """Success response - performance feedback accepted.""" @@ -1489,6 +1493,7 @@ def get_pricing(options: list[PricingOption]) -> None: # Create media buy responses "CreateMediaBuySuccessResponse", "CreateMediaBuyErrorResponse", + "CreateMediaBuySubmittedResponse", # Creative delivery requests "GetCreativeDeliveryByMediaBuyRequest", "GetCreativeDeliveryByBuyerRefRequest", From 7ba945cd211c803b5f5d07294d645a2eb898b7fe Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 4 May 2026 13:57:23 +0000 Subject: [PATCH 2/2] fix(types): address pre-PR review findings for issue #570 - Add is_create_media_buy_submitted() TypeGuard; fix is_create_media_buy_success() to return False for submitted envelopes (status=='submitted' check). - Expand guards.py CreateMediaBuyResponse local union to include the submitted branch. - Export is_create_media_buy_submitted from adcp.types and CreateMediaBuySubmittedResponse from the top-level adcp package alongside the success/error siblings. - Update CreateMediaBuySubmitted generated class to include required status and task_id fields, bringing the standalone schema's Python representation in sync with the updated create-media-buy-async-response-submitted.json. - Regenerate public API snapshot. https://claude.ai/code/session_01MXKE7txstMT3B7aTctfXcZ --- src/adcp/__init__.py | 2 ++ src/adcp/types/__init__.py | 1 + ...create_media_buy_async_response_submitted.py | 14 +++++++++++++- src/adcp/types/guards.py | 17 ++++++++++++++--- tests/fixtures/public_api_snapshot.json | 2 ++ 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/adcp/__init__.py b/src/adcp/__init__.py index 2445170d2..3470f9729 100644 --- a/src/adcp/__init__.py +++ b/src/adcp/__init__.py @@ -294,6 +294,7 @@ CreateContentStandardsErrorResponse, CreateContentStandardsSuccessResponse, CreateMediaBuyErrorResponse, + CreateMediaBuySubmittedResponse, CreateMediaBuySuccessResponse, Deployment, Destination, @@ -888,6 +889,7 @@ def get_adcp_version() -> str: "CalibrateContentErrorResponse", "CreateContentStandardsSuccessResponse", "CreateContentStandardsErrorResponse", + "CreateMediaBuySubmittedResponse", "CreateMediaBuySuccessResponse", "CreateMediaBuyErrorResponse", "Deployment", diff --git a/src/adcp/types/__init__.py b/src/adcp/types/__init__.py index ff92830f0..14860bdcf 100644 --- a/src/adcp/types/__init__.py +++ b/src/adcp/types/__init__.py @@ -601,6 +601,7 @@ is_build_creative_success, is_calibrate_content_success, is_create_media_buy_error, + is_create_media_buy_submitted, is_create_media_buy_success, is_get_account_financials_error, is_get_account_financials_success, diff --git a/src/adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py b/src/adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py index 298e9773b..cf1d781c0 100644 --- a/src/adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py +++ b/src/adcp/types/generated_poc/media_buy/create_media_buy_async_response_submitted.py @@ -4,8 +4,10 @@ from __future__ import annotations +from typing import Annotated, Literal + from adcp.types.base import AdCPBaseModel -from pydantic import ConfigDict +from pydantic import ConfigDict, Field from ..core import context as context_1 from ..core import ext as ext_1 @@ -15,5 +17,15 @@ class CreateMediaBuySubmitted(AdCPBaseModel): model_config = ConfigDict( extra='allow', ) + status: Annotated[ + Literal['submitted'], + Field(description='Discriminates this async envelope from the synchronous success shape.'), + ] = 'submitted' + task_id: Annotated[ + str, + Field( + description='Task handle the buyer uses with tasks/get. The media_buy_id is issued on the completion artifact, not here.' + ), + ] context: context_1.ContextObject | None = None ext: ext_1.ExtensionObject | None = None diff --git a/src/adcp/types/guards.py b/src/adcp/types/guards.py index 8a908ea10..e9510512e 100644 --- a/src/adcp/types/guards.py +++ b/src/adcp/types/guards.py @@ -79,6 +79,7 @@ def is_adcp_success(response: Any) -> bool: CalibrateContentErrorResponse, CalibrateContentSuccessResponse, CreateMediaBuyErrorResponse, + CreateMediaBuySubmittedResponse, CreateMediaBuySuccessResponse, GetAccountFinancialsErrorResponse, GetAccountFinancialsSuccessResponse, @@ -103,7 +104,9 @@ def is_adcp_success(response: Any) -> bool: ) # Type aliases for response unions -CreateMediaBuyResponse = CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse +CreateMediaBuyResponse = ( + CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse | CreateMediaBuySubmittedResponse +) UpdateMediaBuyResponse = UpdateMediaBuySuccessResponse | UpdateMediaBuyErrorResponse ActivateSignalResponse = ActivateSignalSuccessResponse | ActivateSignalErrorResponse BuildCreativeResponse = BuildCreativeSuccessResponse | BuildCreativeErrorResponse @@ -116,11 +119,18 @@ def is_adcp_success(response: Any) -> bool: # --- Create Media Buy --- +def is_create_media_buy_submitted( + response: CreateMediaBuyResponse, +) -> TypeGuard[CreateMediaBuySubmittedResponse]: + """Check if a CreateMediaBuyResponse is an async-submitted envelope.""" + return getattr(response, "status", None) == "submitted" + + def is_create_media_buy_success( response: CreateMediaBuyResponse, ) -> TypeGuard[CreateMediaBuySuccessResponse]: - """Check if a CreateMediaBuyResponse is a success.""" - return not is_adcp_error(response) + """Check if a CreateMediaBuyResponse is a synchronous success.""" + return not is_adcp_error(response) and not is_create_media_buy_submitted(response) def is_create_media_buy_error( @@ -308,6 +318,7 @@ def is_get_creative_features_success( "is_adcp_error", "is_adcp_success", # Media buy guards + "is_create_media_buy_submitted", "is_create_media_buy_success", "is_create_media_buy_error", "is_update_media_buy_success", diff --git a/tests/fixtures/public_api_snapshot.json b/tests/fixtures/public_api_snapshot.json index bfeede817..dbacfcd50 100644 --- a/tests/fixtures/public_api_snapshot.json +++ b/tests/fixtures/public_api_snapshot.json @@ -94,6 +94,7 @@ "CreateMediaBuyErrorResponse", "CreateMediaBuyRequest", "CreateMediaBuyResponse", + "CreateMediaBuySubmittedResponse", "CreateMediaBuySuccessResponse", "Creative", "CreativeApproval", @@ -492,6 +493,7 @@ "CreateMediaBuyErrorResponse", "CreateMediaBuyRequest", "CreateMediaBuyResponse", + "CreateMediaBuySubmittedResponse", "CreateMediaBuySuccessResponse", "CreatePropertyListRequest", "CreatePropertyListResponse",