Write an executor
createExecutor takes a pair of handlers (text / object). Each receives formatted — the neutral rendered prompt — and returns { text | object, usage }. That’s the whole contract; Client setup handles wiring it into a runner and serving it.
Reference setups
Complete, copy-paste executors for the SDKs teams reach for most. Each wraps the SDK directly — there’s no AgentMark adapter package to install or version. Copy the closest one, adjust the model mapping, and you’re done. Every one takes the neutral render and returns{ text | object, usage }.
Vercel AI SDK
Wraps theai package’s generateText / streamText (and generateObject for structured output), with both one-shot and streaming text paths:
OpenAI (raw SDK)
The official OpenAI SDK’schat.completions.create. Messages map straight through; in Python, formatted is a Pydantic model so model_dump the messages:
Anthropic (raw SDK)
The@anthropic-ai/sdk messages.create. Anthropic takes system as a top-level field and requires max_tokens, so split the system message out of the neutral render:
Amazon Bedrock (Python)
Bedrock’sinvoke_model takes a different request shape: anthropic_version lives in the body, max_tokens is required, and the model ID is a full cross-region inference profile ID — not the short alias in the prompt’s model_name. Map it explicitly.
The runner automatically stamps gen_ai.operation.name = "chat" and the config alias on the span, so the Requests view and cost attribution work with no extra code. To surface the full inference profile ID in the dashboard instead of the alias, override gen_ai.request.model on the span after your call (set_attribute is last-write-wins):
Agent frameworks (Pydantic AI, Mastra, Claude Agent SDK)
Agent frameworks follow the identical shape; the only difference is that your handler runs an agent loop instead of a single completion. Feed the render’s messages into your agent, run it, and return its final output plus token usage:text-delta / tool-call / tool-result events as the agent emits them. See Streaming SDKs.
Streaming SDKs
If your SDK streams (e.g. BedrockConverseStream), use the streaming handlers instead of buffering. They yield the same content events (text-delta, tool-call, …) and report usage plus the finish reason on a finish event you yield; the builder emits the single terminal finish for you:
Streaming object handlers yield
object-delta / object-final events (ObjectDeltaEvent / ObjectFinalEvent in Python) and a finish carrying usage. If your SDK only streams cumulative partials (no explicit final), the builder uses the last delta as the resolved value, so AgentMark Cloud always receives a complete object.Validate your executor
Run the conformance suite. One call confirms your executor emits a protocol-correct stream for every kind, streaming and one-shot, including the error path —errorInput is a malformed render your handler rejects before any network call:
ctx, the suite runs your executor twice, once streaming and once one-shot, so if you supply both a one-shot and a streaming handler, both branches are validated (a broken one-shot path won’t hide behind a working stream).
Provider-specific parameter mapping (tool wiring, custom settings, full request control) also lives in your executor: its handlers receive the neutral render and build the exact request your SDK expects. See the resolve-by-name tools pattern for wiring frontmatter tool names to implementations.
Model names vs provider model IDs
formatted.text_config.model_name is the prompt’s model_name verbatim: a registry ID in provider/model form. Your executor owns the translation to whatever ID your SDK expects. Two common shapes:
Strip the provider prefix when the registry ID is your SDK’s model ID — the usual case (openai/gpt-4o → gpt-4o):
anthropic/claude-sonnet-4-6, production on Bedrock). Keep the dict in the executor so it’s versioned with the code, and fail loudly on unmapped names instead of passing them through (an unmapped name otherwise surfaces as a confusing provider-side 404):
builtInModels (a non-empty list is an allowlist). npx @agentmark-ai/cli pull-models --provider bedrock lists the registry’s Bedrock IDs.
Migrating from the adapter packages
The SDK-specific adapter packages (@agentmark-ai/ai-sdk-v4-adapter, @agentmark-ai/ai-sdk-v5-adapter, @agentmark-ai/mastra-v0-adapter, agentmark-pydantic-ai-v0, and the Claude Agent SDK adapters) are removed. Everything they did is covered without them:
- If you used an adapter’s model registry +
format()to call your SDK, switch to the neutral render —format()gives you{ messages, ...config }and you make the SDK call yourself, in your own app. See Running prompts. - If you used an adapter’s webhook handler (
VercelAdapterWebhookHandleret al.) foragentmark devor managed runs, replace it with an executor (this page) wired into a runner and served — see Client setup.
import { createAgentMark } from "@agentmark-ai/prompt-core". The Python adapter packages have no shim; replace them with agentmark-prompt-core directly (from agentmark.prompt_core import create_agentmark, create_executor, create_webhook_runner).
Have questions?
Reach out any time:
- Email us at hello@agentmark.co for support
- Schedule an Enterprise Demo to learn about our business solutions