-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathhello_seller_creative.py
More file actions
122 lines (98 loc) · 4.36 KB
/
hello_seller_creative.py
File metadata and controls
122 lines (98 loc) · 4.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""Hello-seller-creative — minimal CreativeBuilderPlatform adopter.
The smallest possible ``creative-generative`` (or ``creative-template``)
seller. Buyers send a brief; the agent returns a CreativeManifest with
the synthesized asset URL.
This is the template for AI-generated-creative integrators (AudioStack,
Stability AI, Runway, ElevenLabs, etc.). Three return-shape arms are
supported by the framework's projection layer:
1. **Bare manifest** — ``return CreativeManifest(...)``. Framework
wraps it into the wire envelope ``{creative_manifest: {...}}``.
2. **List of manifests** — ``return [m1, m2, ...]``. Framework wraps
into ``{creative_manifests: [...]}`` (multi-format build).
3. **Full envelope** — ``return BuildCreativeSuccessResponse(...)``
if you want explicit control over the wire shape.
Run::
uv run python examples/hello_seller_creative.py
Then connect any AdCP MCP buyer and call ``build_creative`` with a
brief.
"""
from __future__ import annotations
import uuid
from typing import Any
from adcp.decisioning import (
DecisioningCapabilities,
DecisioningPlatform,
RequestContext,
SingletonAccounts,
serve,
)
from adcp.types import AudioContent, CreativeManifest, FormatReferenceStructuredObject
class HelloCreativeSeller(DecisioningPlatform):
"""The canonical minimal ``creative-generative`` adopter.
Implements only ``build_creative`` — the one required method on
:class:`CreativeBuilderPlatform`. Optional ``preview_creative``
is omitted; the framework's ``_require_platform_method`` gate
returns ``UNSUPPORTED_FEATURE`` to buyers who call it on this
seller.
Replace the stub asset_url with your generation pipeline (a real
AudioStack/Stability/Runway integration would call the upstream
API here and return the resulting CDN URL).
"""
capabilities = DecisioningCapabilities(
specialisms=["creative-generative"],
channels=["audio"],
)
accounts = SingletonAccounts(account_id="hello-creative")
def build_creative(
self,
req: Any,
ctx: RequestContext[Any],
) -> CreativeManifest:
"""Synthesize a single audio creative from the buyer's brief.
Returns a bare :class:`CreativeManifest` — the framework's
projection layer wraps it into the wire envelope. The brief
message is on ``req.message``; the requested format is on
``req.target_format_id`` (or ``req.target_format_ids`` for
multi-format builds).
"""
# Real adopters call their generation API here; this stub
# synthesizes a placeholder URL for the example.
creative_id = f"cr-{uuid.uuid4().hex[:12]}"
return CreativeManifest(
creative_id=creative_id,
format_id=FormatReferenceStructuredObject(
agent_url="https://creative.adcontextprotocol.org/",
id="audio_30s",
),
assets={
# Note: ``AudioContent`` (not ``AudioAsset``) — 4.0
# renamed payload-describing types to ``*Content`` so
# they don't collide with ``*FormatAsset`` slot types.
# The framework's MIGRATION_v3_to_v4.md has the full
# rationale.
"primary_audio": AudioContent(
asset_id=f"{creative_id}-audio",
asset_role="primary_audio",
url=f"https://cdn.example.com/synth/{creative_id}.mp3",
duration_ms=30000,
),
},
)
def main() -> None:
"""Boot the seller on http://localhost:3001/mcp.
Buyer-facing surface after boot:
* ``tools/list`` advertises ``build_creative`` and the framework's
always-on protocol/discovery tools — NOT the sales / signals /
governance tools (per-specialism filter).
* ``tools/call build_creative`` returns the synthesized manifest.
The ``auto_emit_completion_webhooks=False`` opt-out keeps this
example minimal. In production, wire ``webhook_sender=`` so
buyers who register ``push_notification_config.url`` get
completion notifications:
from adcp.webhook_sender import WebhookSender
sender = WebhookSender.from_jwk(...)
serve(HelloCreativeSeller(), webhook_sender=sender)
"""
serve(HelloCreativeSeller(), auto_emit_completion_webhooks=False)
if __name__ == "__main__":
main()