-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathhello_seller_creative.py
More file actions
114 lines (91 loc) · 4.06 KB
/
Copy pathhello_seller_creative.py
File metadata and controls
114 lines (91 loc) · 4.06 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
"""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
class HelloCreativeSeller(DecisioningPlatform):
"""The canonical minimal ``creative-generative`` adopter.
Implements only ``build_creative_legacy`` — the one required method on
:class:`CreativeBuilderPlatform`. Optional ``preview_creative_legacy``
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_legacy(
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.creative_manifest.format_kind`` for canonical 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_kind="audio_hosted",
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.
Synchronous terminal responses remain inline-only. A handoff without push
is pollable through ``get_task_status``. Push-configured handoffs require
an external durable outbox; do not wire the SDK sender/supervisor as a
substitute for that beta.5 publication contract.
"""
serve(HelloCreativeSeller())
if __name__ == "__main__":
main()